黑色背景下一团粒子绕中心旋转、边转边往上飘,底部铺着一层透视网格地面。整体像一个发光的漩涡。
整个效果只有一个 HTML 文件,6.6 KB,没有引入 three.js,三维投影是手写的。
一、为什么能这么小
因为它只需要投影点,不需要模型、材质、光照。手写一个透视投影函数,比引入一个 600 KB 的库划算得多。
五个函数包完全部逻辑:
| 函数 | 作用 |
|---|---|
project3D() |
三维坐标转屏幕坐标,含相机旋转和裁剪 |
elevation() |
算仰角 |
rgbArray() / colorString() |
用正弦函数生成颜色 |
process() |
每帧更新所有粒子位置 |
drawFloor() |
画地面网格 |
二、参数全在这几行
在 frame() 函数的初始化段里:
vars.initParticles = 700; // 粒子总数
vars.initV = .01; // 初始上升速度
vars.distributionRadius = 800; // 扩散半径
vars.vortexHeight = 25; // 漩涡高度
vars.scale = 500; // 投影缩放
vars.floor = 26.5; // 地面高度
vars.camZ = -14; // 相机初始位置
调参就改这几个数:
| 想要 | 改哪个 |
|---|---|
| 粒子更多更密 | initParticles 调到 1500(注意性能) |
| 漩涡更高 | vortexHeight 调大 |
| 扩散更开 | distributionRadius 调大 |
| 整体缩放 | scale |
三、粒子是怎么循环的
每帧检查一次:飘太高或者转到圆心附近,就回收重生。
if(vars.points[i].y > vars.vortexHeight/2 || d < .25){
vars.points.splice(i, 1);
spawnParticle(vars);
}
spawnParticle() 在底部随机位置生成新粒子:
p = Math.PI * 2 * Math.random(); // 随机角度
ls = Math.sqrt(Math.random() * vars.distributionRadius); // 半径开根号,保证面上均匀
pt.y = -vars.vortexHeight / 2; // 从底部开始
pt.vy = vars.initV/20 + Math.random()*vars.initV; // 上升速度带随机
pt.radius = 200 + 800 * Math.random(); // 粒子大小
pt.color = pt.radius/1000 + vars.frameNo/250; // 颜色随大小和帧数变化
注意 Math.sqrt(Math.random())——直接 random() 会让粒子挤在圆心,开根号之后才在圆面上均匀分布。这是环形随机分布的标准写法。
四、拖影效果怎么来的
每帧不清屏,而是盖一层 15% 不透明的黑色:
vars.ctx.globalAlpha = .15;
vars.ctx.fillStyle = "#000";
vars.ctx.fillRect(0, 0, canvas.width, canvas.height);
旧画面只被擦掉一部分,所以粒子后面会拖出一条渐隐的尾巴。这是 canvas 做运动轨迹最省事的办法——比记录每帧位置再画线简单一百倍。
想要尾巴更长,把 .15 调小(比如 .08);想要干净利落,调大到 .5 以上。
五、颜色为什么在变
function rgb(col){
col += 0.000001;
var r = parseInt((0.5 + Math.sin(col) * 0.5) * 16);
var g = parseInt((0.5 + Math.cos(col) * 0.5) * 16);
var b = parseInt((0.5 - Math.sin(col) * 0.5) * 16);
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
}
三通道分别用 sin 和 cos 取值,因为相位不同,随着 col 增加就会走出彩虹色。而 col 里带 frameNo/250,所以同一颗粒子的颜色会随时间缓慢变化。
六、地面网格
drawFloor() 画的是 51×51 个点,每个点按 x² + z² 算距离,越远越暗(a = 0.15 - pow(d/50,4)*0.15),形成自然的衰减,看起来就有纵深感。
不是贴图,是一个个 fillRect 画出来的小方块。
七、深度排序
function sortFunction(a, b){
return b.dist - a.dist;
}
每帧结束时按距离从远到近排序,远的先画近的后画,这样近处的粒子会盖住远处的,立体感才对。
八、适合用在哪
| 场景 | 怎么调 |
|---|---|
| 登录页背景 | 直接把 canvas 设为 fixed 全屏,内容压在上面 |
| 大屏演示背景 | initParticles 调到 1500,scale 调大 |
| 深色主题头图 | 高度限制到 400px,改 canvas 尺寸 |
| 节日动效 | 把 rgb() 的系数换成固定配色 |
因为零依赖、6.6 KB,它比任何 three.js 方案都适合当「随手加个动效」的第一选择。
6.6 KB 做出这个效果,靠的是「只画点,不建模型」。想清楚自己到底需要什么,往往比引入一个库更重要。
1、本站所有资源均收集于互联网,分享目的仅限学习与参考。如有侵权,请联系 1685698671@qq.com 及时删除!
2、本站资源不保证完整性和安全性,下载后请自行检测。使用过程中出现的任何问题均与本站无关,敬请自行处理。
3、本站资源仅限个人学习研究使用,严禁用于任何违法违纪用途。因使用者违规使用产生的一切后果,由使用者自行承担,本站不承担任何责任。



评论(0)