animation-fill-mode: forwards 是“挂载终值”,仅在动画自然结束时将 100%/to 关键帧中明确声明的样式写入已计算样式树,缺一不可:动画须执行完、无更高优先级覆盖、不与 transition 冲突。

animation-fill-mode: forwards 不是“记住状态”,而是“挂载终值”
它不缓存动画过程,也不推测意图,只在动画自然结束的那一刻,把 100% 或 to 关键帧里**明确声明的每一条样式声明**,原封不动地写进元素的“已计算样式树”中。浏览器后续渲染时,就当这些样式是开发者直接写的内联样式一样对待——直到被其他更高优先级规则覆盖。
常见错误现象:写了 animation-fill-mode: forwards,但 transform 回退了、opacity 又变回 1、filter 消失了。根本原因不是属性失效,而是关键帧里压根没写全:
-
@keyframes slideIn { from { transform: scale(0.9); } to { transform: translateX(200px); } }→ 缺少scale(0.9),结束时transform被重置为none,forwards 锁住的就是这个空值 - 想保持
opacity: 0,但关键帧只改了transform,那opacity就不会被 forwards 保留 - 用了
animation-direction: alternate且循环多次,forwards停在的是最后一次正向播放的末帧(即偶数轮的100%),不是所有迭代的“最终视觉结果”
为什么写了 forwards 还是闪回?先看这三处
它生效有硬性前提,缺一不可:
- 动画必须真正执行完:
animation-duration不能为0s;animation-iteration-count不能是0或infinite;也不能被animation-play-state: paused卡在中间 - 没有更高优先级样式覆盖:比如动画设了
opacity: 0并forwards,但后面有.box { opacity: 1 !important; },computed 样式里赢的永远是后者 - 没和
transition冲突:同一属性(如transform)同时被transition和animation控制,动画一停,transition可能立刻对“新旧值差”补过渡,造成视觉上“没冻住”
JS 动态设置动画时,forwards 极易被悄悄覆盖
CSS 类里写了 animation-fill-mode: forwards 没用——只要 JS 用 el.style.animation = 'slide 0.3s ease' 赋值,整个 animation 属性就被重写,animation-fill-mode 自动回落到默认 none。
立即学习“前端免费学习笔记(深入)”;
正确做法只有两种:
- 在 JS 中显式带上:
el.style.animation = 'slide 0.3s ease forwards'; - 或拆开写:
el.style.animationName = 'slide'; el.style.animationDuration = '0.3s'; el.style.animationTimingFunction = 'ease'; el.style.animationFillMode = 'forwards';
混用最危险:CSS 类定义了 forwards,JS 又用简写覆盖,等于白配。
连续动画衔接时,forwards 是“单程票”,不是“自动续订”
想让滑入 → 缩放 → 变色三段动画无缝衔接,每一段都得独立配 forwards,且后一段的 0% 必须和前一段的 100% 完全一致:
- 单位要统一:
translateX(100px)≠translateX(100) - 函数写法要一致:
scale(1.2)≠transform: scale(1.2)(后者会覆盖整个 transform 值) - 漏掉任意一个属性,比如第二段
0%没写opacity,那它就会从原始值开始,而不是从前一段终值继承
最容易被忽略的是:forwards 对 infinite 动画完全无效——因为没有“结束”,它就不触发。需要循环+保持终态,得换思路,比如用 JS 监听 animationiteration 事件手动打点。


















