滚动进度条需用 window.scrolly / (document.body.scrollheight - window.innerheight) 计算比例,配合 requestanimationframe 节流、background-size 动画、缓存尺寸值及避免强制回流来实现高性能响应式效果。

滚动时动态更新 progress 元素的 width 值
浏览器原生不提供“页面滚动进度”API,必须手动计算:用 window.scrollY 除以 document.body.scrollHeight - window.innerHeight 得到 0~1 的比例。这个值直接映射到进度条宽度即可。
常见错误是忽略减去视口高度导致进度条超过 100%,或在移动端未考虑 document.documentElement.scrollTop 的兼容写法——现代写法统一用 window.scrollY 即可(IE 不支持,但 IE 已淘汰)。
- 监听
scroll事件,但别直接在里面更新 DOM;加requestAnimationFrame节流,否则 iOS Safari 容易卡顿 -
width必须设为%或vw单位,不能用px,否则响应式失效 - 进度条容器建议固定定位:
position: fixed; top: 0; left: 0; right: 0; height: 3px;
用 linear-gradient 替代 border 实现更顺滑的动画
单纯靠 width 变化会有锯齿感,尤其在 Retina 屏。用渐变背景 + background-size 动画更细腻,且能避免重排。
原理是把整个进度条设为一个长宽比极大的渐变背景,再通过控制 background-size 模拟“填充”效果。CSS 动画对 background-size 的支持比 width 更稳定。
- 关键样式:
background: linear-gradient(to right, #4a90e2, #4a90e2); background-size: 0% 100%; background-repeat: no-repeat; - JS 中只更新
element.style.backgroundSize = progress + '% 100%'; - 务必加
will-change: background-size;提前告知浏览器要动画该属性
防止滚动抖动:避免 layout thrashing
如果在 scroll 回调里反复读取 scrollHeight 或 innerHeight,会强制触发回流,造成肉眼可见的卡顿。
解决方案是把所有“只读”属性缓存起来,仅在 resize 或 DOM 变化时重新计算:
- 初始化时缓存:
const maxScroll = document.body.scrollHeight - window.innerHeight; - 监听
resize事件,更新maxScroll—— 页面缩放、横竖屏切换都会影响它 - 不要在 scroll 回调里调用
getBoundingClientRect()或访问offsetHeight
移动端 Safari 的 scroll-behavior: smooth 冲突问题
如果页面启用了 scroll-behavior: smooth,某些 iOS 版本下 scrollY 在滚动过程中会滞后于真实位置,导致进度条跳变或延迟。
这不是 bug,而是平滑滚动动画与 JS 读取时机不同步所致。修复方式不是禁用平滑滚动,而是改用 scroll event 的 event.timeStamp 辅助判断是否处于主动滚动中:
- 记录上次 scroll 时间戳,若间隔
- 更稳妥的做法:改用
IntersectionObserver分段监听区域,但精度略低,适合长文章分章节进度 - 测试时务必真机验证,模拟器无法复现 Safari 的渲染节奏
真正麻烦的不是怎么画那根线,而是让它的变化和人眼感知同步——差几毫秒,用户就会觉得“卡”或者“没跟上”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











