滚动指示器怎么实现才能实时反映页面滚动进度?

开发者彦鸽 阅读 11

我最近在做个人作品集网站,想加个顶部的滚动进度条,就是那种随着页面往下滚,进度条从0%到100%慢慢变长的效果。试了用 window.scrollY 除以 document.body.scrollHeight - window.innerHeight 来算比例,但结果有时候超过1或者卡在某个值不动,尤其在移动端特别明显。

这是我的代码:

window.addEventListener('scroll', () => {
  const scrollTop = window.scrollY;
  const scrollHeight = document.body.scrollHeight - window.innerHeight;
  const progress = (scrollTop / scrollHeight) * 100;
  document.querySelector('.progress-bar').style.width = <code>${progress}%</code>;
});

是不是哪里没考虑到?比如 body 高度计算的问题?求指点!

我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
Mr.艳青
Mr.艳青 Lv1
你遇到的这个问题我深有体会,根本原因是计算滚动高度时没有考虑到文档实际渲染高度和视窗尺寸的动态变化。特别是移动端经常会有地址栏隐藏/显示的情况,会改变 window.innerHeight 的值。

来一个改进版的解决方案,我通常会这么处理:

// 使用requestAnimationFrame优化性能
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
window.requestAnimationFrame(() => {
updateProgressBar();
ticking = false;
});
ticking = true;
}
});

function updateProgressBar() {
// 更可靠的计算方式,考虑到了文档实际高度
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollHeight = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
document.body.clientHeight,
document.documentElement.clientHeight
) - window.innerHeight;

// 确保progress在0-100范围内
const progress = Math.min(100, Math.max(0, (scrollTop / scrollHeight) * 100));
document.querySelector('.progress-bar').style.width = ${progress}%;
}

// 监听resize事件,因为窗口大小变化会影响计算
window.addEventListener('resize', updateProgressBar);


这里有几个关键改进点:

1. 用 requestAnimationFrame 来节流滚动事件,比直接监听scroll性能好很多,特别是高频触发时

2. 计算文档高度时用了 Math.max 取多种计算方式的最大值,因为不同浏览器对 scrollHeight 的实现可能有差异

3. 加了 Math.minMath.max 双重保险,确保进度值不会超出0-100范围

4. 额外监听resize事件,因为移动端浏览器工具栏的显示/隐藏会改变视窗高度

5. 用 pageYOffset 替代 scrollY 增强兼容性

实际项目中我还会建议加个transition效果让进度条变化更平滑,比如:
.progress-bar {
transition: width 0.2s ease-out;
}


移动端测试的时候特别要注意Safari,这货对文档高度的计算经常抽风,所以才要用那么多备选计算方式。
点赞
2026-03-09 10:01