canvas实现自定义中文跑马灯粒子效果

废话不多说,先看效果
视频效果
动手体验
实现此功能一共有两个小点需要考虑

  1. 如何将中文转为点阵
  2. 如何实现粒子效果

如何将中文转为点阵
粒子效果需要每一个字符都是由点组成的,我们都知道,英文转为点阵或者数组很容易,只有26个字母要区分大小写也不过52个,写一个映射表即可比如A可以写成
image.png

但是汉字千千万,穷举当然是不现实的,所以需要另辟蹊径。
正好canvas提供了getImageData方法,此方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据
对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:

  • R - 红色 (0-255)
  • G - 绿色 (0-255)
  • B - 蓝色 (0-255)
  • A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)

所以我们可以将画布设置为白色rgb(255,255,255),文字设置为红色rgb(255,0,0),将imageData以画布宽高为限制,4为间隔进行遍历,如果

imgData.data[i] === 255 && imgData.data[i + 1] === 0 && imgData.data[i + 2] === 0 && imgData.data[i + 3] === 255

就将该点xy坐标存起来。

let dotList = []
dotList.push(new Dot(x,y))

基于画布宽高遍历,300*150的画布就有45000个点。由于我们做的是粒子效果,不需要那么多的点,所以需要设置一个gap。进行跳跃取点。

let dotList = [];
for (let x = 0; x < imgData.width; x += +this.gap) {
    for (let y = 0; y < imgData.height; y += +this.gap) {
        let i = (y * imgData.width + x) * 4;
        // 判断像素点是不是红色
        if (
            imgData.data[i] === 255 &&
            imgData.data[i + 1] === 0 &&
            imgData.data[i + 2] === 0 &&
            imgData.data[i + 3] === 255
        ) {
          dotList.push({x,y});
        }
      }
    }

这样就取得了所有需要用到的点坐标了,将坐标数组转为阵的方式也很简单,就是创建一个二维数组,长度是画布的高,每一项长度是画布的宽。然后根据xy的值向数组对应位置插入就好,不再赘述。

如何实现粒子效果
有了点数组,已经可以向画布渲染出文字了,为了美观可以再给加点入场动效。这就是纯canvas动画的方面了,我写了一个很简单从四处飞进来的动效。
实现原理是针对每个点,先基于当前xy随机加上一个正负1000以内的数字,作为初始点。结束点就是原本的xy啦。动画部署设定为120帧。然后根据差值算出每一帧需要移动多少距离。在requestAnimationFrame中循环步数加一即可

  class MoveArc{
    constructor(position,context2,duration=120) {
      this.position = position;
      this.context2 = context2;
      this.randomX = Math.random()*1000;
      this.randomY = Math.random()*1000;
      this.startX = this.position.x + this.randomX;
      this.startY = this.position.y + this.randomY;
      this.duration = duration;
      this.gapX = this.randomX/this.duration
      this.gapY = this.randomY/this.duration
      this.step = 0;
    }
    draw(){
      this.context2.beginPath();
      this.context2.arc(this.startX - this.gapX * this.step, this.startY - this.gapY * this.step, 2, 0,2*Math.PI)
      this.context2.fill();
      this.context2.closePath();
      if(this.step !== this.duration){
        this.step++
      }
    }
  }

至此所有功能已经实现啦。

拓展设想
1.获取的数组点阵,可以通过svg方式渲染,这样就可以得到任何一个中文字符的矢量图形了,不仅可以用来做无损渲染方面的东西,也可以作出更多炫酷的动效,比如实现按笔划手写的效果。
2.由于本人比较懒,没有对代码进行优化。都是最土最暴力的写法,已知如果将间隔设置为1,动画会稍稍有些卡顿。我大致想了一下有两个思路尝试解决,一是上算法减少复杂度,二是用WebWorker。
代码地址,大佬拍砖

你可能感兴趣的:(canvas,前端,javascript)