animation-fill-mode: forwards 的作用是让动画结束后保持最后一帧样式,但需配合完整的100%关键帧、避免样式覆盖、正确使用简写语法,并注意兼容性和js设置时机。

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
animation-fill-mode: forwards 必须和 100% 关键帧配合
forwards 不会补全、不继承、不推测——它只忠实地锁住你明确写在 100% 或 to 里的那几行样式。例如:
/* ❌ 错误:只改了 translateX,其他 transform 属性会归零 */
@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); }
}</p>
同理,filter、opacity、color 等只要你想保留,就必须在 100% 中显式写出。
animation 简写 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 确认终值已应用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











