城市漫游特效

代码语言: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