【问题标题】:How to create JS Canvas Top Down Rain Effect or Reverse Warp Speed如何创建 JS Canvas 自上而下的雨效果或反向扭曲速度
【发布时间】:2021-07-01 13:31:04
【问题描述】:

我试图弄清楚如何创建画布雨效果,但从自上而下的角度来看。基本上就像你在低头看着地面,你正在看着雨落在你周围,并以一个角度略微下降到中心,但在结束之前它会下降很短的距离。除了一些不是画布的游戏开发之外,我在搜索中没有看到任何内容。

我也在寻找不需要外部库的解决方案。

我已经能够修改一些扭曲速度效果代码来反转粒子上的方向以下降到背景而不是屏幕外,但它会继续向中心无限远,而不是重绘或循环即将到来的粒子从屏幕外。

我认为这与这部分代码中的粒子不掉屏有关。我是在正确的轨道上还是我不能用当前的代码来实现这个效果?

clear();

  const cx = w / 2;
  const cy = h / 2;

  const count = stars.length;
  for (var i = 0; i < count; i++) {
    const star = stars[i];

    const x = cx + star.x / (star.z * 0.001);
    const y = cy + star.y / (star.z * 0.001);

    if (x < 0 || x >= w || y < 0 || y >= h) {
      continue;
    }

    const d = star.z / 1000.0;
    const b = 1000 - d * d;

    putPixel(x, y, b);
  }

这是我正在查看的完整代码。任何帮助都将不胜感激。

const canvas = document.getElementById("canvas");
const c = canvas.getContext("2d");

let w;
let h;

const setCanvasExtents = () => {
  w = document.body.clientWidth;
  h = document.body.clientHeight;
  canvas.width = w;
  canvas.height = h;
};

setCanvasExtents();

window.onresize = () => {
  setCanvasExtents();
};

const makeStars = count => {
  const out = [];
  for (let i = 0; i < count; i++) {
    const s = {
      x: Math.random() * 1600 - 800,
      y: Math.random() * 900 - 450,
      z: Math.random() * 1000
    };
    out.push(s);
  }
  return out;
};

let stars = makeStars(1000);

const clear = () => {
  c.fillStyle = "black";
  c.fillRect(0, 0, canvas.width, canvas.height);
};

const putPixel = (x, y, brightness) => {
  const intensity = brightness * 255;
  const rgb = "rgb(" + intensity + "," + intensity + "," + intensity + ")";
  c.fillStyle = rgb;
  c.fillRect(x, y, 5, 5);
};

const moveStars = distance => {
  const count = stars.length;
  for (var i = 0; i < count; i++) {
    const s = stars[i];
    s.z += distance;
    while (s.z <= 1) {
      s.z -= 10;
    }
  }
};

let prevTime;
const init = time => {
  prevTime = time;
  requestAnimationFrame(tick);
};

const tick = time => {
  let elapsed = time - prevTime;
  prevTime = time;

  moveStars(elapsed * 1.5);

  clear();

  const cx = w / 2;
  const cy = h / 2;

  const count = stars.length;
  for (var i = 0; i < count; i++) {
    const star = stars[i];

    const x = cx + star.x / (star.z * 0.001);
    const y = cy + star.y / (star.z * 0.001);

    if (x < 0 || x >= w || y < 0 || y >= h) {
      continue;
    }

    const d = star.z / 1000.0;
    const b = 1000 - d * d;

    putPixel(x, y, b);
  }

  requestAnimationFrame(tick);
};

requestAnimationFrame(init);
body {
  position: fixed;
  left: 0px;
  right: 0px;
  top: 0px;
  bottom: 0px;
  overflow: hidden;
  margin: 0;
  padding: 0;
}
&lt;canvas id="canvas" style="width: 100%; height: 100%; padding: 0;margin: 0;"&gt;&lt;/canvas&gt;

【问题讨论】:

    标签: javascript canvas particles


    【解决方案1】:

    除了你想要达到的效果,代码中还有一些其他的问题:

    • 希望这个循环条件永远不会成立,否则它将无限运行:

      while (s.z <= 1) {
        s.z -= 10;
      }
      

      对于您想要实现的目标,此循环毫无意义。它应该被删除。

    • putPixel 的亮度参数需要一个介于 0 和 1 之间的值,但您传递给它的值通常会超出该范围:

      const d = star.z / 1000.0;
      const b = 1000 - d * d;
      

      你需要在那里做1 - d * d

    那么为了达到你想要的效果,你需要:

    • 保持星星按z排序,这样您就可以轻松提取太远的星星,并将它们从数组中移除
    • 每当您移除一颗星时,添加一个新星出现在“下一个”z 层,即当前第一颗星之前的星(至少有z)。
    • 允许在窗口外开始下落,因为随着距离的增加,它们仍可能会变得可见
    • 还可以减小“像素”的大小,因为它们更远。

    以下是根据这些想法更新的代码:

    const canvas = document.getElementById("canvas");
    const c = canvas.getContext("2d");
    
    let w;
    let h;
    
    const setCanvasExtents = () => {
      w = document.body.clientWidth;
      h = document.body.clientHeight;
      canvas.width = w;
      canvas.height = h;
    };
    
    setCanvasExtents();
    
    window.onresize = () => {
      setCanvasExtents();
    };
    
    const makeStars = count => {
      const out = [];
      for (let i = 0; i < count; i++) {
        const star = {
          x: (Math.random() - 0.5) * w * 2,
          y: (Math.random() - 0.5) * h * 2,
          z: i * 1000 / count
        };
        out.push(star);
      }
      return out;
    };
    
    let stars = makeStars(4000);
    
    const clear = () => {
      c.fillStyle = "black";
      c.fillRect(0, 0, canvas.width, canvas.height);
    };
    
    const putPixel = (x, y, brightness, size) => {
      const intensity = brightness * 255;
      const rgb = "rgb(" + intensity + "," + intensity + "," + intensity + ")";
      c.fillStyle = rgb;
      c.fillRect(x, y, size, size);
    };
    
    const moveStars = distance => {
      const count = stars.length;
      for (let star of stars) {
        star.z += distance;
      }
      for (let i = 0; stars[count - 1].z > 1000; i++) { 
        // Replace star
        stars.pop();
        stars.unshift({
          x: (Math.random() - 0.5) * w * 2,
          y: (Math.random() - 0.5) * h * 2,
          z: stars[0].z - 1000/count // keep z ordered
        });
      }
    };
    
    let prevTime;
    const init = time => {
      prevTime = time;
      requestAnimationFrame(tick);
    };
    
    const tick = time => {
      let elapsed = time - prevTime;
      prevTime = time;
    
      moveStars(elapsed * 1.5);
    
      clear();
    
      const cx = w / 2;
      const cy = h / 2;
    
      const count = stars.length;
      for (let star of stars) {
        const x = cx + star.x / (star.z * 0.001);
        const y = cy + star.y / (star.z * 0.001);
    
        if (x < 0 || x >= w || y < 0 || y >= h) {
          continue;
        }
    
        const distance = star.z / 1000;
        const brightness = 1 - distance * distance;
        const size = brightness * 5;
    
        putPixel(x, y, brightness, size);
      }
    
      requestAnimationFrame(tick);
    };
    
    requestAnimationFrame(init);
    body {
      position: fixed;
      left: 0px;
      right: 0px;
      top: 0px;
      bottom: 0px;
      overflow: hidden;
      margin: 0;
      padding: 0;
    }
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    【讨论】:

    • 真的很酷。我不知道为什么,但是如果您将其滚动到视图之外或切换到另一个选项卡然后再返回,它会立即开始同步动画,以便它开始跳动而不是像从一开始那样“流动”。
    • 是的,这是因为OP依赖于经过的时间,当浏览器被占用不调用例程时,下一轮杀了很多star,从顶部开始有很多新的star .我将更新答案以不具有经过的时间依赖性。它使事情变得更糟。
    • 太棒了。现在正在工作!我确实有另一个问题。目标是在其下方放置一张图片,使其看起来像正在下雨的建筑物,但是当我尝试将颜色更改为透明时 - 看起来粒子只是继续构建而没有清除其中一些,并且有很多黑色线条。知道是什么原因造成的吗?这就是我的填充样式 c.fillStyle = "rgba(0, 0, 0, 0)";
    • 对不起,这是不同的问题。如果您无法做到这一点,请提出一个新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 2016-03-18
    • 1970-01-01
    相关资源
    最近更新 更多