<p>滚动百分比应为:Math.min(1, Math.max(0, (scrollY - top) / (scrollHeight - clientHeight))),需缓存 scrollHeight/clientHeight、用 requestAnimationFrame 节流、iOS 需 clamp 橡皮筋异常值,并通过 document.documentElement.style.setProperty('--var', ratio) 传入 CSS 变量配合 hsl() 实现平滑过渡。</p>

直接用 scroll 事件配合 CSS 变量 + hsl() 是目前最可控、最易维护的方案,比纯 CSS 动画或内联 style 更灵活,也比 IntersectionObserver 更精确。
滚动百分比怎么算才不翻车
别在 scroll 回调里反复读 document.body.scrollHeight 和 window.innerHeight——页面加载图片、动态插入内容后,scrollHeight 会突变,导致百分比跳变甚至负值。
- 用
requestAnimationFrame节流:只在下一帧计算,避免高频重排重绘 - 缓存
scrollHeight和clientHeight到闭包变量里,只在DOMContentLoaded或resize后更新一次 - 加安全限幅:
Math.min(1, Math.max(0, ratio)),防止小数精度误差或 DOM 未就绪导致超范围 - 移动端注意:iOS Safari 的“橡皮筋”回弹会让
window.scrollY超出合法范围(比如负值或大于scrollHeight - clientHeight),得额外判断并 clamp
CSS 变量怎么传才真正生效
document.documentElement.style.setProperty('--bg-progress', value) 是唯一推荐写法。其他方式要么不可靠,要么丧失连续性。
- 别用
style.cssText或切换 class 预设色值——要写一堆 CSS 规则,改个颜色就得动两处 - 别漏掉
--前缀,拼错变量名(如写成bgprogress)会导致 CSS 里var(--bg-progress)退回到 fallback 值 - 传值只用纯数字(如
0.42),不要带%或单位,否则calc()里参与运算会出错 - CSS 中必须显式消费变量,例如:
background: hsl(220, 90%, calc(95% - var(--bg-progress) * 85%));
为什么非要用 hsl() 而不是 rgb() 或 linear-gradient
hsl() 的亮度(L)参数天然映射滚动进度,线性调节就能得到视觉平滑的明暗过渡;而 rgb() 三通道需分别插值,稍有偏差就发紫、偏绿,调试成本高。
立即学习“前端免费学习笔记(深入)”;
- 选一个主色相(H)和固定饱和度(S),只动 L,公式形如:
hsl(220, 90%, calc(95% - var(--bg-progress) * 85%)) - L 值避开
0%和100%极端值,建议控制在5%–98%区间,否则暗色/亮色模式下容易糊成一片 - 如果设计稿给的是品牌 RGB 值,用在线工具转一次 HSL 就行,不用手算
- 现代浏览器对
hsl() + calc()解析已高度优化,性能优于 JS 拼字符串再设style.background
过渡动画怎么加才不卡顿
光靠 JS 改变量还不够,必须配合 CSS transition 才能实现视觉平滑。但要注意:过渡目标必须是被变量驱动的属性本身,不能只过渡内联样式。
- 在根元素或目标元素上加:
transition: background 0.3s ease-out;(别写background-color,因为hsl()返回的是 color 值,不是单一通道) - 确保
transition写在 CSS 文件里,而不是 JS 里动态加style.transition,否则可能因样式优先级或重绘时机失效 - 移动端必须监听时加
{ passive: true },否则 Chrome 会警告并降级滚动性能 - 如果只是简单开关(比如滚动过 100px 后变色),用
IntersectionObserver替代scroll更轻量,但精度不如直接读scrollY
最容易被忽略的是 iOS Safari 的橡皮筋回弹导致的 scrollY 异常,以及没加 passive: true 引起的滚动卡顿——这两个点线上一跑就暴露,但本地开发常测不出来。


















