题目与使用场景
目标是让动画进度跟随滚动,同时避免每次滚动都运行 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 增强。
@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:处理减少动态效果。
@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:如何在生产中验证?
记录支持情况,在代表性设备上做性能追踪、减少动态效果检查和无障碍验证,并保留可用的静态基线。