animation-fill-mode: forwards 必须加,否则动画结束会回退到初始状态;应使用 px/rem 单位而非 em/vh;动画延迟宜用 js 控制 class 添加时机;opacity+transform 性能好,但 will-change 需动态增删。

animation-fill-mode: forwards 必须加,否则动画一结束就回退
元素执行完 opacity 从 0 到 1、transform: translateY 从 20px 到 0 的动画后,若不锁定最终状态,会瞬间“啪”一下跳回初始位置和透明度——这是最常被忽略的坑。
原因在于 CSS 动画默认只影响播放期间,结束后浏览器按原始样式重绘。必须显式告诉它“停在最后一帧”:
-
animation-fill-mode: forwards是唯一可靠解法,both虽然也包含forwards,但没必要多一层语义 - 不能靠 JS 在动画结束时手动加 class 设
opacity: 1; transform: translateY(0),时机难控且冗余 - 如果同时用了
transition和animation,forwards仍优先于 transition 的起始态
@keyframes 里 translateY 要用 px/%/rem 等绝对单位,别用 em 或 vh 做入场偏移
用 em 写 transform: translateY(-1em) 看似灵活,但元素刚挂载时字体尚未完全计算(尤其动态插入或 SSR 场景),会导致上浮距离不准甚至为 0;vh 在滚动中触发动画时也会因视口高度抖动而偏移异常。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 统一用
px(如translateY(24px))或rem(需确保根字体大小稳定) - 若需响应式偏移,改用
%配合父容器padding-top或height控制,而非依赖视口或字体 - 避免在
@keyframes中写translateY(calc(...)),多数浏览器不支持 calc 在关键帧内运算
```css
@keyframes floatIn {
from {
opacity: 0;
transform: translateY(24px); /* ✅ 安全 */
}
to {
opacity: 1;
transform: translateY(0);
}
}
```animation-delay 不要设在触发元素上,改用 JS 控制 class 添加时机
想让多个元素依次淡入上浮?直接给每个元素设不同 animation-delay 行不通:DOM 插入顺序和渲染时机不可控,尤其 React/Vue 动态列表中,延迟值容易错位或失效。
更稳的做法是用 JS 批量控制:
- 初始状态统一加
class="fade-up",但不带animation属性 - 等元素真实挂载(
requestAnimationFrame或IntersectionObserver触发后)再批量加is-active类 - 动画定义在
.fade-up.is-active上,用animation-delay控制序列,此时 DOM 已就绪,延迟精准 - 避免用
setTimeout模拟延迟,易受主线程阻塞影响
opacity + transform 组合动画性能好,但别在 will-change 上乱加
opacity 和 transform 是仅有的两个能走合成层(compositor layer)的属性,天然适合流畅动画。但很多人一听说“性能优化”就给元素加 will-change: opacity, transform,结果反而拖慢首次渲染。
关键判断点:
- 只在动画即将开始前 1–2 帧加
will-change(JS 动态添加),动画结束立即移除 - 静态页面中长期挂着
will-change会持续占用 GPU 内存,尤其列表项多时明显卡顿 - 现代 Chrome/Firefox 对
opacity+transform自动启用合成优化,无需手动干预 - 如果动画卡顿,先查是否触发了 layout(比如动画中改了
height或margin),而不是急着加will-change
动画真正难的不是写几行 keyframes,而是让“第一帧不闪、最后一帧不跳、多个元素不错拍、滚动中不掉帧”。这些细节藏在 animation-fill-mode、单位选择、JS 介入时机和合成策略里,漏掉任意一个,用户看到的就是突兀的入场。









