css 动画与过渡的区别_通过 animation 实现复杂效果

animation 与 transition 本质不同:transition 是状态间补间,仅支持两态切换;animation 是时间轴驱动序列,支持多关键帧、循环、方向控制等。必须用 animation 的场景包括:多属性异步变化、中间停顿、非线性路径、反向循环、独立延迟启动。@keyframes 不支持 auto/inherit/calc() 等动态值,且 timing-function 无法逐帧设置,复杂效果需组合多个 animation 实现。

animation 和 transition 本质不是替代关系

它们解决的问题层级不同:transition 是状态切换的“补间”,只在属性值变化时触发,且只能定义起始、结束两个状态;animation 是时间轴驱动的“序列”,通过 @keyframes 显式控制每一帧,支持多关键帧、循环、方向控制等。

什么时候必须用 animation 而不能靠 transition

当你需要以下任一能力时,transition 就无能为力:

  • 同一动画中改变多个属性,且变化节奏不一致(比如 opacity 淡入快、transform 平移慢)
  • 中间停顿、回弹、跳跃(例如:0% → 50% → 100

    %,其中 50% 处停留 0.3s)
  • 非线性路径运动(如贝塞尔曲线轨迹、绕圆旋转 + 缩放)
  • 循环次数大于 1 且需反向播放(animation-iteration-count: 3; animation-direction: alternate;
  • 动画开始前有延迟,但延迟后不希望立即执行第一帧(animation-delay 可独立于触发时机)

@keyframes 里不能写 auto、inherit 或 calc() 动态值

@keyframes 中所有属性值必须是可计算的静态值。常见踩坑点:

  • left: auto 无效 —— 改用具体像素或百分比(如 left: 0left: 100%
  • width: inherit 不被解析 —— 需提前知道目标宽度,或改用 JS 配合 getComputedStyle 注入变量
  • transform: translateX(calc(100vw - 100px)) 在 keyframes 中不生效 —— calc() 只在声明块顶层有效,keyframes 内需预计算为固定值
  • 百分比基准是元素自身尺寸,不是父容器 —— 如 top: 50% 是相对于自身 height 的 50%,不是父元素

animation-timing-function 对整个序列生效,无法逐帧设置

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 属性自动处理。