transition、animation

在 CSS 中,实现动画效果主要有两种方式:transition(过渡)和 animation(关键帧动画)。掌握它们的区别、用法以及性能优化是前端面试的常见考点。

transition-过渡动画

transition 用于在 CSS 属性值发生变化时(如 hover、class 变化),提供平滑的过渡效果。它是一种隐式过渡,需要在两个状态之间切换。

语法

/* transition: property duration timing-function delay; */
transition: width 0.3s ease-in-out 0s;
  • transition-property: 指定要过渡的 CSS 属性(如 width, background-color, all)。
  • transition-duration: 过渡持续时间(如 0.3s, 300ms)。
  • transition-timing-function: 时间曲线(如 ease, linear, ease-in-out, cubic-bezier(...))。
  • transition-delay: 延迟开始时间。

示例

.box {
  width: 100px;
  height: 100px;
  background-color: blue;
  /* 简写:宽度变化耗时1秒,线性过渡,延迟0.5秒;背景色同时变化 */
  transition: width 1s linear 0.5s, background-color 1s ease;
}

.box:hover {
  width: 200px;
  background-color: red;
}

animation-关键帧动画

animation 配合 @keyframes 可以实现更复杂的动画效果。它不需要触发事件即可自动运行,并且可以控制循环次数、方向、暂停等。

语法

/* animation: name duration timing-function delay iteration-count direction fill-mode play-state; */
animation: slide-in 0.5s ease-out forwards;
  • animation-name: @keyframes 定义的动画名称。
  • animation-duration: 持续时间。
  • animation-timing-function: 时间曲线。
  • animation-delay: 延迟时间。
  • animation-iteration-count: 播放次数 (1, infinite 等)。
  • animation-direction: 播放方向 (normal 正常, reverse 反向, alternate 往返)。
  • animation-fill-mode: 动画结束后的状态 (none, forwards 停在最后一帧, backwards, both)。
  • animation-play-state: 播放状态 (running, paused)。

示例

@keyframes slide-in {
  0% {
    transform: translateX(-100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

.modal {
  animation: slide-in 0.5s ease-out forwards;
}

transition 与 animation 的区别

特性transitionanimation
触发方式需要事件触发(hover, js 改 class, focus 等)可以自动运行,也可以通过 JS 控制
状态控制只有开始和结束两个状态可以定义中间的关键帧 (0%, 50%, 100%)
循环不能循环,触发一次执行一次可以设置 iteration-count 循环播放
精细度适合简单的属性变化适合复杂的自定义动画效果
JS 控制较弱较强(Running/Paused, 事件监听 animationend)

性能优化

动画性能是面试中的高频考察点,核心在于减少重排(Reflow)和重绘(Repaint),尽量利用 GPU。

1. 硬件加速 (GPU 加速)

浏览器通常使用 CPU 进行渲染(特别是重排和重绘)。开启硬件加速后,浏览器会将通过特定 CSS 属性的元素提升到一个独立的合成层 (Compositing Layer)。该层的变换由 GPU 处理,跳过 Layout 和 Paint 阶段,直接进行 Composite,效率极高。

如何开启? 常见触发 GPU 加速的属性:

  • transform: translateZ(0)
  • transform: translate3d(0, 0, 0)
  • will-change: transform
  • opacity (配合动画时)

2. 高性能动画属性

始终优先对以下属性进行动画,因为它们只会触发 Composite (合成) 阶段:

  • transform: 处理位移 (translate)、缩放 (scale)、旋转 (rotate)。 (替代 left/top/width/height)
  • opacity: 处理透明度显隐。 (替代 visibility/display)

反例: 使用 left: 100px 动画会触发 Layout,每一帧都要重新计算布局,容易导致卡顿 (Jank)。

3. will-change

will-change 是一个 CSS 属性,用于告知浏览器该元素即将发生变化,让浏览器提前进行优化(如创建合成层)。

.element {
  will-change: transform, opacity;
}

注意事项:

  • 不要滥用:创建合成层会消耗 VRAM (显存)。给太多元素设置 will-change 会导致内存飙升,反而降低性能。
  • 适时移除:最好在动画结束后通过 JS 移除该属性,或者仅在 :hover 状态下使用。

常见面试题解答

1. transition 和 animation 的区别?

(详见上文表格)

  • Transition 是“过渡”,强调从一种状态平滑变到另一种状态,需要触发条件。
  • Animation 是“动画”,强调流程和帧的控制,可以自动播放、循环、暂停。

2. 如何开启 GPU 硬件加速?

  • 使用 Hack 写法:transform: translate3d(0,0,0)translateZ(0)
  • 使用标准属性:will-change: transform
  • 使用 3D 变换、video、canvas 等本身就会创建新图层。

3. 为什么 transform 性能好?

  • 渲染流程:HTML 解析 -> Style -> Layout (重排) -> Paint (重绘) -> Composite (合成)。
  • 修改 left/top 会触发 Layout 及其之后的所有流程,消耗 CPU。
  • 修改 transform/opacity 仅触发 Composite。此时元素已经绘制成位图(Texture),GPU 只需要对位图进行变换,速度极快且不占用主线程。