canvas实现粒子特效

作者: 小鸟游露露 | 来源:发表于2019-12-25 15:53 被阅读0次

    canvas实现粒子特效

    前言

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

    成品图

    粒子特效.gif

    思路

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

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

    我会按照以下顺序来进行实现:
    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轴的速度坐标取反即实现反弹。


    微信截图_20191225154220.png

    要注意:小球的圆心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
        }
    

    下面开始技术总结

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

    相关文章

      网友评论

        本文标题:canvas实现粒子特效

        本文链接:https://www.haomeiwen.com/subject/diecoctx.html