animation-fill-mode: forwards 必须配合完整 keyframes(含100%/to)、非无限循环、无样式覆盖及正确简写写法才能生效,否则终值无法保留。

加 animation-fill-mode: forwards 是必须的,但只写这一行大概率没用——问题几乎都出在关键帧定义、样式覆盖或触发时机上。
为什么写了 animation-fill-mode: forwards 还是闪回
根本不是属性写错了,而是浏览器没拿到“可保留的终值”。常见情况包括:
-
@keyframes里漏了100%或to块:比如只写了0%和50%,浏览器会用元素原始transform补全终态,forwards锁住的就是这个错误值 - 动画根本没“结束”:比如
animation-iteration-count: infinite,或被animation-play-state: paused拦住,forwards不触发 - 内联样式(如
style="opacity: 0")或高优先级类(如.disabled { opacity: 0; })直接覆盖了forwards保持的值 - 用了
animation-direction: alternate却没配animation-iteration-count: 1,动画播完停在反向最后一帧(即起始帧),看起来像“回去了”
animation 简写里必须显式包含 forwards
animation 简写会重置所有未显式写出的子属性,包括把 animation-fill-mode 强制设回 none。很多人只写 animation: fadeIn 0.3s ease;,以为单独声明 animation-fill-mode: forwards; 能补上,实际无效。
正确写法只有两种:
立即学习“前端免费学习笔记(深入)”;
- 直接写进简写:
animation: fadeIn 0.3s ease forwards;(注意分号前有空格) - 分开写,且确保
animation-fill-mode: forwards;和animation在同一选择器下,且后面没出现animation-fill-mode: none;之类重置
@keyframes 必须显式写全 100% 的所有目标属性
forwards 不会“补全”未声明的属性,也不会继承原始 transform 值。例如:
- 元素原本有
transform: scale(0.9) rotate(2deg),动画只写了to { transform: translateX(100px); },那结束时scale和rotate就归none - 正确写法是:
to { transform: translateX(100px) scale(0.9) rotate(2deg); } -
opacity、filter、color等同理,只要你想保留,就必须在100%或to中显式写出
JS 触发动画时最容易忽略的三个坑
用 JS 动态添加 class 控制动画时,forwards 生效的前提是动画真正走到了终点:
- 刚加
class就立刻remove,动画根本没开始播,forwards永远没机会执行 - 在
animationend事件里读取getComputedStyle(el).transform,它几乎总是返回初始值(比如none),不是终态 —— 别依赖它判断是否到位 - 重复快速添加/移除同一
class,可能触发浏览器跳过动画,直接应用初始样式
真正麻烦的不是加不加 forwards,而是它只管动画层,不管布局层;如果动画结束时父容器尺寸变化或媒体查询切换,已“定格”的样式可能因重排而失效。


















