canvas 粒子线条

引言

本来是想学习一下 Pixi.js 的粒子效果,结果做出来的效果粗粗糙糙的,想要做出好看的效果粒子运动轨迹离不开算法,还需要些时间研究= =。于是在网上看了一篇用 canvas 做的粒子 demo 博客,感觉也挺不错的,实现思路简单,于是自己也做了一个,但效果跟前者是不一样的,是在前者的基础上做了些改变。


目录

  1. 实现原理

    1.1 让粒子动起来

    1.2 比对

    1.3 画线

  2. 代码

  3. 另一种效果

  4. 整合封装

  5. 总结

  6. 了解更多


1、实现原理

先看下效果图:

屏幕录制2021-04-22 上午11

1.1 让粒子动起来

效果还是挺炫酷的,实现起来其实也并不难,就是在 canvas 画布上随机生成点位,用 requestAnimationFrame 方法不断更新每个粒子的 x,y 坐标值,就做到了简单的粒子运动效果。

在粒子的 x 或 y 值大于 canvas 宽高或小于 0 时做反向运动:定义两个变量 x, y 轴的运动速度 sx sy 设置速度为 Math.random() * 1, 0~1 区间,正常情况下 粒子的 x,y 值等于 x += sx; y += sy; 在大于 canvas 宽高或小于 0 时将速度值赋值为负数 sx = - sx; sy = -sy; 做反向运动。

1.2 比对

用一个变量数组存储需要绘制的每个粒子信息,在定义一个鼠标粒子对象加入数组当做一个粒子比对,用双重 for 循环让当前粒子跟其他粒子位逐个比对,第一层循环让每个粒子开始移动,判断如果是鼠标粒子则把鼠标移动的 x,y 坐标传给鼠标粒子对象的 x,y 值,第二层循环计算当前粒子与其他粒子距离,如果粒子与粒子的距离小于某值则进行画线。

1.3 画线

在每次 requestAnimationFrame 更新粒子 x,y 位置前先清空上一次粒子的位置,定义一个 max 值用于判断粒子与粒子之间距离是否小于 max 值如果小于 max 值,在他们之间画线,画线可以用 hsla(色相, 饱和度, 亮度, 透明度) 函数,色值用一个变量做累加就好了,透明度也需要一个变量,可以看到效果上在粒子与粒子距离逐渐大于 max 值时线条是一个逐渐弱化的效果:(max - 两点距离) / max;

效果图上可以看到粒子会跟着鼠标走,判断如果比对粒子是鼠标点粒子,并且距离小于 max / 2 时让与鼠标粒子比对的其他粒子的 x,y 值递减,比如粒子与粒子间距离小于 100 就给他画线,此时粒子 x,y 值还在做速度递增运动(现在的递增是逐渐靠近鼠标点位),在小于 50 的时候做反方向运动(逐渐远离鼠标点),大于50靠近鼠标点,小于50远离鼠标点,这样就行成了一个看似吸附点效果。


2、代码

let mons = {
  x: -100,
  y: -100,
  r: 1
};
class Point {
        constructor (max, name) {
            this.x = Math.random() * canvas.width; // x坐标
            this.y = Math.random() * canvas.width; // y坐标
            this.r = 1;
            this.max = max;
            this.name = name;
            this.sx = Math.random() * 2 - 1; // 速度
            this.sy = Math.random() * 2 - 1; // 速度
            this.hue = random( 60, 100 );
            this.brightness = random(0, 250);
        }

        // 随机移动
        move (x, y) {
            this.x += this.sx;
            this.y += this.sy;
            this.hue += 1;

            if (this.name) {
                this.x = x;
                this.y = y;
            }
           
            if (this.x > canvas.width || this.x < 0) this.sx = -this.sx;
            if (this.y > canvas.height || this.y < 0) this.sy = -this.sy;
        }

        // 画线
        drawLine (ctx, p, isDraw){
            let dx = this.x -p.x;
            let dy = this.y -p.y;
            let d = dx * dx + dy * dy;
            if (d < p.max) {
                if (p === mons.point && d > (p.max / 2)) {
                    this.x -= dx * 0.03;
                    this.y -= dy * 0.03;
                }
                let alpha = (p.max - d) / p.max;
                ctx.beginPath();
                ctx.strokeStyle ='hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + alpha + ')'
                ctx.strokeWidth = 1;
                ctx.moveTo(this.x, this.y);
                ctx.lineTo(p.x, p.y);
                ctx.stroke();
            }
        }
    }

    let points = [];
    for (let i = 0; i < 200; i++) {
        points.push(new Point(6000));
    }
    mons.point = new Point(20000, 'touch');
    points.push(mons.point);

    const paint = () => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (let i = 0; i < points.length; i++) {
            points[i].move();
            if (points[i].name) {
                points[i].move(mons.x, mons.y);
            }
            for (let j = i + 1; j < points.length; j++){
                if (points[j] === mons.point) {
                    points[i].drawLine(ctx, points[j], true);
                }
                points[i].drawLine(ctx, points[j]);
            }
        }
    }

    function random( min, max ) {
        return Math.random() * ( max - min ) + min;
    }

    function loop(params) {
        requestAnimationFrame(loop);
        paint();
    }
    loop();


3、另一种效果

效果图:

屏幕录制2021-04-22 下午5

这个效果也是挺不错的,看起来很丝滑。

这个效果是在上一个效果的基础上做了些修改,改成了类似鼠标拖尾的效果,实现起来比上一个效果也简单一些。

for 循环 new Point 类添加到 points 粒子数组中,requestAnimationFrame 执行粒子运动与画线,去掉了鼠标粒子与其他粒子距离的判断只在鼠标移动时 new Point 类添加到粒子数组把鼠标的 x,y 值传入 Point 类: points.push(new Point(x, y)) 在粒子数组长度大于 80 shift() 移除粒子数组第一项。

canvas.addEventListener('mousemove', function(e) {
        e.preventDefault();
        // 根据鼠标位置添加点进数组
        points.push(new Point(e.clientX,e.clientY));
        // 多余80个点移除数组第一个点
        if (points.length > 80) {
            points.shift();
        }
    });


4、整合封装

做出两个效果后就尝试着整合封装了一下,通过传参 isBg 判断展现的是哪种效果 true 为第一种效果 false 为第二种,pc端上两种效果都能呈现,但移动端上目前设置只能展现第二种,第一种效果在手机上体验感比较差 100 个粒子以上运动就会比较卡了。

使用方法:

引入 js js 源码地址

let point = new particleLine({
  canvas: canvas, // canvas
  width: 宽, // canvas宽
  height: 高, // canvas高
  num: 粒子数, // 粒子数
  isBg: true // true: 第一种效果 false: 第二种效果
});
point.play(); // 执行

demo 链接:demo

参考博客:

html canvas粒子线条组合动画背景特效

5、总结

这个 demo 实现起来还是比较简单的,效果也挺不错的。难点应该在于鼠标粒子和普通粒子间的计算。canvas 粒子还有很多其他炫酷的效果,比如粒子变换特定形状,用getImageData 方法获取画布指定矩形像素,通过获取到的点位跟 RGBA 值用粒子绘画出来。


6、了解更多

原文链接:canvas 粒子线条

你可能感兴趣的:(canvas 粒子线条)