必须显式设置animation-fill-mode: forwards,否则动画结束后元素会跳回初始状态;因其默认值为none,仅在播放中生效,结束后立即丢弃关键帧样式,需配合@keyframes中完整的100%定义才能保持末帧效果。

必须显式写 animation-fill-mode: forwards,否则动画一停就“弹回”初始状态——这不是 bug,是 CSS 规范默认行为。
为什么动画结束后元素会跳回原位
因为 animation-fill-mode 默认值是 none,动画只在播放过程中生效,结束后立刻丢弃所有关键帧样式。哪怕你在 @keyframes 的 100% 里写了 transform: translateX(200px),停住瞬间也会跳回去。
这不是 keyframes 没写对,而是缺少 forwards 声明。
-
none:动画前后都不保留任何关键帧样式(默认) -
forwards:动画结束后保持最后一帧定义的样式 -
backwards:仅在animation-delay阶段应用第一帧样式(比如延迟 1s,则这 1s 内就显示0%状态) -
both:forwards+backwards,但容易和初始 class 冲突,慎用
forwards 不生效的三个常见原因
写了 forwards 却还是闪回,大概率卡在这三处:
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
-
animation-duration是0s,或animation-iteration-count被设为0—— 动画根本没执行,forwards就无从谈起 - 关键帧里漏写了某个属性:比如动画只改了
transform,但没提opacity,那即使写了forwards,opacity也不会保持 - 被更高优先级样式覆盖:比如动画结束后,外部类又设了
opacity: 0,那forwards保持的opacity: 1就会被覆盖。用开发者工具看computed样式确认最终值
和 transition 混用时特别容易出问题
animation-fill-mode 只对 @keyframes 生效,对 transition 无效。两者混用极易冲突:
- 动画里设了
transform: scale(1.2)并加了forwards,但 JS 后续又给元素加了style.transform = "scale(0.8)"—— inline style 会赢 - 如果元素同时有
transition和动画,且动画终值与transition的起始值不一致,可能触发一次意外过渡,看起来像“没留住” - 想让 hover 后保持高亮?用
transition配合 class 切换更直接;想让复杂入场后固定位置?才用@keyframes+forwards
JS 控制动画时的坑
用 JS 动态启停动画、读取终值,forwards 的行为会影响结果:
-
getComputedStyle(el).opacity在动画刚结束时可能返回中间值,不是终值——渲染可能未完成,建议用requestAnimationFrame延迟读取 - 用
el.style.animation = 'none'强制终止动画,会立即丢弃forwards效果,元素回归内联样式或层叠值 - 若用 JS 设置
animation,记得把animation-fill-mode一起设上,否则简写会覆盖掉它
Safari ≤14.1 对 transform 多属性组合(如 scale + rotate)偶有渲染残留,此时可在末帧额外加一句 transform: translateZ(0) 强制重绘。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










