scroll-timeline无法驱动全局进度条宽度,因其仅定义动画节奏(0%→100%),不提供可读取的滚动百分比值,也不支持将滚动位置映射为css变量或width/scalex等样式属性;必须用js计算scrolltop/(scrollheight-clientheight)并注入css变量,再通过transform: scalex()实时更新。

必须用 JavaScript 计算滚动百分比并写入 CSS 变量,CSS 本身无法读取或暴露 scrollTop 的实时数值;scroll-timeline 不提供可绑定到 width 或 height 的状态值,仅用于动画节奏控制。
为什么 scroll-timeline: scroll(root) 不能驱动进度条宽高
scroll-timeline 是一个时间线定义机制,不是状态读取 API。它只告诉浏览器“从顶部滚到底部”对应动画的 0% → 100%,但不生成可被 width、height、transform 直接引用的变量值。即使写了 @keyframes { from { width: 0% } to { width: 100% } },动画也只播放一次,无法暂停、回退或响应反向滚动。
更关键的是:scroll(root) 的起止位置按元素在 DOM 中的物理位置计算,不是统一以文档总高度为基准——多个元素共用同一时间线时,各自进度不同步。
- 没有类似
scroll-progress()的 CSS 函数可提取当前滚动比例 -
width: calc(var(--scroll-progress) * 1%)这类表达式里,--scroll-progress必须由 JS 显式注入,否则就是calc(0 * 1%) = 0 - 试图用
@property声明类型也无法绕过“无源数据”问题——没值可声明
JS 怎么安全更新 --scroll-progress 并避免跳变
核心是节流 + 安全计算 + 合成层优化。直接在 scroll 事件里改样式会触发强制同步重排,尤其在 iOS Safari 上极易卡顿。
- 用
requestAnimationFrame包裹更新逻辑,确保每帧最多一次重绘 - 分母必须是
document.documentElement.scrollHeight - window.innerHeight,漏减window.innerHeight会导致滚动到底时宽度超 100% - 分子用
document.documentElement.scrollTop || document.body.scrollTop,兼容旧文档模式 - 数值过滤必不可少:
Math.min(1, Math.max(0, progress)),防止小数精度误差或空值导致calc()失效 - 别用
debounce,它会让进度条“断续前进”,失去实时感
示例关键段:
let ticking = false;
function updateProgress() {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = window.innerHeight;
const progress = Math.min(1, Math.max(0, scrollTop / (scrollHeight - clientHeight)));
document.documentElement.style.setProperty('--scroll-progress', progress);
ticking = false;
}
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(updateProgress);
ticking = true;
}
});
CSS 中怎么用 --scroll-progress 驱动宽高且不卡顿
优先用 transform: scaleX()(水平)或 scaleY()(垂直),而非 width / height。前者走合成层,GPU 加速;后者触发 layout + paint,在中低端设备上明显掉帧。
- 进度条元素需提前触发独立合成:加
will-change: transform或transform: translateZ(0) - 初始
transform-origin设为left(水平)或top(垂直),否则缩放中心偏移,看起来像“从中间展开” - CSS 写法必须是
transform: scaleX(var(--scroll-progress, 0)),不能省略 fallback 值 - 别在父容器上写
transition,要加在进度条自身元素上 - 固定定位用
position: fixed,不是absolute——后者随内容滚动消失
HTML 结构建议:
<div class="progress-bar" style="transform: scaleX(var(--scroll-progress, 0))"></div>
CSS 示例:
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: #007aff;
z-index: 9999;
transform-origin: left;
will-change: transform;
}
最易被忽略的是合成层触发和数值过滤:没加 will-change 或 translateZ(0),scaleX() 就不会进 GPU 合成;传了 null 或空字符串给 --scroll-progress,calc() 就静默失败,进度条直接消失——这两点在真实项目里高频出问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











