用好Parallax视差让你的网页动起来
先看效果,再看代码
我最近在一个品牌官网上做了个视差滚动的首页,客户说“要那种看起来贵一点的动效”。其实说白了就是要点视觉层次感。最后我用纯 CSS + 一点点 JavaScript 搞定了基础视差,性能还行,iPhone 8 上也没卡顿。
最简单的一种做法是利用 background-attachment: fixed,这个属性你可能老早就会了,但真正在项目里用的时候,有些细节还是容易翻车。
.parallax-section {
height: 100vh;
background-image: url('https://jztheme.com/assets/images/parallax-bg.jpg');
background-attachment: fixed;
background-size: cover;
background-position: center;
}
<div class="parallax-section"></div>
<section>其他内容</section>
<div class="parallax-section"></div>
就这么几行,就能实现背景图相对固定、内容滚动时产生视差的效果。亲测有效,而且兼容性不错,IE9 都支持(虽然现在谁还在管 IE 啊)。
但这里注意:如果你在 iOS 上测试,会发现 background-attachment: fixed 基本失效。没错,Safari 就是这么任性。这个问题我踩过好几次坑,第一次还以为是我代码写错了,折腾了半天发现是浏览器行为限制。
想要全平台兼容?还得靠 JS
为了在移动端也能看到效果,我后来改用 JavaScript 控制背景位置。核心思路就是监听滚动事件,动态调整背景图的 background-position-y。
别怕性能问题,加个节流就行。我直接上手写了个简单的版本,没引入第三方库。
function initParallax() {
const sections = document.querySelectorAll('.js-parallax');
function updateBackgroundPosition() {
sections.forEach(section => {
const rect = section.getBoundingClientRect();
const offset = rect.top;
const speed = 0.3; // 背景移动速度,越小越慢
const yPos = -(offset * speed);
section.style.backgroundPositionY = ${yPos}px;
});
}
// 节流函数
function throttle(func, delay) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= delay) {
func.apply(this, args);
lastCall = now;
}
};
}
const handleScroll = throttle(updateBackgroundPosition, 16);
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleScroll); // resize 也要更新
updateBackgroundPosition(); // 初始调用一次
}
// 页面加载完就初始化
window.addEventListener('DOMContentLoaded', initParallax);
.js-parallax {
height: 100vh;
background-image: url('https://jztheme.com/assets/images/parallax-bg.jpg');
background-size: cover;
background-position: center top;
transition: background-position 0.1s ease; /* 可选:让移动更顺滑 */
}
这段代码我在多个项目里复用过,基本稳定。关键点在于:
- 使用
getBoundingClientRect获取元素相对于视口的位置,比直接算 scrollTop 更直观 - 节流设置为 16ms,接近 60fps,避免频繁触发影响性能
- 记得加上 resize 监听,否则窗口一缩放位置就错乱了
这个场景最好用
说实话,不是所有地方都适合上视差。我自己总结了两个最适合的场景:
- 首屏大图标题区:用户一进来就有视觉冲击,配合文案,提升质感
- 章节分隔页:比如产品介绍每一段之间插一个视差图,引导用户往下滚
反例也有——内容密集的信息流页面硬加视差,结果就是眼花缭乱,用户体验反而下降。之前有个同事在后台管理系统也搞了个视差 header,被产品经理骂了一顿:“这是要让用户头晕吗?”
踩坑提醒:这三点一定注意
下面这些坑我都亲自踩过,改完后仍有一两个小问题,但无大碍,分享出来让你少走弯路。
- 图片太大导致卡顿:我之前用了张 5MB 的高清图做背景,安卓机上直接掉帧。建议压缩到 500KB 以内,WebP 格式优先,实在不行就降分辨率
- fixed 在移动端无效:iOS Safari 不支持
background-attachment: fixed,Android 部分浏览器也有问题。所以如果项目必须覆盖移动端,建议一开始就用 JS 方案 - 滚动穿透或抖动:有些手机上会感觉背景“一顿一顿”的。解决方案是把
transform加上去骗硬件加速:transform: translateZ(0);或者will-change: transform;,能缓解不少
还有一个隐藏坑:如果你用了 React/Vue 这类框架,组件销毁时记得移除 scroll 事件!不然内存泄漏警告等着你。
// Vue 中示例
mounted() {
this.handleScroll = throttle(this.updateBackgroundPosition, 16);
window.addEventListener('scroll', this.handleScroll);
},
beforeUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
高级技巧:多层视差才够味
单层视差有点单调,真正“贵”的效果是多层视差——前景、中景、背景以不同速度移动,营造深度感。
实现方式也很简单:不用背景图,改用绝对定位的多个 DOM 元素,各自设置不同的滚动系数。
<div class="parallax-container">
<div class="layer bg" data-speed="0.1"></div>
<div class="layer mid" data-speed="0.3"></div>
<div class="layer fg" data-speed="0.6"></div>
</div>
.parallax-container {
position: relative;
height: 100vh;
overflow: hidden;
}
.layer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-size: cover;
}
.bg { background-image: url('https://jztheme.com/assets/images/bg.jpg'); }
.mid { background-image: url('https://jztheme.com/assets/images/mid.png'); }
.fg { background-image: url('https://jztheme.com/assets/images/fg.png'); }
function initMultiLayerParallax() {
const layers = document.querySelectorAll('.layer');
const container = document.querySelector('.parallax-container');
function updateLayers() {
const rect = container.getBoundingClientRect();
const offset = rect.top;
layers.forEach(layer => {
const speed = parseFloat(layer.getAttribute('data-speed')) || 0.1;
const yPos = -offset * speed;
layer.style.transform = translateY(${yPos}px);
});
}
const throttledUpdate = throttle(updateLayers, 16);
window.addEventListener('scroll', throttledUpdate);
window.addEventListener('resize', throttledUpdate);
updateLayers();
}
window.addEventListener('DOMContentLoaded', initMultiLayerParallax);
这种方案自由度更高,你可以给每一层加 opacity 动画、scale 缩放,甚至结合 Intersection Observer 做进入动画。不过代价是 DOM 结构变复杂了,维护成本上升。
要不要用现成库?我劝你三思
网上有很多视差库,比如 rellax、parallax-js、locomotive-scroll,功能确实强大。
但我自己的经验是:除非项目特别复杂,否则别引入。原因有三个:
- 体积不小,
locomotive-scrollminified 后还有 20KB+ - 配置项太多,学起来费时间
- 和现有滚动逻辑容易冲突,尤其是你用了 smooth scroll 或 anchor link 的时候
我自己试过 rellax,初始化一行代码搞定,确实方便:
const rellax = new Rellax('.rellax');
HTML 写成这样:
<div class="rellax" data-rellax-speed="-2">飘动的元素</div>
但它对 flex 布局有时不友好,某些情况下偏移计算出错,调试起来挺头疼。最后我还是换回自己写的精简版。
结语:这个技术的拓展用法还有很多
以上是我个人对 Parallax 视差的完整讲解,从最简单的 CSS 到 JS 控制,再到多层实现和避坑点。这套方案我在三个实际项目中都跑通了,包括企业官网、活动页和作品集。
当然,这也不是最优解。比如你想做 3D 视差跟随鼠标,就得上 WebGL 了;或者想配合 scroll progress 做进度条联动,也需要额外逻辑。这些我会在后续继续分享这类博客。
有更优的实现方式欢迎评论区交流。我现在已经对视差有点神经质了——看到任何网站背景动一下,第一反应都是“这应该是 JS 实现的,speed 设的是 0.3 左右”……

暂无评论