返回首页

如何使用 CSS 完成视差滚动效果?

问题解析

视差滚动(Parallax Scrolling)是一种通过多层背景以不同速度移动来创建立体运动效果的技术。面试考察这个问题,是想要了解候选人是否掌握 CSS 高级特性,以及能否用纯 CSS 实现视觉效果。

什么是视差滚动

视差滚动是指多层背景以不同的速度移动,形成立体的运动效果,带来出色的视觉体验。

网页可以解构成三个层次:

  • 背景层:移动最慢(通常是背景图)
  • 内容层:正常移动(主要内容)
  • 悬浮层:移动最快(浮动元素)

当滚动页面时,各层以不同速度移动,产生深度感。

CSS 实现方案

方案一:background-attachment: fixed

最简单直接的视差实现方式:

.parallax-section {
  height: 100vh;
  background-attachment: fixed;  /* 关键属性 */
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}

.bg-1 { background-image: url('bg1.jpg'); }
.bg-2 { background-image: url('bg2.jpg'); }
.bg-3 { background-image: url('bg3.jpg'); }
<section class="parallax-section bg-1">
  <h1>第一屏内容</h1>
</section>
<section class="content-section">
  <p>普通内容</p>
</section>
<section class="parallax-section bg-2">
  <h1>第二屏内容</h1>
</section>

原理

background-attachment: fixed 使背景图相对于视口固定,而不是随元素滚动。当元素滚动经过视口时,背景保持不动,产生视差效果。

优缺点

优点 缺点
实现简单 效果相对单一
性能较好 无法实现多层视差
兼容性好 移动端可能有兼容问题

方案二:transform + perspective(3D 视差)

更强大的多层视差实现:

/* 容器设置 3D 视角 */
.parallax-container {
  height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
  perspective: 1px;  /* 3D 视角深度 */
}

/* 内容层 */
.parallax-layer {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

/* 背景层 - 移动最慢 */
.layer-back {
  transform: translateZ(-2px) scale(3);  /* 远离屏幕,移动慢 */
}

/* 中间层 - 正常速度 */
.layer-base {
  transform: translateZ(0);  /* 正常位置 */
}

/* 前景层 - 移动最快 */
.layer-front {
  transform: translateZ(1px) scale(0.5);  /* 靠近屏幕,移动快 */
}

原理

利用 CSS 3D 变换:

  1. perspective 创建 3D 透视效果
  2. translateZ() 将元素在 Z 轴方向上移动
  3. 距离屏幕越远的元素(Z 值越小),滚动时移动越慢
  4. scale 补偿 Z 轴移动带来的大小变化

计算公式scale = 1 + (translateZ * -1) / perspective

例如 translateZ(-2px) 配合 perspective: 1px

  • scale = 1 + 2/1 = 3

完整示例

<div class="parallax-wrapper">
  <div class="parallax-layer layer-back">
    <img src="mountains.jpg" alt="远山">
  </div>
  <div class="parallax-layer layer-base">
    <h1>主要内容</h1>
    <p>滚动查看效果</p>
  </div>
  <div class="parallax-layer layer-front">
    <img src="trees.png" alt="前景树木">
  </div>
</div>
.parallax-wrapper {
  perspective: 1px;
  height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
  scroll-behavior: smooth;
}

.parallax-layer {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.layer-back {
  transform: translateZ(-4px) scale(5);
}

.layer-base {
  transform: translateZ(0);
}

.layer-front {
  transform: translateZ(2px) scale(0.5);
}

深入理解

性能优化

.parallax-layer {
  /* 开启硬件加速 */
  will-change: transform;

  /* 或者 */
  transform: translateZ(0);
  backface-visibility: hidden;
}

移动端适配

iOS Safari 对 background-attachment: fixed 支持不佳,建议使用:

@supports (-webkit-touch-callout: none) {
  /* iOS 设备 */
  .parallax-section {
    background-attachment: scroll;
    /* 或使用 JS 方案 */
  }
}

使用 CSS 变量控制速度

:root {
  --parallax-speed-slow: -2;
  --parallax-speed-normal: 0;
  --parallax-speed-fast: 1;
}

.parallax-layer {
  --speed: var(--parallax-speed-normal);
  --scale: calc(1 + (var(--speed) * -1) / var(--perspective, 1));

  transform:
    translateZ(calc(var(--speed) * 1px))
    scale(var(--scale));
}

.layer-slow {
  --speed: var(--parallax-speed-slow);
}

.layer-fast {
  --speed: var(--parallax-speed-fast);
}

JavaScript 增强方案

纯 CSS 视差有局限,复杂场景可结合 JS:

window.addEventListener('scroll', () => {
  const scrolled = window.pageYOffset;

  // 不同层以不同速度移动
  document.querySelector('.layer-back').style.transform =
    `translateY(${scrolled * 0.5}px)`;

  document.querySelector('.layer-front').style.transform =
    `translateY(${scrolled * -0.3}px)`;
});

注意:使用 requestAnimationFramepassive: true 优化性能:

let ticking = false;

window.addEventListener('scroll', () => {
  if (!ticking) {
    window.requestAnimationFrame(() => {
      // 更新视差效果
      ticking = false;
    });
    ticking = true;
  }
}, { passive: true });

现代 CSS 方案

scroll-timeline(实验性)

@keyframes parallax {
  to {
    transform: translateY(100px);
  }
}

.parallax-element {
  animation: parallax linear;
  animation-timeline: scroll(root);
}

最佳实践

  1. 不要过度使用:过多视差效果会导致眩晕
  2. 考虑性能:视差效果消耗 GPU 资源
  3. 提供关闭选项:尊重用户的 prefers-reduced-motion 设置
@media (prefers-reduced-motion: reduce) {
  .parallax-layer {
    transform: none !important;
  }
}
  1. 移动端简化:移动设备上减少或禁用视差效果

面试要点

  1. 能够说出 background-attachment: fixed 实现视差的方法
  2. 理解 transform: translateZ() 配合 perspective 的 3D 视差原理
  3. 了解视差效果的性能影响和优化方法
  4. 知道移动端的兼容性问题和解决方案
  5. 了解 prefers-reduced-motion 媒体查询的无障碍支持