前端一面:CSS 动效——transition 与 animation 常见考点

CSS 动效相关的问题,经常以「transition 和 animation 有什么区别」「为什么动画不生效」「性能有没有问题」这些形式出现在一面里。
这一篇不打算把所有属性逐条背一遍,而是先搞清楚几件事:transition 和 animation 的本质差异、各自适合解决什么问题,以及在项目和面试里常见的坑有哪些。

可以先把这两个东西抽象成两种不同的「时间线」:

  • transition:从一个状态「平滑过渡」到另一个状态
    • 需要某种「状态变化」触发:比如 :hover、class 切换、JS 修改样式。
    • 更适合「单段」过渡:从 A → B。
  • animation:按照关键帧脚本驱动的一整段动画
    • 不一定需要外部状态变化,写上就能自动播放,也可以设为循环。
    • 更适合多阶段、循环、复杂时间线的动效。

简单总结可以记成一句话:

  • transition 像是「两个状态之间的过渡效果」,animation 更像是「带剧本的时间轴」。

在绝大多数场景里,真正常用的是这几个:

  • transition-property:需要过渡的属性,比如:
    • opacitytransformbackground-colorheight 等。
  • transition-duration:过渡的总时间,如 0.3s200ms
  • transition-timing-function:时间函数,控制「快慢曲线」:
    • 常见值:easelinearease-inease-outease-in-out、自定义 cubic-bezier(...)
  • transition-delay:延迟多久再开始过渡,如 0.2s

通常会用简写来写多条属性的过渡:

1.btn {
2  transition: opacity 0.2s ease, transform 0.2s ease;
3}

常见原因可以归类成几种:

  • 只在目标状态写了 transition
    • 例如只在 :hover 里写 transition,默认状态没写。
    • 结果是:移上去瞬间跳,移开时才有平滑效果。
    • 更好的写法:transition 写在默认状态,目标状态只改属性值。
  • 对不能动画的属性使用 transition
    • 比如 displayvisibility 这种是直接切换,没有中间态。
    • 一般会配合 opacitytransformmax-height 等「可动画」属性来实现显示/隐藏。
  • 起点和终点一样
    • 某些情况下,JS 多次设置了相同的值,或者计算出的结果一样,看起来就像没动。

在一面里,如果想把答题拉到更专业一点,可以提到:

  • 优先对 transformopacity 做过渡
    • 这类属性通常只会引起合成层变化,可以更好地利用 GPU 加速。
  • 慎重对 widthheighttopleft 做长期动画
    • 这些属性变化往往会触发布局和重排,频繁执行会带来性能问题。

用一句话概括就是:

  • 能用 transform/opacity 解决的,就尽量不要用布局相关属性做动效。

animation 的核心是关键帧,可以用百分比或 from/to 来写:

 1@keyframes fade-in-up {
 2  from {
 3    opacity: 0;
 4    transform: translateY(10px);
 5  }
 6  to {
 7    opacity: 1;
 8    transform: translateY(0);
 9  }
10}

更复杂的动效可以有多段关键帧:

1@keyframes pulse {
2  0%   { transform: scale(1); }
3  50%  { transform: scale(1.05); }
4  100% { transform: scale(1); }
5}

动画名和时长是最基本的两个:

  • animation-name:要使用的关键帧名称,如 fade-in-up
  • animation-duration:持续时间,如 0.4s

其他常见控制项:

  • animation-timing-function:时间函数。
  • animation-delay:延迟。
  • animation-iteration-count:执行次数,如 12infinite
  • animation-direction:播放方向:
    • normalreversealternate(往返)等。
  • animation-fill-mode
    • 控制动画前后元素的样式生效情况:noneforwardsbackwardsboth
    • 入场动画常用 forwards,让元素停在最后一帧。
  • animation-play-state
    • running / paused,可以用 JS 控制暂停和继续。

一般写法会用简写:

1.card-enter {
2  animation: fade-in-up 0.4s ease-out 0s 1 forwards;
3}
  • 动画结束后「回弹」到初始状态
    • 原因:没设置 animation-fill-mode: forwards;
  • 想多次触发动画却只播放第一次
    • class 一直挂在元素上,浏览器认为已经播放完成,不会重新播放。
    • 常见解决思路:移除 class 强制重绘,再加回;或者通过改变 animation-name 强制重跑。
  • 无限循环动画性能问题
    • 大面积元素或多个复杂动画同时 animation-iteration-count: infinite,在低端设备上很吃性能。
    • 实战里通常只让小范围元素做简单循环,并控制关键帧逻辑尽量轻量。

在日常写页面和面试答题时,可以按照场景来区分:

  • 优先考虑用 transition 的场景
    • 元素有明确的「前后两种状态」:
      • 按钮 hover、active。
      • 折叠面板展开/收起。
      • 弹窗从隐藏到显示的简单淡入。
    • 只需要在状态切换时平滑过渡,不需要自动循环。
  • 适合用 animation 的场景
    • 需要「自动运行」或「循环」的效果:
      • loading spinner、骨架屏、心跳/呼吸效果。
    • 需要多阶段的复杂时间线:
      • 入场动画:先从下方滑入,再略微弹一下。
      • 轮播图的复杂切换节奏。

可以记一个简单判断:

  • 如果只是状态 A ↔ B 之间的切换,先考虑 transition;如果需要一段编排好的时间轴或循环动效,再考虑 animation。

可以从三个角度回答:

  • 触发方式不同:
    • transition 需要状态变化触发。
    • animation 可以自动或循环播放。
  • 描述方式不同:
    • transition 只描述「怎么从当前值过渡到目标值」。
    • animation 通过 keyframes 描述一整段时间轴。
  • 使用场景不同:
    • transition 更适合交互态的细节过渡。
    • animation 更适合复杂、独立于交互的动效。

可从这几方面展开:

  • 检查动画的是哪些属性:
    • 优先使用 transform / opacity
    • 避免持续动画 width / height / top / left
  • 控制参与动画的元素数量和层级:
    • 大面积背景、复杂阴影、模糊等效果在低端设备上非常吃性能。
  • 在必要时使用 will-change 提示:
    • will-change: transform;,但不要滥用,以免占用过多内存。

如果面试官比较关注用户体验,可以顺带提到:

  • 可以使用媒体查询 @media (prefers-reduced-motion: reduce)
    • 当用户系统偏好「减少动态效果」时,降低动画强度或者直接关掉非必要动画。

示例:

1@media (prefers-reduced-motion: reduce) {
2  * {
3    animation-duration: 0.01ms !important;
4    animation-iteration-count: 1 !important;
5    scroll-behavior: auto !important;
6    transition: none !important;
7  }
8}

这种细节在一面里提到,会明显加分。

围绕 transition 和 animation,答题时可以沿着这样的思路:

  • 先用一两句话说明它们在触发方式和使用场景上的区别
  • 再分别举 1–2 个典型应用场景和常见坑(比如属性选择、fill-mode、性能问题)。
  • 如果时间允许,可以补充一点关于「性能优化」和「可访问性」的思考。

只要沿着「解决什么问题 → 适合哪些场景 → 有哪些容易踩的坑」这个框架来讲,CSS 动效相关的问题通常都能比较系统地答清楚。