前言
前面小包实现一个使用粒子构成 “战场小包” 文字炫酷效果,给小包打开粒子世界的大门,粒子世界真的是炫酷,简单的组合变换可以产生超多炫彩效果,今天小包带大家实现粒子的吸力与斥力效果,可以试玩一下,超级解压和快乐。
体验地址: 粒子万有引力与斥力
实现粒子
首先来实现全屏随机粒子分布,本文章共生成 2500
个粒子,位置 (x,y)
通过随机数生成,粒子形状通过 fillRect
绘制。
// 创建粒子类
class Particle {
constructor() {
// rand 是封装的生成 (n,m) 之间随机数的函数
// x, y 粒子位置
this.x = rand(0, canvas.width);
this.y = rand(0, canvas.height);
// vx, vy 粒子在x, y 轴的移动速度
this.vx = rand(-1, 1);
this.vy = rand(-1, 1);
// 粒子大小
this.r = rand(1, 3);
// 粒子颜色
this.col = `rgba(${Math.round(rand(150, 200))}, ${Math.round(
rand(100, 255)
)}, ${Math.round(rand(180, 255))},${Math.random() + 0.2})`;
}
}
// 生成粒子
function initParticle() {
for (var i = 0; i < part_num; i++) {
P.push(new Particle());
}
}
满屏幕的粒子生成之后,接下来就来做粒子的效果吧。
万有引力效果
要实现万有引力效果,首先需要一个引力中心,初始位置为中心位置,当鼠标开始移动时,鼠标位置为引力中心。为了看起来更明显,通过 canvas
在引力中心处绘制一个十字架。
// X, Y 为引力中心位置
// X, Y 初始化
X = window.innerWidth / 2;
Y = window.innerHeight / 2;
// 绘制十字架
ctx.beginPath();
ctx.moveTo(X, Y - 10);
ctx.lineTo(X, Y + 10);
ctx.moveTo(X - 10, Y);
ctx.lineTo(X + 10, Y);
ctx.stroke();
// 移动鼠标时,修改引力中心位置
window.onmousemove = function (e) {
X = e.clientX;
Y = e.clientY;
};
引力中心确定后,规定粒子移动方式为沿半径向引力中心靠近,越近速度越快。初始化粒子时,提供 vx,vy
属性为粒子分别沿 x,y
轴移动的速度,因此可以通过修改 vx,vy
属性使粒子不断接近引力中心。
在实现上述效果之前,先来复习一下三角函数的知识:
从上面图中可以得出,r*cos
, r*sin
为 r
分别在 x,y
轴上的投影,如果将 r
换成非常微小的长度,r*cos
, r*sin
即为单位时间内粒子在 x,y
轴上即将移动的距离,也就是速度 vx,vy
。因此我们需要求解出粒子到引力中心的角度 angle
。
var dx = p.x - X, // 水平距离
dy = p.y - Y, // 垂直距离
dist = Math.sqrt(dx * dx + dy * dy), // 到引力中心的距离
angle = Math.atan2(dy, dx); // 角度
前端中的坐标系与数学中坐标系略有差异,x
轴正方向相同,但 y
轴正方向向下,各个象限的余弦与正弦大小如下:
复习完上面知识后,接下来就可以计算粒子移动方式了。(粒子越靠近引力中心速度越快)
在坐标系中取三个粒子,我们来实际演练一下:
粒子 | cos /sin | dx /dy | len*cos /len*sin |
---|---|---|---|
A | 正/负 | 正/负 | 正/负 |
C | 负/负 | 负/负 | 负/负 |
D | 正/正 | 正/正 | 正/正 |
dx
与 dy
分别为粒子距离引力中心的长度在 x,y
轴上的投影,存在正负值,例如点 A ,dxA > 0, dyA < 0
,因此如果粒子 A 想靠近引力中心 B ,需设置 vx < 0, vy > 0
,来减少 A 与 B 之间的距离。
从表格中我们可以得知, len*cos
和 len*sin
正好与 dx
与 dy
同号,因此设置 -len*cos
和 -len*sin
为粒子粒子的移动速度,就能使粒子 A 不断靠近引力中心 B(len
为任意长度)
// 粒子越靠近引力中心,dist越小,一方面可以保证粒子速度越来越快,另一方面也达成len不断修改的要求
p.vx -= (20 / (p.r * dist)) * Math.cos(angle);
p.vy -= (20 / (p.r * dist)) * Math.sin(angle);
p.x += p.vx;
p.y += p.vy;
斥力效果
如果光有引力效果,耍一会大部分粒子都被吸引到中间,显得四周空空旷旷的,玩久了也有几分单调。那咱们就给添加个排斥效果,有吸力,有斥力,效果齐活了。
斥力效果的效果为点击鼠标,粒子沿圆形往四周扩散,为保证扩散效率,设置大扩散半径。
有了引力的实现基础,粒子向四周只需保证 len*cos
和 len*sin
正好与 dx
与 dy
同号即可。
p.vx += (250 / (p.r * dist)) * Math.cos(angle);
p.vy += (250 / (p.r * dist)) * Math.sin(angle);
粒子的斥力效果是不是很解压,但也能发现一个比较明显的问题,很多粒子被排斥到页面之外,页面中的粒子越来越少了,这可不好玩啊,因此添加对边界处理逻辑。
边界处理
边界处理逻辑不难实现,当粒子到达边界时,例如碰到垂直方向的上边界,那么意味着粒子的 vy
是负值,修改其为异号,那么粒子就可以反弹回来,其余方向处理类似。
function bounce(b) {
// 左边界
if (b.x < b.r) {
b.x = b.r;
b.vx *= -1;
}
// 右边界
if (b.x > canvas.width - b.r) {
b.x = canvas.width - b.r;
b.vx *= -1;
}
// 上边界
if (b.y - b.r < 0) {
b.y = b.r;
b.vy *= -1;
}
// 下边界
if (b.y > canvas.height - b.r) {
b.y = canvas.height - b.r;
b.vy *= -1;
}
}
源码仓库
源码地址: 粒子的万有引力与斥力
体验地址: 粒子的万有引力与斥力
如果感觉有帮助的话,别忘了给小包点个 ⭐ 。
往期精彩文章
- 牛客最新前端JS笔试百题
- 牛客最新前端面经面试题汇总(含解析)
- CSS实现一只自由飞翔的鸟儿🐦
- 抓取牛客最新前端面试题五百道 数据分析JS面试热点
- 给VSCode和网站领养喵咪 一起快乐撸猫
- 原生JavaScript灵魂拷问(一),你能答上多少?
- JavaScript之彻底理解原型与原型链
- JavaScript之彻底理解EventLoop
- 《2w字大章 38道面试题》彻底理清JS中this指向问题
后语
伙伴们,如果大家感觉本文对你有一些帮助,给阿包点一个赞👍或者关注➕都是对我最大的支持。
另外如果本文章有问题,或者对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!