我的女同事问我这个效果怎么做(超炫酷粒子效果)

1,361 阅读1分钟

前言

前两天我女同事看到了明日方舟首页的一个粒子动画效果,感觉非常炫酷,就问我这个效果是怎么实现的。身为一个自尊心颇强男性,不允许我说不行,于是就做了一个简单的demo,效果图如下(实现灰常简单,几十行代码搞定):

- Demo1

鹿.gif

- Demo2

女神.gif

前期准备

  • 一个html文件
  • 一张剪影图片,如:

image.png

思路

  1. 准备一张剪影图片,通过canvas的drawImage绘制出来
  2. 通过getImageData获取图片像素点信息
  3. 匹配剪影的颜色值,如果像素点为黑色则生成白色圆点来替换
  4. 控制白色圆点的随机位置、速度等因素,使圆点动起来

开整

  • 初始化canvas和img
<div class="container">
    <canvas id="canvas" width="800" height="800"></canvas>
</div>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext("2d")
    const img = new Image();
    img.src = "./imgs/剪鹿.png";
    // 储存图片像素信息
    let imgData = null
    // 防止图片过大超过canvas大小,所以限制图片宽度为600,等比例缩放
    let imgW = 600
    let imgH = null
    // 控制粒子扩散或聚合
    let flag = true
    
    img.onload = function () {
        imgH = imgW * (img.height / img.width)
        ctx.drawImage(img, 0, 0, imgW, imgH);
        imgData = ctx.getImageData(0, 0, imgW, imgH).data;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 生成点的实例
        pointInit(imgData)
        move()
    }
  • 初始化白色圆点
function pointInit(imgData) {
    const gap = 4;
    for (var h = 0; h < imgH; h += gap) {
        for (var w = 0; w < imgW; w += gap) {
            var position = (imgW * h + w) * 4;
            var r = imgData[position], g = imgData[position + 1], b = imgData[position + 2];
            // 当rgb都为0时,说明颜色值为黑色
            if (r + g + b === 0) {
                pointArr.push(new Point(1, w, h))
            }
        }
    }
}
  • 创建圆点的类
class Point {
    constructor(size, w, h) {
        // 保留图像初始位置,方便粒子聚合成图像
        this.orw = w    
        this.orh = h
        // 随机位置
        this.x = Math.random() * canvas.width
        this.y = Math.random() * canvas.height
        this.size = size
        this.w =  w
        this.h =  h
        // 速度
        this.spx = (w - this.x) / 2 / 20
        this.spy = (h - this.y) / 2 / 20
    }
    // 圆点每次位置变化
    update() {
        this.spx = (this.w - this.x) / 2 / 20
        this.spy = (this.h - this.y) / 2 / 20
        if (Math.abs(this.w - this.x) <= Math.abs(this.spx)) {
            this.x = this.w
        } else {
            this.x += this.spx
        }
        if (Math.abs(this.h - this.y) <= Math.abs(this.spy)) {
            this.y = this.h
        } else {
            this.y += this.spy
        }
    }
    // 渲染圆点
    rander() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)
        ctx.fillStyle = '#fff';
        ctx.fill()
        ctx.closePath();
    }

}
  • 接下来让圆点动起来
    function move() {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        pointArr.forEach(point => {
            point.update()
            point.rander()
        })
        // 代替setTimeout提升性能
        requestAnimationFrame(move)
    }
  • 点击事件控制粒子聚合
// 散开聚合
function changePic() {
    flag = !flag
    pointArr.forEach(point => {
        point.w = flag ? point.orw : Math.random() * canvas.width
        point.h = flag ? point.orh : Math.random() * canvas.height
    })
}

到了这里,基本就已经做完了,粒子效果已经可以正常显示了,如果要换成其他的图形直接改变图片的src即可更换图形。

有什么问题可以在评论区讨论。(撰文不易,点赞鼓励)

- 博客
-前端知识文档总结