从0开始canvas系列终章 --- 打造炫酷粒子效果

1,267 阅读2分钟

从0开始canvas系列

从0开始canvas系列一 --- canvas画布

从0开始canvas系列二 --- 文本和图像

从0开始canvas系列三 --- 图像像素级操作

从0开始canvas系列四 --- 运动动画绘制

从0开始canvas系列五 --- 动画

从0开始canvas系列六 --- 合成与裁剪

从0开始canvas系列终章 --- 打造炫酷粒子系统

知识点

基础知识:canvas画布/文本和动画

文本/图片粒子化核心api:getImageData

脑图

各单元代码及实现思路

粒子类

粒子就是一个圆,定义粒子类构造函数定义绘制圆需要的变量及运动的位置和速度

function Particle(x, y, r, color) {
    /**
     * @description: 粒子的构造函数
     */
    this.endX = x; //目标位置x
    this.endY = y; //目标位置y
    this.startX = randomNumber(1, width); //开始位置x
    this.startY = randomNumber(1, height); //开始位置y
    this.r = r || 1; //粒子半径如果不指定
    this.vx = (Math.random() - 0.5) * 10;
    this.vy = (Math.random() - 0.5) * 10;
    this.color = color || "#fff";
    this.friction = Math.random() * 0.025 + 0.95;//阻力系数
}
Particle.prototype.particleMotion = function () {
    /**
     * @description: 粒子的绘制与运动
    //  */
    //弹性运动模型,距离越远加速度越大
    let disX = this.endX - this.startX;
    let disY = this.endY - this.startY;
    this.vx += disX / 1000;
    this.vy += disY / 1000;
    this.vx *= this.friction;
    this.vy *= this.friction;
    this.startX += this.vx;
    this.startY += this.vy;

	//画圆
    ctx.save();
    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.startX, this.startY, this.r, 0, C);
    ctx.fill();
    ctx.restore();
}

文字绘制

获得input框内值,并将文字绘制到canvas内

function drawText() {
    //获得input内的值
    const text = document.querySelector('.text').value;
    /**
     * @description: 文字绘制到canvas内
     */
    ctx.clearRect(0, 0, width, height);
    ctx.save();
    ctx.font = 'bold 200px arial';
    ctx.textAlign = 'center';
    ctx.fillStyle = "#fff" //定义颜色为白色,为后续找到文字对应的像素点提供判断条件
    ctx.fillText(text, width / 2, height / 1.5);
    ctx.restore();
}

文字粒子化

  1. 通过canvas getImageData api获得每一个像素点数据数组Uint8ClampedArray,每四个数据代表一个像素
  2. 根据canvas 宽高将像素点数组转变为二维坐标得到文字的坐标
  3. 将文字对应的像素点取样(为了性能),并将抽样得到的像素点实例化成粒子
  4. 将所有的粒子保存到一个数组内

如果对getImageData和图形的像素化处理不清楚的,请回看从0开始canvas系列三 --- 图像像素级操作

function particleGenerator() {
    /**
     * @description: 粒子发生器,将canvas全部转化为像素点,canvas中的文字的像素点实例化一个粒子
     * @param {*}
     * @return {particles}
     */
    const ctxUint8ClampedArray = ctx.getImageData(0, 0, width, height).data;
    ctx.clearRect(0, 0, width, height);
    particles = [] //每次执行初始化函数清空粒子
    for (let i = 0; i < height; i += Math.round(width / 300)) {
        for (let j = 0; j < width; j += Math.round(width / 300)) {
        //Math.round(width / 300):粒子抽样,每隔300个像素点作为一个粒子
            let key = (i * width + j) * 4;
            if (ctxUint8ClampedArray[key] !== 0) {
                const color =
                    `rgb(${randomNumber(1,255)},${randomNumber(1,255)},${randomNumber(1,255)})`;
                //实例化粒子类并保存
                particles.push(new Particle(j, i, randomNumber(1, 3), color));
            }
        }
    }
    return particles;
}

动画绘制

每一个粒子相同时间内重复执行粒子绘制方法

function render() {
    ctx.clearRect(0, 0, width, height);
    particles.forEach(particle => {
        particle.particleMotion()
    })
    requestAnimationFrame(render);
}

HTML页面及交互

<div id="header">
    <input type="text" class="text" placeholder="请输入文字">
    <button id='btn'>更改</button>
</div>
<canvas id="canvas"></canvas>
<script>
      const canvas = document.querySelector("#canvas");
      const ctx = canvas.getContext('2d');
      let width = canvas.width = window.innerWidth;
      let height = canvas.height = window.innerHeight;
      const C = Math.PI * 2;//弧度2PI对应360°
      const btn = document.querySelector('#btn');
      btn.addEventListener('click', start);
	
      function start() {
          drawText();
          let particles = particleGenerator();
          render();
      }
      function randomNumber(min, max) {
            /**
             * @description: 产生随机数
             * @param {min,max}
             * @return {number}
             */
            return parseInt(Math.random() * (max - min)) + min
        }
</script>