css scroll()时间轴可实现滚动进度条,无需js监听,但仅chrome115+、edge115+、safari17.4+支持;firefox不支持,需用css.supports检测并降级为js方案。

直接用 scroll() 时间轴就能实现,不需要 JavaScript 监听滚动或计算百分比 —— 但前提是浏览器支持(Chrome 115+、Edge 115+、Safari 17.4+,Firefox 尚未支持)。
animation-timeline: scroll(root) 是核心
滚动进度条的本质是把动画时间线绑定到整个页面的滚动位置,而不是传统的时间流逝。关键不是“动多快”,而是“滚到哪,动到哪”。
-
animation-timeline: scroll(root)表示使用根滚动容器(即document.scrollingElement)的滚动进度作为动画驱动源 - 动画本身必须是线性且单次播放(
animation-iteration-count: 1或省略,默认为1),否则会重复触发 - 不能和
animation-play-state: paused混用,否则进度会卡住 - 动画时长(
animation-duration)实际无效,真正起作用的是animation-range(但对scroll(root)默认是0% 100%,通常不用显式写)
进度条元素必须 fixed 定位 + 覆盖全宽
因为要始终贴顶显示,且宽度变化需视觉连贯,position: fixed 是最稳妥选择;若用 absolute 或静态定位,滚动时容易错位或被遮挡。
- 必须设置
top: 0; left: 0; width: 100%,确保横跨视口顶部 - 高度建议设为
height: 4px这类小值,避免遮挡内容 - 不要给父容器加
overflow: hidden,否则可能裁剪掉动画中的宽度过渡 - 如果页面有横向滚动(
overflow-x: auto),进度条宽度要用width: 100vw而非100%,防止在宽屏下缩进
常见错误:动画不动 / 卡在 0% / 突然跳变
这些基本都源于 timeline 绑定失败或 CSS 层叠干扰,不是代码逻辑问题。
- 检查是否漏写了
animation-fill-mode: forwards—— 缺失会导致动画结束后回退到初始状态(看起来像没动) - 确认元素没有被
transform、filter或will-change触发合成层异常,某些旧版 Chromium 下会中断scroll()timeline - 避免在同一个元素上同时写
animation和transition,后者会覆盖 timeline 行为 - 如果页面初始有滚动偏移(如 hash 锚点跳转),
scroll(root)会从当前 offset 开始计,导致动画起点偏移 —— 可加scroll-behavior: smooth并在 JS 中延迟 1 帧初始化,或接受该行为
兼容性兜底必须用 JS 计算 fallback
目前 Firefox 完全不识别 scroll(),Safari 17.4+ 才开始支持,生产环境不能只靠它。最简 fallback 是监听 scroll 事件 + requestAnimationFrame 更新 width:
const bar = document.querySelector('.progress-bar');
const update = () => {
const progress = window.scrollY / (document.body.scrollHeight - window.innerHeight);
bar.style.width = `${Math.max(0, Math.min(100, progress * 100))}%`;
};
window.addEventListener('scroll', update);
update(); // 初始化
注意:CSS 动画部分仍保留(供支持浏览器),JS 只负责 width 设置,不触发动画 —— 否则两套逻辑打架。真正的难点不在实现,而在判断何时该切到 JS 模式:不能只靠 UA,得用 CSS.supports('animation-timeline', 'scroll()') 实际检测特性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











