cover_image

canvas 粒子线条

作者:Wy DigitMagic魔数实验室
2021年07月21日 12:25

引言

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

目录

  1. 实现原理

    1.1 让粒子动起来

    1.2 比对

    1.3 画线

  2. 代码

  3. 另一种效果

  4. 整合封装

  5. 总结

  6. 了解更多

1、实现原理

先看下效果图:

图片


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、另一种效果


效果图:

图片

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

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

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 <script src="./particle-line.js"></script> 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、了解更多

官网:treedom
站酷:treedom0
微信公众号: DigitMagic 魔数实验室


继续滑动看下一个
DigitMagic魔数实验室
向上滑动看下一个