animation-fill-mode: forwards 必须显式声明,否则粒子动画结束后会回退到初始状态;正确写法为 animation: explode 0.6s steps(1) forwards,且需配合 js 显式重置样式、清除旧动画并兜底删除。

animation-fill-mode: forwards 必须显式声明
不加 forwards,粒子动画播完就回退到初始位置,炸裂效果直接消失。这不是“没动”,而是浏览器按规范把 transform 和 opacity 恢复成动画前的值——哪怕你设了 opacity: 0 在终点,没 forwards 就不算数。
常见错误是只写 animation: explode 0.6s ease-out,结果粒子闪一下又弹回原点。正确写法必须带 forwards:animation: explode 0.6s steps(1) forwards。注意 steps(1) 和 forwards 是绑定使用的:前者控制帧跳变,后者锁定终态。
-
fill-mode不继承,每个粒子元素都得单独设 - 用 JS 动态插入粒子时,别依赖 CSS 类里的
animation简写——容易漏掉forwards,建议直接写el.style.animation = "explode 0.6s steps(1) forwards" - 如果粒子要复用、再触发一次炸裂,得先清除旧动画:
el.style.animation = "none"; el.offsetHeight; el.style.animation = "...";,否则forwards状态会卡住
opacity 动画终点必须为 0,且不能靠 visibility 或 display 隐藏
炸裂后粒子要彻底“消失”,只能靠 opacity: 0 配合 forwards。用 visibility: hidden 或 display: none 会中断动画流程,导致 forwards 失效;更糟的是,display: none 会让元素立刻脱离渲染树,后续 remove() 可能报错或残留。
真实场景中,常有人在 @keyframes explode 里写 0% { opacity: 1; } 100% { opacity: 0; transform: translate(120px, -80px); },这没问题;但若终点加了 visibility: hidden,浏览器可能在 opacity 到 0 的瞬间就隐藏元素,造成视觉断层。
- 所有透明度变化必须在
@keyframes内完成,不要在动画外补opacity: 0 - 别给粒子设
transition,它和animation冲突,尤其在快速连点时会导致 opacity 值错乱 - 移动端 Safari 对
opacity+transform的forwards支持稳定,但若同时设了will-change: opacity,iOS 16+ 可能出现图层残留,建议炸裂前再设、结束后立即清除
为什么不能用 animation-fill-mode: both 或 backwards
both 会让粒子在动画开始前就应用 0% 关键帧样式,导致点击前就提前偏移或变透明;backwards 更危险——它只作用于动画延迟阶段(animation-delay),而炸裂是即时触发,几乎无延迟,等于白设。
典型误用:为了“让粒子从点击点开始”,在 JS 创建粒子后立刻设 el.style.opacity = 1,再加 animation-fill-mode: backwards。结果是粒子还没开始动,opacity 已被强制设为 1,和后续动画冲突,最终表现为闪烁或不动。
- 炸裂类动画只用
forwards,其他值一律避免 - 初始状态(点击瞬间)必须由 JS 显式设置:
el.style.opacity = "1"; el.style.transform = "translate(0, 0)",确保起点干净 - 如果要用延迟(比如模拟“蓄力”感),用正的
animation-delay,而不是靠backwards拉回
高频点击时 opacity 状态容易错乱
用户连续点击,JS 可能还没删完上一批粒子,新粒子就已创建并启动动画。此时若新粒子的 opacity 被旧动画的 forwards 状态污染(比如上一个动画结束在 opacity: 0),新粒子一出来就是透明的,根本看不到炸裂。
根本解法不是加节流,而是每次创建前重置关键样式:el.style.opacity = "";、el.style.transform = "";、el.style.animation = "none"。再设动画时,浏览器才会从干净的初始值出发。
- 删除粒子别只靠
animationend事件——动画被暂停或跳帧时会漏掉;必须加setTimeout(() => el.remove(), 700)兜底(比动画时长多 100ms) - 粒子 DOM 节点复用比新建更快,但复用前必须清空所有内联样式,尤其是
opacity和transform - 用
getComputedStyle(el).opacity调试时注意:它返回字符串如"0",不是数字,别直接跟0比较
opacity 和 transform 状态是否被严格隔离——稍有复用或残留,炸裂就变成“哑炮”。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











