jtwang7 / Project-Note

开发小记 - 项目里的一些新奇玩意儿
1 stars 0 forks source link

基于 canvas 的雪花飘落效果 #4

Open jtwang7 opened 3 years ago

jtwang7 commented 3 years ago

基于 canvas 的雪花飘落效果

代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>雪花飘路特效</title>
</head>

<body>
    <canvas id="catkins"></canvas>
    <script src="./index.js"></script>
</body>

</html>
// ./index.js
function rand(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

class Catkins {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    // 飘动加速度
    this.acceleration = rand(this.OPT.acceleration[0], this.OPT.acceleration[1]);
    // 颜色
    this.color = this.OPT.randColor ? rand(0, 255) + "," + rand(0, 255) + "," + rand(0, 255) : this.OPT.color;
    // 透明度
    this.opacity = Math.random();
    // 方向
    this.direction = this.getDirection();
    // 粒子半径
    this.radius = rand(this.OPT.radius[0], this.OPT.radius[1]);
  }

  OPT = {
    selector: "#catkins", // <canvas> 的 id
    amount: 10, // 粒子数量
    speed: 0.1, // pixels per frame
    color: "rgb(255, 255, 255)", // 粒子颜色
    randColor: false, // 是否随机颜色
    acceleration: [2, 100], // 加速度范围
    radius: [1, 2], // 粒子半径范围
  };

  // 随机初始粒子移动方向
  getDirection() {
    return {
      x: Math.random(),
      y: Math.random(),
    }
  }

  // 移动粒子,结合 requestAnimation 实现移动效果
  go() {
    this.x += this.OPT.speed * this.direction.x * this.acceleration / 2;
    this.y += this.OPT.speed * this.direction.y * this.acceleration / 2;
  }
}

class CatkinsCanvas extends Catkins {
  // 画布对象
  ctx = null;
  // 点数组
  store = [];

  // 创建画布
  createCanvas() {
    let node = document.querySelector(this.OPT.selector);
    this.ctx = node.getContext("2d");

    window.addEventListener('resize', function () {
      this.setCanvasSize();
    })
  }

  // 设置画布大小
  setCanvasSize() {
    try {
      if (this.ctx) {
        this.ctx.canvas.width = window.innerWidth;
        this.ctx.canvas.height = window.innerHeight;
      } else {
        throw new Error('canvas 对象不存在')
      }
    } catch (err) {
      console.log(err);
    }
  }

  // 添加粒子
  addCatkins() {
    let x = rand(-200, window.innerWidth + 200);
    let y = 0
    this.store.push(new Catkins(x, y));
  }

  draw() {
    // 清除画布
    this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
    this.store.forEach((item, i, array) => {
      // 若粒子飘出可视范围,则从数组中移除
      if (Math.abs(item.x) > this.ctx.canvas.width || (Math.abs(item.y) > this.ctx.canvas.height)) {
        array.splice(i, 1);
      } else {
        this.drawCircle(item);
      }
    });

    window.requestAnimationFrame(() => this.draw());
  }

  // 绘制粒子
  drawCircle(item) {
    item.go();
    this.ctx.beginPath();
    this.ctx.arc(item.x, item.y, this.radius, 0, 2 * Math.PI, false);
    this.ctx.fillStyle = this.color;
    this.ctx.fill();
  }

  // 开启动画
  init() {
    this.createCanvas();
    this.setCanvasSize();
    // 画布背景
    this.ctx.fillStyle = 'rgba(0, 0, 0, 0)';
    this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

    // 添加粒子
    window.setInterval(() => {
      if (this.store.length < this.OPT.amount) {
        this.addCatkins();
      }
    }, 1000 / this.OPT.amount);

    window.requestAnimationFrame(() => this.draw()); // 开启第一次动画帧
  }
}

export default CatkinsCanvas;