必须用js注入--scroll-y变量,因css无法获取滚动值;需用requestanimationframe节流并初始化调用,配合translatez(0)升层和calc(var(--scroll-y)*var(--parallax-factor))乘法计算位移。

直接用 CSS 变量驱动视差滚动,必须靠 JavaScript 注入 --scroll-y,纯 CSS 拿不到滚动值——这是最常被忽略的前提。
为什么必须用 JS 更新 --scroll-y
CSS 本身没有访问 window.scrollY 或 document.documentElement.scrollTop 的能力。所谓“CSS 驱动”,只是把计算逻辑从 JS 样式赋值挪到了 CSS 的 calc() 里,变量源头仍得由 JS 控制。
-
background-attachment: fixed看似不用 JS,但它差速固定为 1:0,无法设0.3或-0.5这类系数,也不支持局部滚动容器 - 不注入变量就写
transform: translateY(calc(var(--scroll-y) * var(--parallax-factor))),结果是translateY(NaN),元素直接消失 - IE/Edge 兼容需同时读取
window.scrollY和document.documentElement.scrollTop,漏一个会导致移动端或旧浏览器滚动值为 0
怎么安全注入并节流 --scroll-y
高频 scroll 事件不节流会吃光主线程,尤其在低端安卓机上明显卡顿。别用 setTimeout 防抖,要用 requestAnimationFrame 保证与渲染帧同步。
- 初始化时就要调一次
updateScrollVar(),否则首屏加载后变量为空,视差不生效 - 写法必须是
document.documentElement.style.setProperty('--scroll-y', y + 'px'),不是document.body—— 否则 iOS Safari 下可能读不到 - 别在每个视差元素上都监听 scroll,统一用一个全局监听器 + 批量更新变量,减少事件绑定开销
const updateScrollVar = () => {
const y = window.scrollY || document.documentElement.scrollTop;
document.documentElement.style.setProperty('--scroll-y', y + 'px');
};
window.addEventListener('scroll', () => requestAnimationFrame(updateScrollVar));
updateScrollVar(); // 初始化
calc() 里该用乘法还是除法?
必须用乘法:calc(var(--scroll-y) * var(--parallax-factor))。除法看似直观,但风险极高。
-
--parallax-factor若为0、空值或未定义,calc(var(--scroll-y) / var(--parallax-factor))直接返回NaN,整个transform失效 - 系数推荐设为小数(
0.1~0.8),负值可反向(-0.3),避免用px单位硬编码,否则响应式切换时没法动态改 - 多个图层不同速度,不要共用 class 覆盖系数,而是给每个元素单独设内联 style:
style="--parallax-factor: 0.4"
为什么加 translateZ(0) 不是可选项
就算写了 transform: translateY(...),浏览器默认仍可能把它和普通文本合在同一个图层渲染。滚动时照样触发 paint,中低端机一卡一卡的。
-
translateZ(0)是轻量升层手段,比will-change: transform更可控,不会引发过度优化 - 必须写在同一个
transform声明里:transform: translateZ(0) translateY(calc(...)),分开写会被后者覆盖 - 别对超过 3~4 个元素同时加,iOS Safari 上 GPU 内存容易爆,导致页面崩溃或白屏
真正卡点不在写法,而在变量注入时机和图层控制——漏掉 requestAnimationFrame 或忘了 translateZ(0),效果看起来“差不多”,但性能差十倍。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











