パララックスとは

背景と本文をスクロール量に応じて異なる速さで動かし、奥行きのある視差(パララックス)を感じさせる技法。

概要

背景と手前の本文が、スクロール量に対してそれぞれ違う速さで動いて見えるようにする技法です。 背景をゆっくり動かし、本文を通常の速さでスクロールさせると、奥行きのある立体的な印象が生まれます。 background-attachment: fixed だけで済ませる実装は端末や iframe 内で視差が出なかったりちらついたりすることがあるため、 スクロール量に応じて背景レイヤーを transform: translateY() で動かす JavaScript 実装にするのが安定します。

別名

表記ゆれ:パララックス、視差効果、パララックススクロール

英語の parallax(視差)をそのままカタカナにした呼び方が最も一般的です。

AI への伝え方

「背景がゆっくり動くパララックスにして」で伝わります。背景を完全に固定したいのか、本文よりわずかに遅い速さで動かしたいのかを伝えると仕様がぶれません。 スクロール量に連動して大きく動く演出は乗り物酔いに似た症状(動揺病)を引き起こすことがあるため、prefers-reduced-motion: reduce の環境では動きを止める配慮が必要です。

似たパーツとの違い

「スクロールアニメーション」は要素が画面に入った一瞬だけ再生されるのに対し、パララックスはスクロールしている間ずっと動き続ける点が違います。

デモ

コード

HTML
<div class="parallax-demo">
  <section class="parallax-demo__layer">
    <div class="parallax-demo__bg" data-parallax-bg></div>
    <span class="parallax-demo__label" aria-hidden="true">背景(ゆっくり)</span>
    <h2 class="parallax-demo__heading">ゆっくり動く背景</h2>
  </section>
  <div class="parallax-demo__content">
    <p>本文はふつうにスクロールします。背景セクションだけゆっくり動いて見えます。</p>
    <p>さらにスクロールしてください。</p>
    <p>もう少し続きます。</p>
  </div>
  <section class="parallax-demo__layer parallax-demo__layer--alt">
    <div class="parallax-demo__bg" data-parallax-bg></div>
    <span class="parallax-demo__label" aria-hidden="true">背景(ゆっくり)</span>
    <h2 class="parallax-demo__heading">もう一つの背景</h2>
  </section>
  <div class="parallax-demo__content">
    <p>2 つ目の背景セクションです。</p>
    <p>これでダミー本文は終わりです。</p>
  </div>
</div>
CSS
.parallax-demo {
  font-family: sans-serif;
  color: #333333;
}

.parallax-demo__layer {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 320px;
  overflow: hidden;
  color: #ffffff;
}

/* 本文の 0.25〜0.3 倍の速さで動く背景レイヤー。
   position: absolute + 高さ 160% にして、動いてもセクションの外にはみ出さないようにする。
   斜めのストライプ模様を重ねているのは、単色グラデーションのままだと動いても見た目に差が出ないため */
.parallax-demo__bg {
  position: absolute;
  top: -30%;
  left: 0;
  width: 100%;
  height: 160%;
  background-image: repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.16) 0 24px,
      rgba(255, 255, 255, 0) 24px 48px
    ),
    linear-gradient(135deg, #0017c1 0%, #4dd0e1 100%);
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.parallax-demo__layer--alt .parallax-demo__bg {
  background-image: repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.16) 0 24px,
      rgba(255, 255, 255, 0) 24px 48px
    ),
    linear-gradient(135deg, #333333 0%, #0017c1 100%);
}

.parallax-demo__label {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 1;
  padding: 4px 10px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.35);
  font-size: 11px;
  /* ラベル自体は背景と一緒に動かさず、位置を固定して速さの違いが分かる目印にする */
}

.parallax-demo__heading {
  position: relative;
  z-index: 1;
  margin: 0;
}

.parallax-demo__content {
  padding: 24px;
  line-height: 1.8;
}

.parallax-demo__content p {
  margin: 0 0 16px;
}
JS
(function () {
  var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  var layers = document.querySelectorAll("[data-parallax-bg]");
  if (!layers.length || reduceMotion) return;

  // 背景を本文の 0.25〜0.3 倍の速さで動かす係数(小さくするほど本文との速度差が大きく見える)
  var speed = 0.3;
  var ticking = false;

  function update() {
    layers.forEach(function (bg) {
      var section = bg.parentElement;
      var rect = section.getBoundingClientRect();
      // 背景レイヤーは高さ 160% で余白を確保しているので、動かせる量もその範囲に収める
      var maxOffset = (bg.offsetHeight - section.offsetHeight) / 2;
      var distanceFromCenter = rect.top + rect.height / 2 - window.innerHeight / 2;
      var offset = Math.max(-maxOffset, Math.min(maxOffset, -distanceFromCenter * speed));
      bg.style.transform = "translateY(" + offset + "px)";
    });
    ticking = false;
  }

  function onScroll() {
    if (ticking) return;
    ticking = true;
    window.requestAnimationFrame(update);
  }

  window.addEventListener("scroll", onScroll, { passive: true });
  window.addEventListener("resize", onScroll);
  update();
})();