黑色背景下一团粒子绕中心旋转、边转边往上飘,底部铺着一层透视网格地面。整体像一个发光的漩涡。

整个效果只有一个 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、本站资源仅限个人学习研究使用,严禁用于任何违法违纪用途。因使用者违规使用产生的一切后果,由使用者自行承担,本站不承担任何责任。