animation-fill-mode: forwards 能让动画停在终态,但需满足三个前提:关键帧必须显式定义100%(或to)且写全transform等所有属性;动画必须真正结束(非infinite或paused);不能被更高优先级样式覆盖。

动画结束后跳回初始状态不是 bug,是默认行为;加 animation-fill-mode: forwards 就能停住,但必须满足三个前提:关键帧写全、动画真正结束、没被更高优先级样式覆盖。
为什么写了 forwards 还是闪回?
写了 animation-fill-mode: forwards 却仍跳回,基本卡在这三处:
-
@keyframes里没显式写100%(或to),比如只定义了0%和50%,浏览器会用元素原始transform值“补”终态,forwards锁住的就是这个错误值 - 动画根本没“结束”:比如
animation-iteration-count是infinite,或被animation-play-state: paused暂停在中间,forwards不触发 - 有更高优先级样式覆盖:例如动画后另一个 class 又设了
opacity: 0,或内联样式style="transform: none",直接压过forwards的效果
animation-fill-mode: forwards 必须和 animation 同级声明
它不是独立开关,只对正在运行的动画生效。常见错误写法:
.box {
animation: slideIn 0.4s;
}
.box.active {
animation-fill-mode: forwards; /* ❌ 无效:不在同一规则下 */
}
正确做法是合并进简写,或确保同选择器:
.box.active {
animation: slideIn 0.4s ease forwards; /* ✅ 推荐 */
/* 或分开写但同级 */
animation-name: slideIn;
animation-duration: 0.4s;
animation-fill-mode: forwards;
}
关键帧里 transform 必须写全,不能靠“继承”
transform 是复合属性,CSS 动画不会叠加或延续未声明的部分。比如元素原本有 transform: scale(0.9) rotate(5deg),动画只写 100% { transform: translateX(100px); },结束时 scale 和 rotate 就变成 none,视觉上就是“缩回去”“转正”。
解决办法只有:
- 在
100%中一次性写出所有要保留的变换:transform: translateX(100px) scale(0.9) rotate(5deg); - 如果原始
transform来自 JS 或动态计算,改用left/top+position: relative,或动画结束后用transitionend事件读取getComputedStyle并固化到style
移动端特别注意 100% 而不是 to,以及 JS 设置时机
部分 Android WebView 对 to 解析不稳定,显式写 100% 更稳妥。JS 动态添加动画时,别只设 element.style.animation,必须同步设 element.style.animationFillMode = 'forwards',否则内联样式会丢掉 CSS 文件里的 fill-mode。
最易被忽略的一点:forwards 只锁住动画结束那一刻的计算值。如果父容器尺寸变化、媒体查询触发重排,或视口滚动导致 transform 重新计算,那个“保持住的状态”依然会变——这时候问题已不在 animation-fill-mode,而在响应式上下文本身。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











