canvas实现粒子特效

canvas实现粒子特效

前言

前段时间在学习canvas,实现了一些有趣的功能,最近有时间就把它拿出来分享一下。

成品图

在这里插入图片描述

思路

将这个功能先分析拆解一下:

  • 一张画布上放一个背景图
  • 有很多小球在运动
  • 每个小球的速度和方向不同
  • 碰撞到边界会反弹

我会按照以下顺序来进行实现:
1.图片作为背景
2.绘制一个静止小球
3.让一个小球运动起来
4.绘制多个小球并让他们运动起来
5.实现小球的反弹

一、图片作为背景

这一步没有使用canvas的绘制图片功能,直接css样式走起。
background-image: url()
唯一要注意的点就是记得让背景图片的z-index: -2;堆叠顺序最低即可。

二、绘制一个静止小球

这一步也没什么说的,就是在画布上随便画一个实心圆即可。

<canvas id="canvas" width="500" height="500"></canvas>

  <script>
    let canvas = document.querySelector('#canvas');
    let cxt = canvas.getContext('2d');
    let cX = 200;
    let cY = 200;
    let r = 10;
    cxt.beginPath();
    cxt.fillStyle = 'red';
    cxt.arc(cX, cY, r, 0, 2*Math.PI);
    cxt.fill();
  </script>

三、让一个小球运动起来

小球运动就是小球的圆心坐标xy改变
使用定时器实现移动,绘制前要清空前一帧
将绘制一个小球抽成一个方法

    ballDraw(cX, cY, r);
    // 绘制一个运动的圆
    function ballDraw (x, y, r) {
      setInterval(() => {
      // 绘制前要清空前一帧的画布
      cxt.clearRect(0, 0, canvas.width, canvas.height)
      /* 绘制一个圆形 */
      x += 2; // 圆心坐标每次+2
      y += 2;
      cxt.beginPath();
      cxt.fillStyle = 'red';
      cxt.arc(x, y, r, 0, 2*Math.PI);
      cxt.fill();
      }, 1000/60);

四、绘制多个小球并让他们运动起来

绘制小球cxt.arc(x, y, r, 0, 2*Math.PI); 将一个小球的数据都作为一个对象,多个对象合并成一个数组。
x轴和y轴的范围是-2到2。二者合并即为运动的方向和速度。
小球的圆心、半径、颜色、透明度、速度、方向都随机生成。

  • 先绘制多个静止小球
let setArr = [];
let maxNum = 30;
// 绘制生成多个圆
ballSet();
function ballSet () {
  let timer1 = setInterval(() => {
    // 控制小球总数
    if (setArr.length >= maxNum) {
      clearInterval(timer1);
    }
    let cX = 200 + Math.ceil(Math.random() * 200); // 200--400  Math.ceil对浮点数向上取整
    let cY = 200 + Math.ceil(Math.random() * 200); // 初始Y坐标
    let r = 5 + Math.ceil(Math.random() * 5); // 半径5-12
    let red = Math.floor(Math.random() * 256); // 0--255
    let green = Math.floor(Math.random() * 256); // rgba
    let blue = Math.floor(Math.random() * 256);
    let alpha = 0.5 + Math.random() * 0.3; // 0.5--0.8 透明度
    let sX = -2 + Math.ceil(Math.random() * 4); // -2 --- 2  x轴速度
    let sY = -2 + Math.ceil(Math.random() * 4);
    let obj = {
      cX: cX,
      cY: cY,
      r: r,
      red: red,
      green: green,
      blue: blue,
      alpha: alpha,
      sX: sX,
      sY: sY,
      bgColor: `rgba(${red},${green},${blue},${alpha})`,
    };
    if (obj.sX !== 0 || obj.sY !== 0){ // 防止速度为0 ,不要静止的小球
      setArr.push(obj);
    }
  }, 1000/20);  // 每秒20
}

这里要注意因为使用Math.random()生成的速度,可能会出现x或y轴速度为0的小球,在画布上就会表现为水平移动、垂直移动或静止不动,我们要剔除这种小球。

  • 让所有小球都运动起来
    逻辑跟让一个小球运动起来差不多,就是多个遍历
// 绘制圆的运动 
ballDraw(setArr);
function ballDraw (arr) {
  setInterval(() => {
  // 绘制前要清空前一帧的画布
  cxt.clearRect(0, 0, canvas.width, canvas.height)
  arr.forEach(item => { // 循环遍历setArr生成小球
    /* 绘制一个圆形 */
    item.cX += item.sX;
    item.cY += item.sY;
    cxt.beginPath();
    cxt.fillStyle = item.bgColor;
    cxt.arc(item.cX, item.cY, item.r, 0, 2*Math.PI);
    cxt.fill();
  })
  }, 1000/60); // 每秒60
}

五、实现小球的反弹

反弹的原理更简单,小球的边缘碰到画布边界后将x轴和y轴的速度坐标取反即实现反弹。
在这里插入图片描述
要注意:小球的圆心x坐标加上半径r大于等于右边界x坐标即代表小球碰撞到边界。同理上、下、左边界的碰撞也是类似的算法。

// 绘制圆的运动 
ballDraw(setArr);
function ballDraw (arr) {
  setInterval(() => {
  // 绘制前要清空前一帧的画布
  cxt.clearRect(0, 0, canvas.width, canvas.height)
  arr.forEach(item => {
    /* 边界碰撞处理 - 速度取反 */
    if ((item.cX + item.r >= canvas.width) || (item.cX - item.r <= 0)) { // 碰到左右边界,反向弹
      item.sX *= -1
    }
    if ((item.cY + item.r >= canvas.height) || (item.cY - item.r <= 0)) { // 碰到上下边界,反向弹
      item.sY  *= -1
    }
    item.cX += item.sX;
    item.cY += item.sY;
    /* 绘制一个圆形 */
    cxt.beginPath();
    cxt.fillStyle = item.bgColor;
    cxt.arc(item.cX, item.cY, item.r, 0, 2*Math.PI);
    cxt.fill();
  })
  }, 1000/60); // 每秒60
}

最后贴一下完成代码

  • HTML
<canvas id="canvas" width="1200" height="500"></canvas>
<h2>粒子特效</h2>
  • CSS
 #canvas {
      width: 100%;
      background-image: url('./img/997.jpg');
      background-size: 100%;
      z-index: -2;
      position: absolute;
      left: 0;
      top: 0;
    }
    h2 {
      position: absolute;
      top: 50px;
      left: 50%;
      margin-left: -140px;
      font-size: 50px;
      color: aliceblue;
      text-align: center;
      letter-spacing: 20px; /* 文字间隔 */
    }
  • JS
let canvas = document.querySelector('#canvas');
    let cxt = canvas.getContext('2d');
    let setArr = [];
    let maxNum = 30;
    // 绘制生成多个圆
    ballSet();
    function ballSet () {
      let timer1 = setInterval(() => {
        // 控制小球总数
        if (setArr.length >= maxNum) {
          clearInterval(timer1);
        }
        let cX = 200 + Math.ceil(Math.random() * 200); // 200--400  Math.ceil对浮点数向上取整
        let cY = 200 + Math.ceil(Math.random() * 200); // 初始Y坐标
        let r = 5 + Math.ceil(Math.random() * 5); // 半径5-12
        let red = Math.floor(Math.random() * 256); // 0--255
        let green = Math.floor(Math.random() * 256); // rgba
        let blue = Math.floor(Math.random() * 256);
        let alpha = 0.5 + Math.random() * 0.3; // 0.5--0.8 透明度
        let sX = -1 + Math.ceil(Math.random() * 6); // -1 --- 5  x轴速度
        let sY = -1 + Math.ceil(Math.random() * 6);
        let obj = {
          cX: cX,
          cY: cY,
          r: r,
          red: red,
          green: green,
          blue: blue,
          alpha: alpha,
          sX: sX,
          sY: sY,
          bgColor: `rgba(${red},${green},${blue},${alpha})`,
        };
        if (obj.sX !== 0 || obj.sY !== 0){ // 防止速度为0 ,不要静止的小球
          setArr.push(obj);
        }
      }, 1000/20);  // 每秒20
    }
    // 绘制圆的运动 
    ballDraw(setArr);
    function ballDraw (arr) {
      setInterval(() => {
      // 绘制前要清空前一帧的画布
      cxt.clearRect(0, 0, canvas.width, canvas.height)
      arr.forEach(item => {
        /* 边界碰撞处理 - 速度取反 */
        if ((item.cX + item.r >= canvas.width) || (item.cX - item.r <= 0)) { // 碰到左右边界,反向弹
          item.sX *= -1
        }
        if ((item.cY + item.r >= canvas.height) || (item.cY - item.r <= 0)) { // 碰到上下边界,反向弹
          item.sY  *= -1
        }
        item.cX += item.sX;
        item.cY += item.sY;
        /* 绘制一个圆形 */
        cxt.beginPath();
        cxt.fillStyle = item.bgColor;
        cxt.arc(item.cX, item.cY, item.r, 0, 2*Math.PI);
        cxt.fill();
      })
      }, 1000/60); // 每秒60
    }

下面开始技术总结

咳咳!没什么可总结的,都是简单的代码-_-||

发布了5 篇原创文章 · 获赞 7 · 访问量 1947

猜你喜欢

转载自blog.csdn.net/qq_35192247/article/details/103700759