scroll-timeline 无法实现真正的全局滚动进度条,它仅支持单向不可逆动画,不能实时反映滚动百分比、回滚、暂停或动态内容重排;必须用 js 计算进度 + css 变量 + transform: scalex() 实现精确可控的进度条。

直接说结论:scroll-timeline 不能实现真正的“全局滚动进度条”,它只能驱动一次性的、不可逆的动画播放,无法实时反映 scrollTop / (scrollHeight - clientHeight) 这个百分比值。想让进度条宽度随滚动精确变化、支持回滚、暂停、动态内容重排后仍准确 —— 必须用 JS 计算 + CSS 变量 + transform: scaleX()。
scroll-timeline 实现的“进度条”本质是单向动画
- 它把整个滚动过程映射为一个从 0% 到 100% 的时间线,动画只在用户从顶部滚到底部时“播放一遍”
- 没有 API 或 CSS 函数能读取当前滚动位置(比如没有
scroll-progress()) - 写
@keyframes { from { width: 0% } to { width: 100% } }看似可行,但:- 动画一旦播完就停在 100%,不会随用户往上滚而缩回
- 若用户中途停止滚动,进度条卡在中间,但无法被 JS 读取或控制
- 多个元素共用同一
scroll(root)时间线时,各自起始/结束位置由 DOM 位置决定,不是统一按文档总高计算
为什么 animation: name auto 不等于“实时进度”
-
auto是滚动驱动动画的必需关键字,它告诉浏览器“时长由滚动距离决定”,不是“值可读取” - 浏览器内部把滚动位移线性映射到动画关键帧的 0%→100%,但这个映射结果不暴露给 CSS 属性(如
width、background-position) - 所以你无法用
width: calc(var(--scroll-progress) * 100%)这类写法 —— 因为--scroll-progress根本不存在,也没地方注入
真正可控的方案:JS 计算 + CSS 变量 + transform
- 监听
scroll事件,用requestAnimationFrame节流(别用debounce,会跳变) - 计算:
const progress = Math.min(1, Math.max(0, (document.documentElement.scrollTop || document.body.scrollTop) / (document.documentElement.scrollHeight - window.innerHeight))) - 注入变量:
document.documentElement.style.setProperty('--scroll-progress', progress) - 进度条样式必须用:
.progress-bar { transform: scaleX(var(--scroll-progress)); transform-origin: left; will-change: transform; } - 注意移动端 Safari:
-
document.body.scrollTop恒为 0,必须优先取document.documentElement.scrollTop - 若页面启用了
overflow-anchor: auto(默认),可能抑制 scroll 事件,加html { overflow-anchor: none; }更稳
-
真正容易被忽略的是:即使你只在 Chrome 里跑,scroll-timeline 驱动的“进度条”也无法响应动态插入内容后的高度变化。比如加载更多文章后,scroll(root) 时间线长度不会自动更新,动画会提前结束或错位。而 JS 方案只要重新计算分母,就能立刻对齐 —— 这才是生产环境里绕不开的一环。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











