烟花动画需用@keyframes分四阶段控制轨迹:爆发→扩散→回落→消散,配合随机类名分配不同动画、分段贝塞尔缓动、will-change优化及animation-play-state精准同步。

用 @keyframes 控制粒子从爆发到消散的完整轨迹
烟花动画本质是「中心爆发 → 径向扩散 → 减速上升/飘落 → 透明消失」四阶段。不能只靠 transform: scale() 或单纯 opacity 动画,必须用 @keyframes 精确控制每个粒子的 top/left 位移、opacity 和 transform: scale() 组合。关键点在于:扩散方向需随机(靠 JS 初始化时写入内联样式),但动画路径必须预定义——CSS 本身不支持运行时随机路径。
常见错误是把所有粒子共用一个 @keyframes,结果所有粒子走完全相同的抛物线,一眼假。正确做法是为每类轨迹(如“高抛型”“侧飘型”“直坠型”)定义 3–5 个不同 @keyframes,JS 创建粒子时随机分配类名。
- 典型爆发帧(0%):
opacity: 1; transform: scale(1); - 峰值帧(30%–50%):
opacity: 0.8; transform: scale(1.2) translate(±80px, -120px);(上抛+横向偏移) - 回落帧(70%):
opacity: 0.4; transform: scale(0.9) translate(±60px, 40px);(向下+继续飘) - 结束帧(100%):
opacity: 0; transform: scale(0.3);
用 animation-timing-function 模拟物理减速与重力感
线性缓动(linear)会让粒子像太空漂浮,毫无烟火的爆发张力和下坠重量。必须分段控制:爆发初期用 cubic-bezier(0.2, 0.8, 0.4, 1)(快出慢入),模拟火药推力;中段转 cubic-bezier(0.4, 0.2, 0.6, 1) 加入轻微下坠加速;末段用 ease-out 或自定义缓出贝塞尔让消散更自然。
注意:不要对整个动画用单一 animation-timing-function。CSS 不支持关键帧级缓动,所以得拆成多个 @keyframes 片段,或接受中段过渡略生硬——实际体验中,人眼更敏感于起始和结束的节奏,中段微瑕可接受。
立即学习“前端免费学习笔记(深入)”;
- 爆发段(0%→40%):用
cubic-bezier(0.1, 0.9, 0.3, 1.2)强化初速度 - 回落段(40%→80%):改用
cubic-bezier(0.3, 0.4, 0.7, 0.9)加入重力拖拽感 - 消散段(80%→100%):固定
ease-out,避免突兀消失
用 will-change: transform, opacity 防止粒子动画掉帧
当同时播放 50+ 粒子时,Chrome/Firefox 在低端设备上极易掉帧,尤其在 transform 和 opacity 同时变化时。根本原因不是计算量大,而是浏览器未提前触发 GPU 合成层。不加 will-change,粒子会频繁触发重排(reflow)和重绘(repaint)。
必须对每个粒子元素(通常是 <div class="particle">)设置 will-change: transform, opacity。但别滥用——仅在粒子创建后、动画开始前动态添加该样式;动画结束后立即移除,否则长期占用 GPU 内存。
- JS 中创建粒子后:
particle.style.willChange = 'transform, opacity'; - 监听
animationend事件后:particle.style.willChange = 'auto'; - 避免在 CSS 规则里全局写
.particle { will-change: transform, opacity; },这会让所有粒子常驻合成层
用 animation-play-state: paused 实现点击即爆的精准触发
网页烟花常需用户点击/悬停触发,但直接用 animation: explode 1.2s forwards 会导致多个粒子启动时间不同步——JS 创建顺序有微小延迟,视觉上变成“拉链式”绽开。解决方案是:所有粒子初始状态设为 animation-play-state: paused,统一用 JS 批量调用 el.style.animationPlayState = 'running'。
这个技巧能压到毫秒级同步,且兼容性好(IE10+ 支持)。比用 setTimeout 对齐时间点更可靠,也比 CSS animation-delay 调整更灵活——延迟值难精确匹配粒子生成时机。
- CSS 中预设:
.particle { animation: explode 1.2s forwards; animation-play-state: paused; } - JS 触发时:
particles.forEach(p => p.style.animationPlayState = 'running'); - 注意:若粒子需循环播放(如背景烟花),改用
animation-iteration-count: infinite+paused控制启停更稳妥
粒子数量、颜色渐变、模糊阴影这些视觉细节,都建立在轨迹逻辑和性能控制扎实的基础上。很多人卡在“看起来像烟花”,却没意识到掉帧和不同步会让效果瞬间廉价——先跑通 20 个粒子的精准同步与流畅回落,再叠加上色和光晕,才是正路。


















