安全读取滚动百分比需用 requestanimationframe 节流,缓存 scrollheight 和 clientheight,math.min/max 限幅;css 变量用 setproperty 设置,配合 hsl() 中 l 参数线性变化实现平滑渐变;监听必须加 { passive: true }。

scroll事件里怎么安全读取滚动百分比
直接用 window.scrollY / (document.body.scrollHeight - window.innerHeight) 算百分比,看似简单,但容易在页面重绘、动态内容插入后出错——比如图片加载完成导致 scrollHeight 突然变大,而你没重新绑定或校准。更稳的做法是每次计算前先 requestAnimationFrame 节流,并缓存最新 scrollHeight 和 clientHeight 值。
实操建议:
- 别在
scroll回调里反复调用document.body.scrollHeight,提前存到闭包变量里,只在resize或DOMContentLoaded后更新 - 用
Math.min(1, Math.max(0, ratio))包一层,防止因小数精度或 DOM 未就绪导致负值或超 1 - 移动端要注意
body可能不滚动,真正滚动的是某个overflow-y: scroll的容器,得换目标元素监听
CSS变量怎么从JS传入并实时生效
document.documentElement.style.setProperty('--bg-progress', value) 是唯一可靠写法。别用 style.cssText 或操作 class 切换预设色值——那样要提前写一堆 CSS 规则,扩展性差,且无法实现连续渐变。
常见错误现象:设了 --bg-progress: 0.3,但背景色没变——大概率是 CSS 里没用 hsl() 或 rgba() 主动消费这个变量,或者变量名拼错(比如少个横线)。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- CSS 中必须显式引用变量,例如:
background: hsl(210, 100%, calc(80% - var(--bg-progress) * 30%)); - 变量值推荐传纯数字(如
0.42),不要带单位或百分号,避免 CSS 计算出错 - 如果要兼容老浏览器,得 fallback 到 JS 直接改
style.background,但现代项目基本可忽略
为什么用 hsl() 而不是 rgb() 做渐变背景
hsl() 的亮度(L)参数天然适合映射滚动进度:L 从 95%(浅灰白)线性降到 10%(深蓝黑),视觉过渡平滑;而 rgb() 三个通道得分别插值,稍有偏差就会发紫、偏绿,调试成本高。
性能影响很小,现代浏览器对 hsl() + calc() 的解析已高度优化,比用 JS 拼字符串再设 background 更快也更声明式。
实操建议:
- 选一个主色相(
H),固定饱和度(S),只动亮度(L),公式形如:hsl(220, 90%, calc(95% - var(--bg-progress) * 85%)) - 避免 L 值低于 5% 或高于 98%,否则在暗色/亮色模式下可能糊成一片
- 如果设计稿指定的是品牌 RGB 值,用在线工具转一次 HSL 就行,不用手算
滚动监听的性能坑:被动事件和 preventDefault
Chrome 会警告「Unable to preventDefault inside passive event listener」——因为你给 scroll 加了 { passive: false } 却没真调 preventDefault()。但这里根本不需要阻止默认行为,所以正确做法是显式声明 { passive: true }。
不加这个,iOS Safari 和新版 Chrome 会强制降帧,滚动卡顿明显,尤其低端机。
实操建议:
- 监听时务必写:
window.addEventListener('scroll', handler, { passive: true }) - 别在 handler 里做 DOM 查询、class 切换、复杂计算;所有耗时操作挪到
requestAnimationFrame里 - 如果用了 IntersectionObserver 做替代方案,它本身不触发重排,但无法精确到百分比,只适合区域级切换
滚动进度驱动背景色这事,关键不在“怎么动”,而在“动得稳不稳”——变量传得准不准、L 值压得够不够、passive 有没有设对,三处任一松动,用户滑着滑着就发现色块跳变或掉帧。这些点不报错,但体验毛刺感极强,最容易被忽略。










