animation-fill-mode: forwards 只在动画未播放时生效,需配合完整的100%关键帧样式,否则无法保留终态;常见失效原因包括关键帧缺失、样式覆盖、简写遗漏forwards及旧版浏览器兼容问题。

它不控制动画怎么动,只管动画“没在动”的时候元素该长什么样——比如刚点下按钮还没开始动、动完停着、或者被暂停时要不要留着最后一帧。
为什么写了 animation-fill-mode: forwards 还是闪回?
根本不是属性写错了,而是浏览器没拿到“可保留的终值”。常见原因有:
-
@keyframes里漏了100%或to块:只写了0%和50%,浏览器会用原始transform补全终态,forwards锁住的就是这个错的值 - 被更高优先级样式覆盖:
style="opacity: 0"、带!important的类、或后续 CSS 规则(如.disabled { opacity: 0; })都会直接压过forwards保持的值 -
animation-iteration-count没设为1:若用了infinite或配合animation-direction: alternate,动画结束时可能停在反向最后一帧(即0%),看起来像回退 -
animation简写里没显式含forwards:只写animation: fadeIn 0.3s ease;,forwards会被重置为none
forwards 必须和 100% 关键帧配合
forwards 不会补全、不继承、不推测——它只忠实地锁住你明确写在 100% 或 to 里的那几行样式。例如:
@keyframes slideIn {
from { transform: scale(0.8) rotate(-5deg); }
to { transform: translateX(100px); } /* ❌ 缺少 scale/rotate,结束时这些值变 none */
}
<p>/<em> ✅ 正确:所有想保留的 transform 属性都得写全 </em>/
@keyframes slideIn {
from { transform: scale(0.8) rotate(-5deg); }
to { transform: translateX(100px) scale(0.8) rotate(-5deg); }
}同理,filter、opacity、color 等只要你想保留,就必须在 100% 中显式写出。
立即学习“前端免费学习笔记(深入)”;
简写 vs 单独声明的兼容性陷阱
两种写法都可行,但顺序和覆盖关系极易出错:
- 简写必须包含
forwards:animation: slideIn 0.4s ease forwards;(注意分号前有空格) - 分开写时,
animation-fill-mode: forwards;必须和animation在同一选择器下,且后面不能出现animation-fill-mode: none;这类重置 - 旧版 WebView(如 Android 4.4 内置浏览器)不支持
forwards,需加-webkit-animation-fill-mode: forwards; - JS 动态触发动画时,如果先设
animation再设animation-fill-mode,后者可能被简写覆盖;推荐一起设置或用getComputedStyle验证是否生效
真正容易被忽略的是:animation-fill-mode 只在动画“不播放”时起作用——延迟中、结束后、暂停时。哪怕你写了 forwards,只要动画还在循环、或被 animation-play-state: running 中断,它就完全不生效。


















