纯js随机生成夜间城市背景图像的工具

近年来,夜间经济飞速发展,城市夜景的美丽吸引了无数人的目光。如果您也热爱城市夜景,并且对于设计和开发有着浓厚的兴趣,那么我为您推荐一款绝佳工具——随机生成夜间城市背景图像的纯js工具!

这个工具简单易用,只需要点击一下,就能够生成非常漂亮的夜间城市背景图像。同时,您可以根据自己的喜好和需要,调整颜色、图像复杂度、高度等参数,生成与众不同的夜间城市背景图像。

这个工具的使用范围非常广泛,首先,它可以用于网站和应用的界面设计,作为背景图像非常合适,可以大大提升界面的美感和用户体验。其次,它也可以用于个人创作和分享,比如制作精美的明信片、书签、海报等。

除了美观的外观,这个工具的另一个优点是使用纯js进行开发,无需安装任何第三方库或插件,轻松使用的同时也保证了网页的兼容性和响应速度。

如果您不知道如何使用这个工具,也不要担心,我们提供了详细的文档和演示,让您轻松上手并实现您的创意。同时,我们的团队也会为您及时提供技术支持和反馈,确保您的使用体验。

总之,这个随机生成夜间城市背景图像的纯js工具是开发者、设计师和创作者的最佳选择,快来尝试吧!

在线demo地址

效果如图

纯js随机生成夜间城市背景图像的工具_第1张图片

纯js随机生成夜间城市背景图像的工具_第2张图片

 代码分享


//import {createLight} from './createLight.js';

//x is at left of grid
//y is at bottom of grid

window.addEventListener('load', function () {

    var docHeight = document.body.clientHeight;
    var docWidth = document.body.clientWidth;
    let margin = 5;

    let bottomAreaHeight = 70;

    const numberOfAttemptedLights = 1000;
    const minLightSize = 1;
    const maxLightSize = 3;
    const percentOfLightsThatAreStatic = 15;//0-100

    //Builds batch 1
    const numberOfBuildings = 30;
    const minBldgWidthPercent = 1;
    const maxBldgWidthPercent = 5;
    const minBldgHeightPercent = 40;
    const maxBldgHeightPercent = 85;

    //Builds batch 2
    const numberOfBuildings2 = 15;
    const minBldgWidthPercent2 = 8;
    const maxBldgWidthPercent2 = 15;
    const minBldgHeightPercent2 = 10;
    const maxBldgHeightPercent2 = 40;

    const numberOfArtifactsToAttempt = 15;

    //Set bottom element
    let bottom = document.getElementById('bottom');
    bottom.style.width = docWidth + "px";
    bottom.style.height = bottomAreaHeight + "px";

    // Generate the buildings and the grid that represents where the buildings are in a 2D array
    const maxX = docWidth;
    const maxY = docHeight - bottomAreaHeight;
    const grid = createArray(maxX, maxY);
    for(let x = 0; x < grid.length; x++){
        for(let y = 0; y < grid[x].length; y++){
            grid[x][y] = false;
        }
    }
    generateBuildings(grid, margin, maxY, maxX, numberOfBuildings, maxLightSize,
        minBldgWidthPercent, maxBldgWidthPercent, minBldgHeightPercent, maxBldgHeightPercent);
    generateBuildings(grid, margin, maxY, maxX, numberOfBuildings2, maxLightSize,
        minBldgWidthPercent2, maxBldgWidthPercent2, minBldgHeightPercent2, maxBldgHeightPercent2);

    // Identify areas to add artifacts to the buildings' sides and roofs.
    const minHorSurfaceLength = 20;
    const minVerSurfaceLength = 80;
    const surfaces = identifyArtifactPlacements(grid, minHorSurfaceLength, minVerSurfaceLength);
    placeArtifacts(surfaces, numberOfArtifactsToAttempt, grid, minHorSurfaceLength, minVerSurfaceLength);
    
    // Create lights that only go into that 2D array
    const colorScheme = generateColorScheme();
    for(let x = 0; x < numberOfAttemptedLights; x ++){
        let remainder = x % 100;
        const lightIsStatic = remainder < percentOfLightsThatAreStatic;
        createLight(margin, docHeight - bottomAreaHeight, docWidth, grid, 
            minLightSize, maxLightSize, lightIsStatic, 
            colorScheme, 4);
    }
    // Apply bg color
    let everything = document.getElementById('everything');
    everything.style.height = docHeight;
    everything.classList.add(colorScheme.bgColor);
    // Apply sun color and size
    let sun = document.getElementById('sun');
    if(sun !== undefined){
        let sunSizePercent = generateRand(45, 80);
        let sunSize = docWidth * sunSizePercent / 100;
        let sunMarginPercent = generateRand(10,40);
        let sunMargin = docHeight * sunMarginPercent / 100;
        sun.style.width = sunSize;
        sun.style.height = sunSize;
        sun.style.borderRadius = sunSize + "px";
        sun.style.transform = `translateY(${sunMargin}px)`;
        sun.style.backgroundColor = colorScheme.sunColor;
    }

    // If in debug mode, print the color scheme number
    //console.log("colorScheme: " + colorScheme.colorScheme);
})

function identifyArtifactPlacements(grid, minHorSurfaceLength, minVerSurfaceLength)
{
    // Start on the left side. Find the rooftop there by starting at the bottom-left-most pixel
    // and going up, 1 pixel at a time, till finding the roof of the left-most building.
    // NOTE: this assumes that there is a left-most building.
    let coord = {
        x: 0,
        y: 0
    };
    for(let y = 0; y < grid[0].length; y++){
        let pixel = grid[0][y];
        if(pixel === false){
            coord = {
                x: 0,
                y: y
            };
            break;
        }
    }
    
    // Go until reaching a corner. Then record the surface, turn the corner, and repeat.
    let direction = "right";//can be "up", "down", or "right".
    let surfaces = {
        horizontalSurfaces: [],
        verticalSurfaces: []
    };
    traverseCityScape(grid, surfaces, direction, coord);

    // Discard any surfaces less than a certain length.
    let newHorizontalSurfaces = [];
    surfaces.horizontalSurfaces.forEach(s => {
        if(s.length >= minHorSurfaceLength)
            newHorizontalSurfaces.push(s);
    });
    let newVerticalSurfaces = [];
    surfaces.verticalSurfaces.forEach(s => {
        if(Math.abs(s.length) >= minVerSurfaceLength)
            newVerticalSurfaces.push(s);
    });

    return {
        horizontalSurfaces: newHorizontalSurfaces,
        verticalSurfaces: newVerticalSurfaces,
    };
}

// Recursive function to piece together all the surfaces of the buildings.
// Go until reaching a corner. Then record the surface, turn the corner, and repeat.
// direction: can be "up", "down", or "right".
function traverseCityScape(grid, surfaces, direction, coord){
    // Go until reaching a corner.
    let foundEndOfSegment = false;
    let y = undefined;
    let nextCoord = undefined;
    switch(direction){
        case "right":
            let x = coord.x;
            while(!foundEndOfSegment){
                if(x === grid.length){
                    // We have reached the end of the screen. Return.
                    return;
                }
                nextCoord = grid[x + 1][coord.y];
                let coordBeneathNextCoord = grid[x + 1][coord.y - 1];
                if(nextCoord === true){
                    // We have reached a new building that goes taller than this one.
                    // Therefore we need to record this segment, and turn the corner up.
                    foundEndOfSegment = true;
                    surfaces.horizontalSurfaces.push({
                        startCoord: { x: coord.x, y: coord.y },
                        length: 1 + x - coord.x
                    });
                    traverseCityScape(grid, surfaces, "up", { x: x, y: coord.y });
                }
                else if(coordBeneathNextCoord === false){
                    // We have reached the end of this building's roof.
                    // Therefore we need to record this segment, and turn the corner down.
                    foundEndOfSegment = true;
                    surfaces.horizontalSurfaces.push({
                        startCoord: { x: coord.x, y: coord.y },
                        length: 1 + x - coord.x
                    });
                    traverseCityScape(grid, surfaces, "down", { x: x + 1, y: coord.y - 1 });
                }
                else{
                    // The next coord is empty and the coord beneath it is full. So we are still
                    // on the roof of this building. Therefore continue.
                    x++;
                }
            }
            break;
        case "up":
            y = coord.y;
            while(!foundEndOfSegment){
                nextCoord = grid[coord.x][y + 1];
                let coordRightOfNextCoord = grid[coord.x + 1][y + 1];
                if(coordRightOfNextCoord === false){
                    // We have reached the top of this building.
                    // Therefore we need to record this segment, and turn the corner right.
                    foundEndOfSegment = true;
                    surfaces.verticalSurfaces.push({
                        startCoord: { x: coord.x, y: coord.y },//the y is wrong. should be 89, but is 335.
                        length: 1 + y - coord.y
                    });
                    traverseCityScape(grid, surfaces, "right", { x: coord.x + 1, y: y + 1 });
                }
                else{
                    // The coord right of next coord is full, so that means the building is continuing up.
                    // Therefore continue.
                    // Note: this assumes that no building reaches the very top of the screen.
                    y++; 
                }
            }
            break;
        case "down":
            y = coord.y;
            while(!foundEndOfSegment){
                if(y === 0){
                    // We have reached the bottom of the building at ground level.
                    // Therefore record this segment, and then skim along the ground to the next building,
                    // and pick up going up there.
                    foundEndOfSegment = true;
                    surfaces.verticalSurfaces.push({
                        startCoord: { x: coord.x, y: coord.y },
                        length: -1 * (1 + coord.y - y) // in this case, negative to indicate down direction
                    });
                    let groundX = coord.x + 1;
                    let groundHasEnded = false;
                    let screenHasEnded = false;
                    while(!groundHasEnded){
                        if(groundX === grid.length)
                        {
                            // We have reached the end of the screen.
                            groundHasEnded = true;
                            screenHasEnded = true;
                        }
                        else{
                            nextCoordRight = grid[groundX][0];
                            if(nextCoordRight === true)
                                // We have reached a building. 
                                // Therefore we are done being at ground level, and can go up the new building.
                                groundHasEnded = true;
                            else
                                groundX++;
                        }
                    }
                    if(screenHasEnded)
                        return;
                    else
                        traverseCityScape(grid, surfaces, "up", { x: groundX - 1, y: 0 });
                }
                else{
                    nextCoord = grid[coord.x][y - 1];
                    if(nextCoord === true){
                        // We have reached the corner of a new building meeting this building's right side.
                        // Therefore we need to record this segment, and turn the corner right.
                        foundEndOfSegment = true;
                        surfaces.verticalSurfaces.push({
                            startCoord: { x: coord.x, y: coord.y },
                            length: y - 1 - coord.y
                        });
                        traverseCityScape(grid, surfaces, "right", { x: coord.x, y: y });
                    }
                    else{
                        // The next coordinate is empty. We have not reached the bottom of this building.
                        // Therefore continue.
                        y--;
                    }
                }
            }
            break;
    }
}

function generateBuildings(grid, margin, maxY, maxX, numberOfBuildings, maxLightSize,
    minBldgWidthPercent, maxBldgWidthPercent, minBldgHeightPercent, maxBldgHeightPercent)
{
    let buildings = [];

    //Each building has a height and a width.
    //The height can't be more than a certain percentage of the total. Same for width.
    for(let i = 0; i < numberOfBuildings; i++)
    {
        //Pick a height and width.
        let heightPercentage = generateRand(minBldgHeightPercent, maxBldgHeightPercent);
        let height = maxY * heightPercentage / 100;

        let widthPercentage = generateRand(minBldgWidthPercent, maxBldgWidthPercent);
        let width = maxX * widthPercentage / 100;

        //Now try to fit that into the grid.
        const numberOfTries = 5;
        const xMargin = 20;
        let wasSuccessful = false;
        for(let j = 0; j < numberOfTries; j++)
        {
            if(wasSuccessful === false){
                //Pick an x coordinate at random from which to start the building.
                //There's no point in picking one right at the beginning or end, and it can't
                //be so close to the end that the building overflows off the edge of the screen.
                let xCoord = 0;//The very first building will be all the way on the left.
                if(i > 0 && i < numberOfBuildings - 1)//buildings in middle are picked random.
                    xCoord = generateRand(xMargin, maxX - xMargin - width);
                if(i === numberOfBuildings - 1)//last building is close to the end
                    xCoord = Math.round(maxX - xMargin - width - 1);

                //Determine if the building will validly fit given that xCoord.
                wasSuccessful = true;//TODO
                //If it fits, then end loop and create building.
                if(wasSuccessful){
                    let building = {
                        Height: height,
                        Width: width,
                        XCoord: xCoord,
                    };
                    buildings.push(building);

                    createBuildingDiv(building, margin, maxX, maxY, maxLightSize);

                    addToGrid(building, grid);

                    //End loop as we have now been successful.
                    break;
                }
            }
        }
    }
}
function createBuildingDiv(building, margin, maxX, maxY, maxLightSize){
    let cityscape = document.getElementById('cityscape');
    let buildingDiv = document.createElement('div');
    buildingDiv.classList.add("building");

    buildingDiv.style.width = building.Width + maxLightSize + "px";
    buildingDiv.style.height = building.Height + maxLightSize + "px";
    buildingDiv.style.left = building.XCoord;
    buildingDiv.style.top = maxY - building.Height;
    
    cityscape.appendChild(buildingDiv);
}
function addToGrid(building, grid)
{
    //the grid is x long to represent the x pixels wide.
    //each element in the grid is an array y long to represent the y pixels high.

    //Start at the x coordinate.
    for(let x = building.XCoord; x < building.XCoord + building.Width + 1; x++)
    {
        //This is a vertical strip of the building, 1 pixel wide, and y pixels high.
        for(let y = 0; y < building.Height + 1; y++)
        {
            // This is a single pixel in the strip. Add it to the grid.
            grid[x][y] = true;
        }
    }
}
function pickRandomColor(color1, color2, color3, color4, NUMBER_OF_COLORS){
    let num = generateRand(0, NUMBER_OF_COLORS);
    let color = '';
    switch(num){
        case 1:
            color = color1;
            break;
        case 2:
            color = color2;
            break;
        case 3:
            color = color3;
            break;
        case 4:
            color = color4;
            break;
    }
    return color;
}

 

你可能感兴趣的:(javascript,开发语言,ecmascript)