纯css无法实现真实烟花爆炸效果,因其不支持随机方向、差异化初速度及动态衰减控制;必须用javascript创建粒子并设置自定义属性(如--tx、--ty)驱动transform和opacity动画,配合animationend及时销毁dom节点。

animation 无法直接控制粒子轨迹,得靠 JavaScript 配合
CSS animation 只能做预设路径的线性/缓动运动,烟花爆炸需要随机方向、不同初速度、带衰减的位移和透明度变化——纯 CSS 做不到动态生成和差异化控制。必须用 JS 创建粒子元素,再逐个设置 transform 和 opacity 的关键帧动画,或用 @keyframes 配合动态 class 注入。
常见错误是试图用一个 @keyframes 规则驱动所有粒子,结果全部朝同一方向飞、同时消失。正确做法是:每个粒子创建时,JS 随机生成 --tx、--ty、--dur、--delay 等 CSS 自定义属性,再用 animation 引用这些变量。
- 用
document.createElement('div')批量创建粒子 DOM 节点 - 给每个粒子设置
style.setProperty('--tx', `${Math.random() * 200 - 100}px`)等 - 动画中用
transform: translate(var(--tx), var(--ty))实现差异化位移 - 避免用
top/left,会触发重排,性能差
关键帧里必须用 transform + opacity,别碰 width/height
烟花粒子是瞬间出现、快速扩散、渐隐,动画周期短(通常 300–800ms),任何触发布局计算的属性都会卡顿。实测在 60fps 下,只要动画中包含 width、height、border 或 box-shadow 的变化,中低端设备就会掉帧。
正确写法只依赖合成层属性:transform(平移+缩放)和 opacity。例如:
@keyframes explode {
0% {
opacity: 1;
transform: translate(0, 0) scale(1);
}
100% {
opacity: 0;
transform: translate(var(--tx), var(--ty)) scale(0.2);
}
}
-
scale(0.2)模拟粒子飞远变小的效果,比单纯缩小width更高效 - 不要在
@keyframes里写background-color渐变,颜色变化不影响性能,但没必要——粒子通常用单色border-radius: 50%模拟光点 - 动画时长用
var(--dur, 500ms),让 JS 控制每个粒子持续时间
粒子销毁时机不对会导致内存泄漏
动画播完后,DOM 节点还在但不可见,大量粒子堆积会吃光内存。不能只靠 opacity: 0 隐藏,必须显式移除节点。
可靠做法是监听 animationend 事件,且加防抖判断(防止重复触发):
particle.addEventListener('animationend', () => {
particle.remove();
}, { once: true });
- 务必加
{ once: true },否则多次绑定会漏删 - 别用
setTimeout延迟删除,动画实际时长受ease-out影响,不一定等于duration - 如果用了
animation-fill-mode: forwards,粒子最终状态仍保留样式,但 DOM 已删,无影响 - 移动端要注意
animationend的兼容写法:webkitAnimationEnd在旧 iOS 上仍需保留
真要“零 JS”?只能做简化版静态爆炸效果
如果硬要纯 CSS,唯一可行的是固定数量、固定轨迹的模拟:比如用 8 个 div,分别设 transform: translate(100px, 0)、translate(70.7px, 70.7px) 等八个方向,再统一加 animation。但这不是爆炸,是八爪鱼伸展。
这种方案的问题很实在:
- 粒子数写死,无法响应点击位置
- 所有粒子动画参数相同,缺乏真实感
- 无法控制爆炸半径随距离衰减
- IE 和旧安卓 WebView 不支持
@keyframes中的transform复合值(如translate(100px, 50px) scale(0.5))
真正可用的烟花效果,JS 创建粒子这一步绕不开。CSS 负责动效,JS 负责逻辑和生命周期——这个分工边界,比怎么写 keyframes 更关键。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











