🐲龙年临门,给大家画条龙

龙年即将到来,给大家画一条龙,提前祝读者祥龙翱翔,岁月腾飞。

本博文绘制的龙由粒子进行重组展示。使用到的素材图(网图,侵删)如下:

实现的效果如下:

在之前文章 引力粒子特效 - 归为尘埃中,我们实现的是粒子破碎的效果,而本文实现的是粒子重组,大同小异,异曲同工。

我们讲讲关键的两个点,吃透这两个点,已经成功了一大半:

  • 图转粒子
  • 粒子加速

图转粒子

首先,我们已经得到图片素材,如上。

接下来,我们需要的到图片的数据:

javascript 复制代码
// drawImage 将图片绘制在 canvas 上
context.drawImage(imgDom, startX, startY);
// getImageData 得到整个画布的数据
let imgData = context.getImageData(0, 0, canvasDom.width, canvasDom.height).data;

得到的数据比较特殊,我们可以简单理解,一个像素里面有四个 imgData 值,四个值分别代表 rgba,即 红,绿,蓝和透明度。拿画布的原点(左上角)第一个点来举例:

bash 复制代码
let red = imgData[0];
let green = imgData[1];
let blue = imgData[2];
let alpha = imgData[3];

这些颜色值都是需要赋值在粒子的颜色上的。

那么说到粒子,粒子需要具备的属性如下:

javascript 复制代码
// 10 为自定义的数值,可根据需要调整
{
  x: canvasDom.width*Math.random() * 10, // 随机 x 值
  y: canvasDom.height*Math.random() * 10, // 随机 y 值
  originX: originX, // 图片在 canvas 上的原始 x 值
  originY: originY, // 图片在 canvas 上的原始 y 值
  color: color, // 粒子的颜色,也就是我们上面提到的 rgba 颜色
  vx: 0, // 粒子在 x 轴方向移动的速度,初始值为 0
  vy: 0, // 粒子在 y 轴方向移动的速度,初始值为 0
  size: size * Math.random(), // 粒子的随机大小
}

嗯,收集完粒子之后,我们将其绘制在 canvas 上:

javascript 复制代码
// 给定粒子的颜色为画笔的颜色
context.fillStyle = paticle.color;
// 填充粒子(矩形)
context.fillRect(paticle.originX, paticle.originX, paticle.size, paticle.size);

效果如下:

咦,不错哦😯。这感觉,加点粒子闪烁,那不岂是变成星空🌃闪耀了。这个后面可以出篇文章跟进,本文就点到为止。

粒子加速

👌,粒子我们已经有了。我们需要为粒子添加特效了。

粒子动起来的加速度呢?

我们高中物理都学过万有引力的定律:

F = G m M r 2 F=G\frac{mM}{r^2} F=Gr2mM

  • F 是物体上的合力
  • m / M 是物体的质量
  • G 是引力常量
  • r 是两个物体的距离

然后,我们又知道牛顿的第二定律:

F = m . a F=m.a F=m.a

  • F 是引力
  • m 是物体的质量
  • a 是物体的加速度

结合上面的两条定律,那么,我们知道粒子的加速度公式如下:

a = G M r 2 a=G\frac{M}{r^2} a=Gr2M

因为这里,我们并没有考虑到物体的质量,所以 G*M 的值,我们设定为一个常量,即为 const STRENGTH = 3 * 1000 * 1000,当然,这个值读者可根据实际情况进行调整。

所以,我们可以得到每个粒子每个时刻的加速度,或者说 x 轴和 y 轴的每个时刻移动的距离:

javascript 复制代码
let dx = paticle.originX - paticle.x;
let dy = paticle.originY - paticle.y;
// 两个粒子之间的直线距离
let distance = Math.sqrt(dx * dx + dy * dy);
// 两个粒子线段与 x 轴的夹角
let angle = Math.atan2(dy, dx);
// 粒子的加速度
let acceleration = STRENGTH / (distance * distance);
// 粒子在 x 轴方向此时的速度
paticle.vx += acceleration * Math.cos(angle); // acceleration * Math.cos(angle) 表示在 x 轴方向的加速度
// 粒子在 y 轴方向此时的速度
paticle.vy += acceleration * Math.sin(angle); // acceleration * Math.sin(angle) 表示在 y 轴方向的加速度

上面👆我们得到了粒子当前速度,那么我们就计算粒子当前的距离了:

javascript 复制代码
paticle.x += paticle.vx;
paticle.y += paticle.vy;

当粒子靠近得足够的距离,我们将粒子放置在原来的坐标位置 (originX, originY):

javascript 复制代码
if(paticle.x > (paticle.originX - paticle.vx) && 
  paticle.x < (paticle.originX + paticle.x)  && 
  paticle.y > (paticle.originY - paticle.vy) &&
  paticle.y < (paticle.originY + paticle.vy)) {
  paticle.x = paticle.originX;
  paticle.y = paticle.originY;
}

👌,大功告成🎉🎉🎉

如果本文对你有帮助,不妨一键三连 「点赞👍,收藏🌟,分享⭕️」 🌹🌹🌹

相关推荐
李小轰_Rex12 小时前
零开销 AI 数字人方案|Live2D 数字人驱动方案分享
前端·aigc
ModyQyW12 小时前
vite-plugin-uni-manifest 0.7:在 uni-app 读取之前生成
前端·uni-app
我命由我1234512 小时前
Python 实用操作 - 文件操作(打开与关闭文件、读取文件、文件写入)
java·开发语言·前端·javascript·数据库·python·java-ee
修炼的dance12 小时前
批量转码前用 ffprobe 筛一遍,36 个文件能跳过 26 个
前端
费曼学习法12 小时前
从 0 到 1 上手 Super-Agent 架构:任务拆解、工具路由与失败回滚的三段实现
前端
2026小鱼AI12 小时前
当 LLM 生成的教学设计“不合格”:结构化校验、局部重生成与并发下的质量漂移
前端
疯华整猫12 小时前
借助 Trae 复刻百度网盘“无权限摇一摇”检测:Android 传感器 AI 辅助实战
前端
deli00712 小时前
流式输出为什么一段一段蹦?拆开 SSE 分块实测 20094 个乱码
前端
不一样的少年_12 小时前
为什么你写的 Agent 一报错就死循环?很多人都漏掉了这层“错误自救与熔断”机制
前端·后端·agent
857god12 小时前
做了个匿名聊天站,我把 7 个前端坑踩了一遍
前端