萤火虫背景特效(Canvas 萤火虫动画,鼠标驱散互动)
萤火虫背景特效:发光的萤火虫在深色夜空随机游走、呼吸闪烁,鼠标划过会被驱散,数量、飞行速度、光晕大小实时可调,Canvas 自研适配高分屏。
效果演示(真实可交互)
一群萤火虫在深蓝夜空里缓缓游荡,每只都带有独立的呼吸式闪烁节奏:亮度按正弦曲线在 30%-95% 之间起伏,光晕半径也随之微微伸缩,营造出忽明忽暗的生命感。把鼠标移进画面,附近的萤火虫会被一股”斥力”推开,划过一片便散开一片,离开画布后它们又慢慢聚回。演示面板可以调整萤火虫数量(10-80)、飞行速度(1-10)与光晕大小(8-30px)。运动模型采用”随机游走 + 限速”:每帧给速度叠加一个微小的随机扰动再钳制到上限,让轨迹平滑又不可预测,比直线往返自然得多。
完整代码
<canvas id="firefly" style="width:100%;height:340px;display:block;background:radial-gradient(120% 120% at 50% 0%,#0d1b2e,#04070d);"></canvas>
const canvas = document.getElementById('firefly');
const ctx = canvas.getContext('2d');
let W, H, flies = [];
const cfg = { count: 35, speed: 3, glow: 16 };
const mouse = { x: -9999, y: -9999 };
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() {
return {
x: Math.random() * W, y: Math.random() * H,
vx: (Math.random() - .5) * cfg.speed, vy: (Math.random() - .5) * cfg.speed,
phase: Math.random() * Math.PI * 2, tw: Math.random() * Math.PI * 2,
};
}
function init() { flies = []; for (let i = 0; i < cfg.count; i++) flies.push(spawn()); }
function step() {
ctx.clearRect(0, 0, W, H);
for (const f of flies) {
// 随机游走 + 限速
f.phase += .01;
f.vx += (Math.random() - .5) * .2;
f.vy += (Math.random() - .5) * .2;
const sp = Math.hypot(f.vx, f.vy) || 1;
if (sp > cfg.speed) { f.vx = f.vx / sp * cfg.speed; f.vy = f.vy / sp * cfg.speed; }
// 鼠标驱赶:120px 内施加斥力
const dx = f.x - mouse.x, dy = f.y - mouse.y;
const dm = Math.hypot(dx, dy);
if (dm < 120 && dm > .01) {
const force = (120 - dm) / 120 * 1.6;
f.vx += dx / dm * force; f.vy += dy / dm * force;
}
f.x += f.vx; f.y += f.vy;
if (f.x < 0 || f.x > W) f.vx *= -1;
if (f.y < 0 || f.y > H) f.vy *= -1;
// 呼吸闪烁 + 径向渐变光晕
f.tw += .04;
const alpha = .3 + .65 * (Math.sin(f.tw) * .5 + .5);
const r = cfg.glow * (.6 + .8 * Math.sin(f.tw));
const g = ctx.createRadialGradient(f.x, f.y, 0, f.x, f.y, r);
g.addColorStop(0, `rgba(255,236,130,${alpha})`);
g.addColorStop(.35, `rgba(255,210,90,${alpha * .45})`);
g.addColorStop(1, 'rgba(255,210,90,0)');
ctx.fillStyle = g;
ctx.beginPath(); ctx.arc(f.x, f.y, r, 0, Math.PI * 2); ctx.fill();
}
requestAnimationFrame(step);
}
resize(); init(); step();
window.addEventListener('resize', () => { resize(); init(); });
canvas.addEventListener('mousemove', e => {
const r = canvas.getBoundingClientRect();
mouse.x = e.clientX - r.left; mouse.y = e.clientY - r.top;
});
canvas.addEventListener('mouseleave', () => { mouse.x = -9999; mouse.y = -9999; });
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
| 萤火虫数量 | cfg.count | 10-80,越多画面越热闹,性能开销线性增长 |
| 飞行速度 | cfg.speed | 1-10,同时作为速度上限 |
| 光晕大小 | cfg.glow | 8-30px,实际光晕在其 60%-140% 间呼吸 |
| 闪烁频率 | f.tw += .04 | 调大闪得更快 |
| 驱散半径 | dm < 120 | 鼠标影响范围,可调大增强互动感 |
| 光色 | 渐变三档颜色 | 可整体换成冷色调(青蓝)或暖色调 |
兼容性与性能
- 已做
devicePixelRatio适配,高分屏光晕边缘平滑,无发虚问题。 - 每帧每只萤火虫只需一次
createRadialGradient+ 一次 fill,80 只约 80 次渐变创建,现代浏览器压力很小。 - 若追求极致性能,可把渐变预渲染成离屏 sprite(Canvas 快照),再
drawImage复用,开销再降一个量级。 - 鼠标事件只在 canvas 范围内触发,不影响页面其余交互;移动端无鼠标时自动退化为纯游走模式。
- 建议配合
prefers-reduced-motion关闭闪烁或整体降速,照顾光敏用户。
常见问题
Q: 为什么萤火虫经常扎堆在角落? 边界用的是速度反向(反弹),如果随机游走扰动太大,粒子会贴着边界滑动。可以把反弹改为”推回中心方向”,或在边界处增加随机逃逸速度。
Q: 光晕太亮/太暗怎么调? 亮度由 alpha 公式 0.3 + 0.65*(sin+1)/2 决定,把 0.65 换成 0.4 会更内敛;光晕半径则由 cfg.glow 控制。
Q: 想加入点击”吸引”效果? 把驱散逻辑的斥力方向取反即可变成吸引力:f.vx -= dx / dm * force,就能做出萤火虫飞向鼠标的效果。
Q: 性能比粒子系统差吗? 不会。这里每只萤火虫只有位置、速度、相位三个状态,无碰撞计算,O(n) 复杂度,比带连线计算的粒子系统便宜得多。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布: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(缓动曲线)等属性即可,代码中有注释说明。