代表性面试主题

前端面试:如何安全实现滚动驱动动画?

前端中等
OfferCC 编辑团队发布 更新

题干

产品页需要进度指示器和进入视口时动画的卡片。请设计滚动驱动实现,解释滚动进度与视图进度时间轴,兼容旧浏览器,并保证性能和无障碍。

题目与使用场景

目标是让动画进度跟随滚动,同时避免每次滚动都运行 JavaScript 处理器。CSS 滚动驱动动画通过 animation-timeline、scroll() 和 view() 提供滚动进度与视图进度时间轴。实现仍需要降级方案、减少动态效果、布局边界和明确的浏览器支持策略。

面试官考察什么

  • 是否区分滚动容器进度和元素可见进度。
  • 是否把 animation-timeline 放在 animation 简写之后。
  • 是否优先选择 transform 和 opacity,而不是触发布局的属性。
  • 是否使用特性查询或无动画基线。
  • 是否尊重 prefers-reduced-motion,并在无动画时保持内容可读。
  • 是否测试长页面、嵌套滚动容器、sticky 元素和移动端性能。

作答前的澄清问题

  • 进度跟随页面、嵌套滚动容器,还是元素进入视口?
  • 动画是在传达状态,还是纯装饰?
  • 支持哪些浏览器和内嵌 WebView?
  • 减少动态效果的用户应该看到什么?
  • 是否能降级成静态进度条或静态展示?
  • 是否有 sticky、transform 或虚拟列表祖先改变包含块?

30 秒回答框架

“页面进度使用滚动进度时间轴,卡片进入视口使用视图进度时间轴。我先提供可读的静态基线,再在特性查询中增强,并把 animation-timeline 放在动画简写之后。动画优先使用 transform 或 opacity,测试嵌套滚动和 sticky 元素,在 prefers-reduced-motion 下关闭非必要动画。上线前验证支持情况、帧成本、键盘访问和内容理解。”

深入作答步骤

步骤 1:选择时间轴。

动画映射到容器滚动范围时使用滚动进度时间轴;动画映射到元素穿过滚动视口的过程时使用视图进度时间轴。

步骤 2:建立基线。

先以有用的静态状态渲染进度条和卡片。浏览器忽略动画时,内容顺序、对比度、控件和状态仍应可用。

步骤 3:加入 CSS 增强。

css
@supports (animation-timeline: scroll()) {
  .progress {
    animation: grow-progress linear both;
    animation-timeline: scroll(root block);
  }

  .card {
    animation: reveal linear both;
    animation-timeline: view(block);
    animation-range: entry 0% cover 35%;
  }
}

由于简写会重置时间轴相关子属性,animation-timeline 要放在简写之后。嵌套滚动容器可能让最近滚动祖先不明确,应明确命名或指定目标容器。

步骤 4:保持动画属性轻量。

优先使用 opacity 和 transform。长列表默认不要动画 width、height、top、left 或大面积阴影,除非性能分析证明成本可接受。不要猜测性地给每张卡片添加 will-change。

步骤 5:处理减少动态效果。

css
@media (prefers-reduced-motion: reduce) {
  .progress,
  .card {
    animation: none;
    transform: none;
    opacity: 1;
  }
}

没有动画时也要保留内容和进度含义。静态指示器或瞬时状态比只缩短长过渡更安全。

步骤 6:测试几何关系与支持。

测试根滚动器、横向和纵向嵌套滚动、sticky 元素、动态内容、缩放、键盘导航和移动端视口变化。特性检测只控制增强,不应隐藏基线。

步骤 7:测量并观察。

用 DevTools 性能追踪检查样式、布局、绘制和合成器工作。测试长列表和低端移动设备,观察时间轴不支持或中断时内容是否仍可读。

高质量示范回答

“页面进度条使用 scroll(root block),因为它对应根滚动范围;每张卡片使用 view(block) 和 entry 到 cover 的范围,因为展示应跟随自身可见度。卡片先正常渲染,再在 @supports 中加入时间轴,并把声明放在动画简写之后。动画只使用 transform 和 opacity;减少动态效果用户不做非必要动画,并在嵌套场景明确滚动容器。最后测试键盘、sticky、动态内容、不支持的浏览器和移动端长列表性能。”

常见错误

  • 每帧监听 scroll → 处理器会和滚动竞争 → 关系可声明时优先 CSS 时间轴。
  • 混淆 scroll() 与 view() → 动画开始时机错误 → 按容器进度或元素可见度选时间轴。
  • 把 animation-timeline 放在简写前 → 简写会重置它 → 把时间轴声明放在后面。
  • 默认动画布局属性 → 长页面可能产生昂贵工作 → 先用 transform/opacity,再分析性能。
  • 不处理减少动态效果 → 用户无法控制偏好 → 提供静态状态。
  • 动画不支持就隐藏内容 → 旧浏览器失去信息 → 先建立基线。
  • 假设最近滚动容器总是正确 → 嵌套容器会改变时间轴 → 指定并测试目标容器。

追问与回答

追问 1:scroll() 和 view() 有什么区别?

scroll() 跟踪滚动容器进度;view() 跟踪元素穿过该容器滚动视口时的可见进度。

追问 2:为什么时间轴必须放在简写后?

animation 简写会重置时间轴相关子属性,提前声明可能被清除。

追问 3:降级方案是什么?

让内容以最终可读状态显示,使用静态进度指示器;只有确有产品需要时才加入小型 JavaScript 增强。

追问 4:如何支持减少动态效果?

关闭非必要动画、移除 transform,并用静态样式保留同样的内容和状态含义。

追问 5:可以动画 height 吗?

只有性能分析确认必要时才做。transform 和 opacity 通常避免布局工作,height 可能让兄弟元素重新布局。

追问 6:嵌套滚动容器怎么办?

最近的适用滚动容器可能不是页面。要测试几何关系,并命名目标时间轴或指定正确容器参数。

追问 7:如何在生产中验证?

记录支持情况,在代表性设备上做性能追踪、减少动态效果检查和无障碍验证,并保留可用的静态基线。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比