直接用 scroll 事件更新进度条会卡顿,因高频触发导致频繁重排重绘,须用 requestAnimationFrame 对齐刷新节奏并加 ticking 标志位防重复入队。

为什么直接用 scroll 事件更新进度条会卡顿
因为滚动时浏览器每秒可能触发几十次 scroll 事件,每次都在主线程里算百分比、改 style.width,会频繁触发重排重绘。尤其在低端安卓机或 iOS Safari 中,页面明显掉帧、拖影、跳变。
这不是“加个 debounce(16)”就能解决的——延迟会导致滚动到底部时进度条还停在 95%,用户感知就是“卡住了”。
- 必须用
requestAnimationFrame,让更新和屏幕刷新节奏对齐 - 加
ticking标志位,避免快速滚动时重复入队多个回调 - 计算逻辑(如
window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight))放进去,不要在 scroll 里做 - 移动端要监听
resize,因为window.innerHeight在 Safari 地址栏收起/展开时会变,但document.documentElement.clientHeight更稳
document.documentElement.scrollHeight 为什么不能换成 document.body.scrollHeight
很多教程写 document.body.scrollHeight,但在 Safari、Firefox 或 DOCTYPE 不规范的页面里,它常比实际可滚动高度小 20–50px——因为 body 可能被 CSS 的 margin、padding 或 box-sizing 干扰,甚至被某些 CMS 插入的空 div 拉高虚值。
document.documentElement 指向 <html> 根元素,样式影响小、行为稳定,是 W3C 推荐的跨浏览器取法。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- scrollTop 统一用
window.scrollY(IE 用document.documentElement.scrollTop兜底) - clientHeight 必须用
document.documentElement.clientHeight,不是window.innerHeight - 如果页面内容不足一屏(
scrollHeight ≤ clientHeight),记得手动设progress = 0或隐藏进度条,否则算出来是NaN或负数
CSS 过渡怎么写才不“抽搐”
常见错误是只给进度条容器设 transition: width 0.2s,但没控制子元素或伪元素的继承行为,导致宽度突变、边缘锯齿、iOS 上闪烁。
真正平滑的关键是:过渡必须作用在最终渲染的“填充层”上,且初始状态要明确。
- 用
<div class="progress-bar"><span class="fill"></span></div>结构,只给.fill设transition: width 0.2s linear -
.fill必须有display: block和height: 100%,避免因 inline 默认行为导致高度塌陷 - 别依赖
max-width或flex布局自动撑开,用width: 0%显式初始化 - 移动端慎用
position: fixed,Safari 滚动时易闪;改用position: sticky; top: 0更稳
环形进度条的 stroke-dashoffset 怎么动态算
环形进度不是靠“画弧”,而是靠移动一条长度等于圆周的虚线段。起点默认在 3 点钟方向,人眼习惯从 12 点开始,所以必须加 transform="rotate(-90)"。
假设半径 r = 50,周长 circumference = 2 * Math.PI * r ≈ 314.16:
- 0% 时,
stroke-dashoffset = circumference(实线段完全偏移出去,看不见) - 60% 时,
stroke-dashoffset = circumference * (1 - 0.6) = 125.66 - 务必给
<circle class="progress">加内联style="transition: stroke-dashoffset 0.3s ease",写在 CSS 类里容易被覆盖 - 背景圆(track)和前景圆(progress)必须是两个独立
<circle>,否则无法分别控制颜色和虚线属性
滚动指示器最难的不是算百分比,而是应对那些“页面还没加载完就滚动”“内容动态插入后高度突变”“iOS 地址栏收放导致视口高跳变”的瞬间。这些边界情况不处理,进度条就会在 0% 和 100% 之间乱跳。


















