烟雾背景特效(Canvas 烟雾粒子动画,浓度速度可调)
烟雾背景特效:柔光烟雾粒子从底部缓缓升起,边升边摇曳边淡出,烟雾浓度、上升速度与不透明度实时可调,Canvas 自研并适配高分屏,复制即用。
效果演示(真实可交互)
烟雾粒子从画布底部持续生成,以随机速度向上漂浮,过程中按正弦轨迹左右摇曳,并经历”淡入-淡出”的完整生命周期,最后在顶部消散,像一缕缕青烟在深色背景上袅袅升起。三条滑杆分别控制烟雾浓度(每秒生成 1-20 个粒子)、上升速度(1-10)与不透明度(10%-90%)。关键细节是粒子颜色用径向渐变从中心到边缘透明,且透明度按 sin(π·life/maxLife) 变化——这是烟雾”由薄变浓再变淡”的自然感来源。每个粒子只保存位置、半径、相位等少量状态,生命周期结束后即被移除,内存占用恒定。
完整代码
<canvas id="smoke" style="width:100%;height:340px;display:block;background:linear-gradient(180deg,#131a2b,#0a0d18);"></canvas>
const canvas = document.getElementById('smoke');
const ctx = canvas.getContext('2d');
let W, H, parts = [], acc = 0;
const cfg = { density: 8, speed: 3, alpha: 45 };
function resize() {
const r = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
canvas.width = Math.round(r.width * dpr);
canvas.height = Math.round(r.height * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
W = r.width; H = r.height;
}
function spawn() {
parts.push({
x: Math.random() * W, y: H + 30,
r: 24 + Math.random() * 42, // 烟团半径
speed: (Math.random() * .5 + .5) * cfg.speed,
phase: Math.random() * Math.PI * 2, // 摇曳相位
sway: 14 + Math.random() * 26, // 摇曳幅度
life: 0, maxLife: 220 + Math.random() * 140,
});
}
function step() {
ctx.clearRect(0, 0, W, H);
acc += cfg.density / 60; // 按帧累加生成
while (acc >= 1) { spawn(); acc -= 1; }
for (let i = parts.length - 1; i >= 0; i--) {
const p = parts[i];
p.life++;
if (p.life > p.maxLife || p.y < -p.r * 2) { parts.splice(i, 1); continue; }
p.y -= p.speed;
p.x += Math.sin(p.phase + p.y * .01) * .5; // 摇曳
const fade = Math.sin((p.life / p.maxLife) * Math.PI); // 淡入淡出
const alpha = fade * (cfg.alpha / 100) * .5;
const g = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
g.addColorStop(0, `rgba(214, 222, 238, ${alpha})`);
g.addColorStop(1, 'rgba(214, 222, 238, 0)');
ctx.fillStyle = g;
ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fill();
}
requestAnimationFrame(step);
}
resize(); step();
window.addEventListener('resize', resize);
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
| 烟雾浓度 | cfg.density | 每秒生成粒子数 1-20 |
| 上升速度 | cfg.speed | 1-10,同时参与每粒子随机速度 |
| 不透明度 | cfg.alpha | 10-90,整体烟雾明暗 |
| 烟团半径 | 24 + Math.random() * 42 | 越大烟越”团”,越小越”丝” |
| 摇曳幅度 | p.sway | 14-40,越大飘得越散 |
| 生命周期 | p.maxLife | 220-360 帧,越长烟升得越高 |
兼容性与性能
- 基础 Canvas 2D API,兼容所有现代浏览器;已适配
devicePixelRatio,高分屏下烟团边缘柔和无锯齿。 - 粒子是”即生即灭”模式:生成时 push、消亡时 splice,存量约等于 density × 寿命/60,浓度 8 时峰值约 40 个粒子,开销可忽略。
- 每粒子每帧创建一个径向渐变,若追求极致性能,可改用预渲染的烟雾 sprite 贴图(
drawImage)替代渐变填充。 - 透明烟团叠加时会产生自然的”堆叠变浓”效果,这正是烟雾的视觉特性,无需额外混合模式。
- 移动端建议浓度 ≤ 12;页面不可见时应暂停 rAF,避免后台耗电。
常见问题
Q: 为什么烟雾看起来像一个个圆圈而不是一片烟? 粒子密度不够或半径太小。提高浓度(10-15)、把半径下限调大(如 40),粒子之间相互重叠后就会连成连续的烟雾层。
Q: 能让烟雾颜色更”白”或更”彩”吗? 可以,把 rgba(214, 222, 238, ...) 换成任意颜色即可;暖色(如橙灰)适合烛火氛围,冷色适合科技感场景。
Q: 烟雾有时会突然消失是怎么回事? 每个粒子有 maxLife 生命周期,到期即被移除。若想让烟雾升得更高,加大 maxLife 或降低 p.speed,就能看到粒子飘到画面顶部。
Q: 可以和鼠标交互吗? 可以。在 mousemove 事件里对粒子施加斥力(参考本站萤火虫组件的驱散写法),就能做出”拨开烟雾”的效果。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。