超解压,实现粒子的万有引力与斥力效果

简介: 超解压,实现粒子的万有引力与斥力效果

前言


前面小包实现一个使用粒子构成 “战场小包” 文字炫酷效果,给小包打开粒子世界的大门,粒子世界真的是炫酷,简单的组合变换可以产生超多炫彩效果,今天小包带大家实现粒子的吸力与斥力效果,可以试玩一下,超级解压和快乐。


体验地址: 粒子万有引力与斥力


实现粒子


首先来实现全屏随机粒子分布,本文章共生成 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());
    }
}
复制代码


image.png

满屏幕的粒子生成之后,接下来就来做粒子的效果吧。


万有引力效果


要实现万有引力效果,首先需要一个引力中心,初始位置为中心位置,当鼠标开始移动时,鼠标位置为引力中心。为了看起来更明显,通过 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 属性使粒子不断接近引力中心。


在实现上述效果之前,先来复习一下三角函数的知识:


image.png

从上面图中可以得出,r*cos, r*sinr 分别在 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 轴正方向向下,各个象限的余弦与正弦大小如下:


image.png

复习完上面知识后,接下来就可以计算粒子移动方式了。(粒子越靠近引力中心速度越快)

在坐标系中取三个粒子,我们来实际演练一下:


image.png

粒子 cos/sin dx/dy len*cos/len*sin
A 正/负 正/负 正/负
C 负/负 负/负 负/负
D 正/正 正/正 正/正


dxdy 分别为粒子距离引力中心的长度在 x,y 轴上的投影,存在正负值,例如点 A ,dxA > 0, dyA < 0 ,因此如果粒子 A 想靠近引力中心 B ,需设置 vx < 0, vy > 0 ,来减少 A 与 B 之间的距离。


从表格中我们可以得知, len*coslen*sin 正好与 dxdy 同号,因此设置 -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;
复制代码


image.png

斥力效果


如果光有引力效果,耍一会大部分粒子都被吸引到中间,显得四周空空旷旷的,玩久了也有几分单调。那咱们就给添加个排斥效果,有吸力,有斥力,效果齐活了。


斥力效果的效果为点击鼠标,粒子沿圆形往四周扩散,为保证扩散效率,设置大扩散半径


有了引力的实现基础,粒子向四周只需保证 len*coslen*sin 正好与 dxdy 同号即可。


p.vx += (250 / (p.r * dist)) * Math.cos(angle);
p.vy += (250 / (p.r * dist)) * Math.sin(angle);
复制代码


image.png


粒子的斥力效果是不是很解压,但也能发现一个比较明显的问题,很多粒子被排斥到页面之外,页面中的粒子越来越少了,这可不好玩啊,因此添加对边界处理逻辑。


边界处理


边界处理逻辑不难实现,当粒子到达边界时,例如碰到垂直方向的上边界,那么意味着粒子的 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;
  }
}
复制代码

image.png

源码仓库


源码地址: 粒子的万有引力与斥力


体验地址: 粒子的万有引力与斥力


如果感觉有帮助的话,别忘了给小包点个 ⭐ 。




相关文章
|
C++ Python
哈夫曼编码实现文件的压缩和解压
哈夫曼编码实现文件的压缩和解压
140 0
|
1月前
|
存储 C#
C#使用哈夫曼编码实现压缩与解压
C#使用哈夫曼编码实现压缩与解压
23 0
|
3月前
|
移动开发
【光波电子学】MATLAB绘制子午光线路径方程曲线
本文介绍了如何使用MATLAB绘制基于特定折射率分布的第一种子午光线路径方程曲线的方法。
43 3
|
6月前
|
存储 编解码 API
基于GDAL命令行对遥感影像加以投影的方法
【2月更文挑战第3天】本文介绍基于gdal模块,在命令行中通过GDAL命令的方式(不是Python或者C++代码,就是gdal模块自身提供的命令行工具),对栅格遥感影像数据加以投影,即将原本的地理坐标系转为投影坐标系的方法~
206 5
基于GDAL命令行对遥感影像加以投影的方法
|
索引
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子(下)
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子
180 0
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子(下)
|
6月前
|
存储 传感器 算法
相机标定系列---opencv相关标定算子
相机标定系列---opencv相关标定算子
133 0
|
6月前
植被冠层参数计算软件CAN-EYE的下载与安装方法
植被冠层参数计算软件CAN-EYE的下载与安装方法
|
传感器 编解码
在gazebo中进行相机标定
在gazebo中进行相机标定
在gazebo中进行相机标定
|
索引
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子(上)
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子
226 0
Unreal Niagara粒子入门3 - 根据模型顶点发射粒子(上)
CAN-EYE植被参数计算软件的下载与安装
本文介绍植被指数计算软件CAN-EYE的下载、安装方法~
187 1
CAN-EYE植被参数计算软件的下载与安装