滚动进度指示器是一种常见的界面模式,可直观地传达用户在可滚动文档或容器中的进度。当用户滚动时,视觉元素会更新以反映其位置,从而清晰直观地显示已查看的内容量和剩余内容量。
如何实现
如需创建滚动进度指示器,您需要准备两样东西:
- 用作进度条的元素。此元素通常为
position: fixed或position: absolute,以便在用户滚动时保持在视野范围内。 - 与滚动位置相关联的动画。
具体方法如下:
- 首先,创建一个 HTML 元素作为进度条。您可以根据自己的喜好设置此元素的样式。
- 接下来,在 CSS 中定义一个
@keyframes动画,用于缩放进度条。一种常见方法是将元素从scaleX(0)缩放到scaleX(1)。 - 最后,将此动画应用到进度条元素,并将其
animation-timeline设置为 scroll-timeline。这会指示浏览器根据最近的祖先滚动条的滚动位置来驱动动画进度。
示例代码
此代码使用由 scroll() 函数创建的匿名滚动时间轴,在滚动时放大 #progress 元素。
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: scroll();
}
}
}
由于 scroll() 函数在 DOM 中的位置,它将跟踪 block 方向上最近的祖先滚动条,此处为根滚动条。
<body>
<!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
<div id="progress" aria-hidden="true"></div>
</body>
此代码使用由 scroll-timeline 属性创建的命名 scroll-timeline,在滚动时放大 #progress 元素。
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
:root {
scroll-timeline: --tl block;
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: --tl;
}
}
}
最佳做法
使用滚动驱动动画时,请务必遵循一些最佳实践,以确保提供流畅且无障碍的体验:
- 务必添加功能检测:并非所有浏览器都支持滚动驱动的动画。使用
@supports (animation-timeline: scroll())检查是否支持,并为不支持的浏览器提供后备方案。- 请勿将
scroll-timeline-polyfill软件包用于回退策略,因为它功能不完整,并且存在许多已知问题。 - 如果动画仅被视为装饰性元素,请选择渐进增强,不要提供后备。
- 请勿将
- 务必从辅助技术朗读流程中移除纯装饰性元素:对纯视觉滚动指示器应用
aria-hidden="true",以确保屏幕阅读器不会遇到未命名的空节点。 - 务必尊重用户偏好:有些用户更喜欢在网页上看到较少的动画。使用
prefers-reduced-motion媒体查询为这些用户停用或减少动画。 - 务必尝试仅为高性能 CSS 属性添加动画效果:为了获得最流畅的动画效果,请坚持为可由浏览器的合成器线程处理的属性(例如
transform和opacity)添加动画效果。为其他属性(例如width或height)添加动画效果可能会导致性能问题。 - 务必使用正确的声明顺序:使用
animation简写属性时,请在其后声明animation-timeline,以防止简写属性重置时间轴。
使用 scroll() 函数创建滚动驱动的动画时:
- 可选:明确指定滚动条。如果目标不是最近的祖先滚动条,请使用
scroll(root)或scroll(self)明确指定要使用的滚动条。- 如果
root、nearest或self不够用,请使用命名的 scroll-timeline。
- 如果
- 可选:明确指定要跟踪的轴。如果未定位到默认的
block轴(例如在水平滚动条中),请使用scroll(block)或scroll(inline)明确指定要跟踪的轴。
使用 scroll-timeline 属性创建滚动驱动的动画时:
- 务必使用 CSS
<dashed-ident>作为名称。 - 可选:明确指定要跟踪的轴。如果目标不是默认的
block轴(例如在水平滚动条中),请使用scroll-timeline-axis明确指定要跟踪的轴。 - 务必确保查找范围有效:如果声明
scroll-timeline的元素不是动画元素的平面树祖先,请在共享祖先上使用timeline-scope来提升scroll-timeline名称的可见性。
后备策略
对于不支持滚动驱动动画的浏览器,您可以使用后备方案来重现视觉效果。后备方案通常使用滚动监听器(用于 ScrollTimeline 效果)或 IntersectionObserver API(用于 ViewTimeline 效果)构建。
在内置支持滚动驱动动画的浏览器中,请始终使用原生 CSS 实现,因为这些实现的性能更高。
请注意,并非所有效果都可以通过后备方法重现。
具体而言,对于此用例,以下脚本会为不支持滚动驱动动画的浏览器应用回退。它使用滚动监听器来跟踪根元素的滚动位置,并相应地更新进度条的 transform 属性。
<script>
if (!CSS.supports('animation-timeline', 'scroll()')) {
const progress = document.querySelector('#progress');
window.addEventListener('scroll', () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPercentage = (scrolled / scrollable);
progress.style.transform = `scaleX(${progressPercentage})`;
});
}
</script>