城市漫游特效
代码语言:html
所属分类:背景
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> Procedural Canvas cityscape (profile header)</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body { background: black; margin: 0; padding: 0; overflow: hidden; } canvas { background: transparent; background-image: linear-gradient(black 20%, #101 30%, #211 40%, #070702 52%, #000 90%, #000 100%); background-repeat: no-repeat; display: block; margin: 0 auto; width: 100%; max-width: 1800px; height: 300px; } #vignette { background-image: linear-gradient(right, black 0%, transparent 10%, transparent 90%, black 100%); position: absolute; top: 0; left: 50%; width: 100%; height: 300px; max-width: 1800px; -webkit-transform: translateX(-50%); transform: translateX(-50%); z-index: 50; } </style> </head> <body translate="no"> <script> // OVERENGINEERED UNOPTIMIZED CANVAS BULLSH*T // BUT IT'S OKAY SINCE IT'S BLADE RUNNER INNIT // Some stuff left unoptimized / verbose to show the work. // TODO: // - optimize render loop, avoid overdraws, etc // - smoothly fade rows in on the horizon // Constants. Change at own risk const CANVAS_WIDTH = 900; const CANVAS_HEIGHT = 300; const FRAME_TIME = 1000 / 16; const LIGHT_ROWS = 20; const LIGHT_ROW_DEPTH = 2; const LIGHT_SPACING = 0.6; const LIGHT_SIZE = 0.1; const LIGHT_SCATTER = 0.4; const BUILDING_ROWS = 38; const BUILDING_ROW_DEPTH = 1; const BUILDING_ROW_WIDTH = 60; const BUILDING_MIN_HEIGHT = 1.5; const BUILDING_MAX_HEIGHT = 3; const STACK_HEIGHT = 9; const STACK_THRESHOLD = 0.87; const STACK_LIGHT_CHANCE = 0.95; const STACK_LIGHT_SIZE = 0.13; const FADE_GRAY_VALUE = 25; const FADE_OFFSET = 0.35; // Virtual camera. Used in perspective calculations const CAMERA = { x: 0, y: 10, z: 0, fov: 170, dist: 30, zSpeed: 0.005 // Virtual vanishing point XY. Used in perspective calculations };const VP_OFS = { x: 0.5, y: 0.27 // Global hoisted vars for rendering contexts and timers };let c, ctx, output_c, output_ctx; let _t, _dt, _ft; // Seedable random number generator. // Not particularly well-distributed, but fine for this case. // Allows us to emit the same set of random numbers on every frame // so we can consistently re-render the scene. const RNG = { seed: 1, random() { const x = Math.sin(RNG.seed++) * 10000; return x - (x << 0); }, randomInRange(min, max) { return (RNG.random() * (max - min + 1) << 0) + min; } }; // Module to get a random colour from a predefined list. // Uses the seedable RNG const Palette = (() => { const PAL = ['black', '#111', '#113', 'white', 'sliver', '#f88', 'orange', 'oldlace', '#569']; const lastIndex = PAL.length - 1; function getRandomFromPalette() { return PAL[RNG.randomInRange(0, lastIndex)]; } return { getRandom: getRandomFromPalette }; })(); function ceil(n) { var f = n << 0,f = f == n ? f : f + 1; return f; } // Update method of main loop function update() { // Update our global timestamp (used in rendering) _t = Date.now() * 0.001; // Move the camera slowly 'forward' CAMERA.z += CAMERA.zSpeed; } // Draw a frame of the scene. // Uses the current timestamp and the seeded RNG to render a // pseudorandom cityscape with lights and buildings. // We always generate and draw a set amount of city in front of // the camera, so it appears to be endless as we 'fly over' it. // // 1. Clear the whole scene // 2. Render random rows of lights // 3. Render random rows of buildings // 4. Blit scene to onscreen canvas let _$ = { vPointX: 0, vPointY: 0, rowScreenX: 0, MAX_LIGHTS: 0, closestLightRow: 0, rowZ: 0, rowRelativeZ: 0, scalingFactor: 0, rowScreenWidth: 0, rowScreenHeight: 0, rowScreenY: 0, rowScreenLightSpacing: 0, rowLightCount: 0, lightSize: 0, lightHalfSize: 0, lightScreenX: 0, lightScreenY: 0, closestBuildingRow: 0, rowBuildingCount: 0, rowBuildingScreenWidth: 0, rowShade: 0, rowStyleString: '', lightData: [], isStack: false, buildingHeight: 0, buildingScreenHeight: 0, buildingScreenX: 0, buildingScreenY: 0, lightSize: 0, lightHalfSize: 0, lightColor: 0 }; function render() { // Calculate the pixel XY of the vanishing point // (could be done on init, but useful if we ever want to // dynamically move the camera) _$.vPointX = c.width * VP_OFS.x >> 0; _$.vPointY = c.height * VP_OFS.y >> 0; // If we wanted to, we could give each row an X offset // and include it in perspective calculations, // but we just use the centre alignment for each one here. _$.rowScreenX = CAMERA.x + _$.vPointX; // 1. Clear the whole scene... // (canvases are transparent so that the CSS 'sky' gradient can be seen) ctx.clearRect(0, 0, c.width, c.height); output_ctx.clearRect(0, 0, output_c.width, output_c.height); // 2. Render random rows of lights... // Calculate the closest row to the camera so we // can render the required number of rows into the distance _$.closestLightRow = Math.floor(CAMERA.z / LIGHT_ROW_DEPTH); // Draw each row of lights for (let i = 0; i < LIGHT_ROWS; i++) { // Calculate this row's base Z position // and Z relative to camera _$.rowZ = _$.closestLightRow * LIGHT_ROW_DEPTH + LIGHT_ROW_DEPTH * i; _$.rowRelativeZ = _$.rowZ - CAMERA.z; // Don't draw the row if it's behind the camera, // or beyond the camera's draw distance if (_$.rowRelativeZ <= 0 || _$.rowRelativeZ > CAMERA.dist) { continue; } // Get the perspective scaling factor and pixel Y position for this row _$.scalingFactor = CAMERA.fov / _$.rowRelativeZ; _$.rowScreenY = CAMERA.y * _$.scalingFactor + _$.vPointY; // Don't draw the row if it's off-canvas if (_$.rowScreenY > c.height) { continue; } // Calculate the spacing and number of lights we need to render for this row _$.rowScreenLightSpacing = LIGHT_SPACING * _$.scalingFactor; _$.rowLightCount = c.width / _$.rowScreenLightSpacing; // Seed the RNG in a way that gets us decent distribution // for the random lights RNG.seed = _$.rowZ * 0.573; // Render the random lights for this row for (let j = 0; j < _$.rowLightCount; j++) { // Randomize light size, with perspective _$.lightSize = RNG.random() * (LIGHT_SIZE * _$.scalingFactor); _$.lightHalfSize = _$.lightSize * 0.5; // Randomly offset the XY of the light, with perspective _$.lightScreenX = j * _$.rowScreenLightSpacing + RNG.random() * LIGHT_SCATTER * _$.scalingFactor - _$.lightHalfSize; _$.lightScreenY = _$.rowScreenY + RNG.random() * LIGHT_SCATTER * _$.scalingFactor.........完整代码请登录后点击上方下载按钮下载查看
网友评论0