animation 与 transition 本质不同:transition 是状态间补间,仅支持两态切换;animation 是时间轴驱动序列,支持多关键帧、循环、方向控制等。必须用 animation 的场景包括:多属性异步变化、中间停顿、非线性路径、反向循环、独立延迟启动。@keyframes 不支持 auto/inherit/calc() 等动态值,且 timing-function 无法逐帧设置,复杂效果需组合多个 animation 实现。
它们解决的问题层级不同:transition 是状态切换的“补间”,只在属性值变化时触发,且只能定义起始、结束两个状态;animation 是时间轴驱动的“序列”,通过 @keyframes 显式控制每一帧,支持多关键帧、循环、方向控制等。
当你需要以下任一能力时,transition 就无能为力:
animation-iteration-count: 3; animation-direction: alternate;)animation-delay 可独立于触发时机)@keyframes 中所有属性值必须是可计算的静态值。常见踩坑点:
left: auto 无效 —— 改用具体像素或百分比(如 left: 0 或 left: 100%)width: inherit 不被解析 —— 需提前知道目标宽度,或改用 JS 配合 getComputedStyle 注入变量transform: translateX(calc(100vw - 100px)) 在 keyframes 中不生效 —— calc() 只在声明块顶层有效,keyframes 内需预计算为固定值top: 50% 是相对于自身 height 的 50%,不是父元素animation-timing-function(如 cubic-bezier(.2, .8, .4, 1))作用于整个动画周期,从 0% 到 100% 的整体插值方式。它不能像 SVG 的 animate 那样给每一段 keyframe 单独设缓动。
如果需要分段不同缓动,只能拆成多个 animation:
@keyframes slide-in {
0% { transform: translateX(-100%); }
100% { transform: translateX(0); }
}
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0); }
}
.element {
animation: slide-in 0.6s cubic-bezier(.2, .8, .4, 1),
bounce 0.4s 0.6s ease-out;
}注意第二段动画的 0.6s 延迟,就是靠这个实现“滑入完成后再弹跳”。这种组合才是真实项目里写复杂效果的常规做法。
真正难的不是写多少帧,而是理清各段动画的时间对齐点、是否依赖 DOM 尺寸、是否要响应用户交互中断 —— 这些没法靠 animation 属性自动处理。