パララックス背景

複数レイヤーをスクロール速度差で動かし奥行きを演出します。ヒーローセクションのビジュアルに。

#javascript#parallax#animation

ライブデモ

使用例(お題: アイドルグループ Sakura)

この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- Sakura ライブ告知ヒーロー。複数レイヤーが速度差で動く -->
<div class="sk-scroller" id="plxScroller">
  <section class="sk-hero">
    <!-- 奥から手前へ、速度の異なるレイヤー -->
    <div class="sk-layer sk-sky" data-plx="0.1"></div>
    <div class="sk-layer sk-petals sk-petals-a" data-plx="0.45"></div>
    <div class="sk-layer sk-hill" data-plx="0.25"></div>
    <div class="sk-layer sk-petals sk-petals-b" data-plx="0.7"></div>

    <div class="sk-hero-copy">
      <span class="sk-badge">SPRING LIVE 2026</span>
      <h1>満開ツアー<br>「はなびらメロディ」</h1>
      <p>桜舞う春、5人がおくる新章のステージ。</p>
      <span class="sk-arrow" aria-hidden="true">&#8595;</span>
    </div>
  </section>

  <section class="sk-info">
    <h2>公演スケジュール</h2>
    <ul class="sk-dates">
      <li><span class="sk-city">東京</span><span class="sk-date">4/05 (日) 18:00</span></li>
      <li><span class="sk-city">大阪</span><span class="sk-date">4/12 (日) 17:30</span></li>
      <li><span class="sk-city">名古屋</span><span class="sk-date">4/19 (日) 18:00</span></li>
      <li><span class="sk-city">福岡</span><span class="sk-date">4/26 (日) 17:00</span></li>
    </ul>
    <p class="sk-note">※チケットは抽選先行受付中。応募はファンクラブ会員サイトから。</p>
    <p class="sk-foot">Sakura Official ・ はなびらメロディ全国ツアー</p>
  </section>
</div>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --pink: #ffd1e0;
  --pink-deep: #ff8fb3;
  --gray: #eef0f3;
  --ink: #5a4853;
}

body {
  font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", sans-serif;
  background: #fff;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* 内部スクロール領域 */
.sk-scroller {
  width: 100%;
  height: 100vh;
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--pink-deep) transparent;
}

/* ヒーロー:レイヤーを重ねる舞台 */
.sk-hero {
  position: relative;
  height: 320px;
  overflow: hidden;
}
.sk-layer {
  position: absolute;
  left: 0; right: 0;
  top: -40px;
  height: 400px;
  will-change: transform;
}
/* 奥:空のグラデ */
.sk-sky {
  background: linear-gradient(180deg, #fff6fa 0%, var(--pink) 60%, #ffe3ec 100%);
}
/* 手前:桜色の丘 */
.sk-hill {
  top: auto; bottom: -60px;
  height: 220px;
  background: radial-gradient(120% 100% at 50% 100%, var(--pink-deep) 0%, #ffb6cd 45%, transparent 70%);
  border-radius: 50% 50% 0 0 / 70% 70% 0 0;
  opacity: .85;
}
/* 花びらレイヤー(CSSのradial-gradientで散らす) */
.sk-petals {
  background-repeat: repeat;
  opacity: .8;
}
.sk-petals-a {
  background-image:
    radial-gradient(circle at 15% 20%, var(--pink-deep) 0 3px, transparent 4px),
    radial-gradient(circle at 60% 50%, #ffaecb 0 2px, transparent 3px),
    radial-gradient(circle at 85% 30%, var(--pink-deep) 0 3px, transparent 4px),
    radial-gradient(circle at 35% 75%, #ffc2d6 0 2px, transparent 3px);
  background-size: 220px 220px;
}
.sk-petals-b {
  background-image:
    radial-gradient(circle at 25% 60%, #fff 0 2px, transparent 3px),
    radial-gradient(circle at 70% 25%, var(--pink-deep) 0 4px, transparent 5px),
    radial-gradient(circle at 90% 80%, #ffaecb 0 3px, transparent 4px);
  background-size: 160px 160px;
}

/* ヒーローのコピー(レイヤーより前面、固定) */
.sk-hero-copy {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 0 20px;
}
.sk-badge {
  font-size: .62rem;
  letter-spacing: .26em;
  padding: 5px 14px;
  border-radius: 999px;
  background: #fff;
  color: var(--pink-deep);
  box-shadow: 0 8px 20px -10px rgba(255,143,179,.8);
}
.sk-hero-copy h1 {
  font-size: 1.9rem;
  font-weight: 800;
  line-height: 1.35;
  color: #c4396a;
  text-shadow: 0 2px 10px rgba(255,255,255,.7);
}
.sk-hero-copy p { font-size: .84rem; color: #8a5a70; }
.sk-arrow { margin-top: 4px; font-size: 1.4rem; color: var(--pink-deep); animation: skBob 1.6s ease-in-out infinite; }
@keyframes skBob {
  0%,100% { transform: translateY(0); opacity: .6; }
  50% { transform: translateY(8px); opacity: 1; }
}

/* 下部の公演情報 */
.sk-info {
  position: relative;
  z-index: 4;
  background: #fff;
  padding: 30px 24px 80px;
  max-width: 540px;
  margin: 0 auto;
}
.sk-info h2 {
  font-size: 1.2rem;
  font-weight: 800;
  color: #c4396a;
  text-align: center;
  margin-bottom: 16px;
}
.sk-dates { list-style: none; display: grid; gap: 10px; }
.sk-dates li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-radius: 12px;
  background: linear-gradient(90deg, #fff5f9, #fff);
  border: 1px solid var(--pink);
}
.sk-city { font-weight: 700; color: #b8456f; }
.sk-date { font-size: .86rem; color: #6b5560; }
.sk-note { font-size: .72rem; color: #9a8590; margin-top: 16px; line-height: 1.7; }
.sk-foot { text-align: center; font-size: .7rem; color: var(--pink-deep); letter-spacing: .1em; margin-top: 18px; }

@media (prefers-reduced-motion: reduce) {
  .sk-arrow { animation: none; }
}
JavaScript
// Sakura ライブ告知:スクロール量に係数を掛けて各レイヤーをずらす(パララックス)
(() => {
  const scroller = document.getElementById('plxScroller');
  const layers = Array.from(document.querySelectorAll('[data-plx]'));
  if (!scroller || !layers.length) return; // null安全

  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  function render() {
    const y = scroller.scrollTop;
    layers.forEach(el => {
      const speed = parseFloat(el.dataset.plx) || 0;
      el.style.transform = `translate3d(0, ${y * speed}px, 0)`;
    });
  }

  let ticking = false;
  scroller.addEventListener('scroll', () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => { render(); ticking = false; });
  }, { passive: true });
  render(); // 初期位置

  // 操作がなくても奥行きが伝わるよう、ゆっくり往復スクロール
  let auto = !reduce;
  let dir = 1;
  const stop = () => { auto = false; };
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
    scroller.addEventListener(ev, stop, { passive: true }));

  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      const max = scroller.scrollHeight - scroller.clientHeight;
      if (max <= 0) return;
      scroller.scrollTop += dir * 1.6;
      if (scroller.scrollTop >= max - 1) dir = -1;
      else if (scroller.scrollTop <= 1) dir = 1;
      requestAnimationFrame(step);
    }, 600);
  }
})();

コード

HTML
<!-- 自前のスクロール領域(プレビュー枠内で完結) -->
<div class="plx-scroller" id="plxScroller">
  <div class="plx-scene">
    <!-- 奥行きの異なるレイヤー(速度差でパララックス) -->
    <div class="plx-layer plx-sky"></div>
    <div class="plx-layer plx-stars" data-plx="0.15"></div>
    <div class="plx-layer plx-moon" data-plx="0.35"></div>
    <div class="plx-layer plx-hills-back" data-plx="0.55"></div>
    <div class="plx-layer plx-hills-front" data-plx="0.8"></div>

    <div class="plx-copy">
      <p class="plx-kicker">PARALLAX</p>
      <h1>夜のレイヤー</h1>
      <p class="plx-sub">スクロールで奥行きが生まれる</p>
      <span class="plx-arrow" aria-hidden="true">&#8595;</span>
    </div>
  </div>
  <div class="plx-after">
    <h2>速度差が立体感を生む</h2>
    <p>遠い層ほどゆっくり、近い層ほど速く動かすだけ。transformとwill-changeで滑らかに。</p>
  </div>
</div>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: "Segoe UI", system-ui, sans-serif;
  background: #0a0a14;
  color: #fff;
}

/* プレビュー枠を埋める自前スクロール領域 */
.plx-scroller {
  width: 100%;
  height: 100vh;
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
}

/* シーンはスクロール領域に貼り付けて固定し、奥行きだけ動かす */
.plx-scene {
  position: sticky;
  top: 0;
  height: 100vh;
  max-height: 100%;
  overflow: hidden;
}

/* 各レイヤーは絶対配置で重ねる */
.plx-layer {
  position: absolute;
  left: 0; right: 0; top: -10%;
  height: 130%;
  will-change: transform;
}

/* 空のグラデーション(固定背景) */
.plx-sky {
  background: linear-gradient(180deg, #1a1340 0%, #3a2a6d 45%, #6e4a8a 80%, #c66b7a 100%);
}

/* 星(放射状の点を複数) */
.plx-stars {
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, #fff, transparent),
    radial-gradient(1.4px 1.4px at 70% 20%, #fff, transparent),
    radial-gradient(1px 1px at 45% 50%, #fff, transparent),
    radial-gradient(1.6px 1.6px at 85% 40%, #fff, transparent),
    radial-gradient(1px 1px at 30% 65%, #fff, transparent),
    radial-gradient(1.3px 1.3px at 60% 70%, #fff, transparent);
  opacity: .85;
}

/* 月 */
.plx-moon {
  background:
    radial-gradient(circle at 78% 26%, #fff8e6 0%, #ffe9b3 38%, transparent 40%);
  filter: drop-shadow(0 0 30px rgba(255,233,179,.6));
}

/* 奥の丘 */
.plx-hills-back {
  background:
    radial-gradient(120% 70% at 20% 130%, #2c1a4d 60%, transparent 61%),
    radial-gradient(120% 80% at 80% 135%, #34205c 60%, transparent 61%);
}
/* 手前の丘 */
.plx-hills-front {
  background:
    radial-gradient(140% 80% at 50% 140%, #160d2e 60%, transparent 61%);
}

/* 中央コピー */
.plx-copy {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  text-shadow: 0 2px 18px rgba(0,0,0,.45);
}
.plx-kicker { letter-spacing: .4em; font-size: .7rem; color: #ffd9a0; }
.plx-copy h1 { font-size: 2.6rem; font-weight: 800; }
.plx-sub { font-size: .95rem; opacity: .9; }
.plx-arrow {
  margin-top: 8px;
  font-size: 1.5rem;
  animation: plxBob 1.6s ease-in-out infinite;
}
@keyframes plxBob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(8px)} }

/* スクロール後の説明 */
.plx-after {
  position: relative;
  z-index: 6;
  background: #0a0a14;
  padding: 60px 26px 90px;
  text-align: center;
  max-width: 560px;
  margin: 0 auto;
}
.plx-after h2 { font-size: 1.5rem; margin-bottom: 12px; }
.plx-after p { color: #b9b9d0; line-height: 1.7; }

@media (prefers-reduced-motion: reduce) {
  .plx-arrow { animation: none; }
}
JavaScript
// 自前スクロール領域のスクロール量に係数を掛けて各層をずらす
(() => {
  const scroller = document.getElementById('plxScroller');
  const layers = Array.from(document.querySelectorAll('[data-plx]'));
  if (!scroller || !layers.length) return; // null安全

  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  function render() {
    const y = scroller.scrollTop;
    layers.forEach(el => {
      const speed = parseFloat(el.dataset.plx) || 0;
      el.style.transform = `translate3d(0, ${y * speed}px, 0)`;
    });
  }

  let ticking = false;
  scroller.addEventListener('scroll', () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => { render(); ticking = false; });
  }, { passive: true });
  render(); // 初期位置

  // 操作がなくても奥行きが伝わるよう、ゆっくり往復スクロール
  let auto = !reduce;
  let dir = 1;
  const stop = () => { auto = false; };
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
    scroller.addEventListener(ev, stop, { passive: true }));

  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      const max = scroller.scrollHeight - scroller.clientHeight;
      if (max <= 0) return;
      scroller.scrollTop += dir * 1.6;
      if (scroller.scrollTop >= max - 1) dir = -1;
      else if (scroller.scrollTop <= 1) dir = 1;
      requestAnimationFrame(step);
    }, 600);
  }
})();

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「パララックス背景」の効果を追加してください。

# 追加してほしい効果
パララックス背景(スクロール演出)
複数レイヤーをスクロール速度差で動かし奥行きを演出します。ヒーローセクションのビジュアルに。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 自前のスクロール領域(プレビュー枠内で完結) -->
<div class="plx-scroller" id="plxScroller">
  <div class="plx-scene">
    <!-- 奥行きの異なるレイヤー(速度差でパララックス) -->
    <div class="plx-layer plx-sky"></div>
    <div class="plx-layer plx-stars" data-plx="0.15"></div>
    <div class="plx-layer plx-moon" data-plx="0.35"></div>
    <div class="plx-layer plx-hills-back" data-plx="0.55"></div>
    <div class="plx-layer plx-hills-front" data-plx="0.8"></div>

    <div class="plx-copy">
      <p class="plx-kicker">PARALLAX</p>
      <h1>夜のレイヤー</h1>
      <p class="plx-sub">スクロールで奥行きが生まれる</p>
      <span class="plx-arrow" aria-hidden="true">&#8595;</span>
    </div>
  </div>
  <div class="plx-after">
    <h2>速度差が立体感を生む</h2>
    <p>遠い層ほどゆっくり、近い層ほど速く動かすだけ。transformとwill-changeで滑らかに。</p>
  </div>
</div>

【CSS】
* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: "Segoe UI", system-ui, sans-serif;
  background: #0a0a14;
  color: #fff;
}

/* プレビュー枠を埋める自前スクロール領域 */
.plx-scroller {
  width: 100%;
  height: 100vh;
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
}

/* シーンはスクロール領域に貼り付けて固定し、奥行きだけ動かす */
.plx-scene {
  position: sticky;
  top: 0;
  height: 100vh;
  max-height: 100%;
  overflow: hidden;
}

/* 各レイヤーは絶対配置で重ねる */
.plx-layer {
  position: absolute;
  left: 0; right: 0; top: -10%;
  height: 130%;
  will-change: transform;
}

/* 空のグラデーション(固定背景) */
.plx-sky {
  background: linear-gradient(180deg, #1a1340 0%, #3a2a6d 45%, #6e4a8a 80%, #c66b7a 100%);
}

/* 星(放射状の点を複数) */
.plx-stars {
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, #fff, transparent),
    radial-gradient(1.4px 1.4px at 70% 20%, #fff, transparent),
    radial-gradient(1px 1px at 45% 50%, #fff, transparent),
    radial-gradient(1.6px 1.6px at 85% 40%, #fff, transparent),
    radial-gradient(1px 1px at 30% 65%, #fff, transparent),
    radial-gradient(1.3px 1.3px at 60% 70%, #fff, transparent);
  opacity: .85;
}

/* 月 */
.plx-moon {
  background:
    radial-gradient(circle at 78% 26%, #fff8e6 0%, #ffe9b3 38%, transparent 40%);
  filter: drop-shadow(0 0 30px rgba(255,233,179,.6));
}

/* 奥の丘 */
.plx-hills-back {
  background:
    radial-gradient(120% 70% at 20% 130%, #2c1a4d 60%, transparent 61%),
    radial-gradient(120% 80% at 80% 135%, #34205c 60%, transparent 61%);
}
/* 手前の丘 */
.plx-hills-front {
  background:
    radial-gradient(140% 80% at 50% 140%, #160d2e 60%, transparent 61%);
}

/* 中央コピー */
.plx-copy {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  text-shadow: 0 2px 18px rgba(0,0,0,.45);
}
.plx-kicker { letter-spacing: .4em; font-size: .7rem; color: #ffd9a0; }
.plx-copy h1 { font-size: 2.6rem; font-weight: 800; }
.plx-sub { font-size: .95rem; opacity: .9; }
.plx-arrow {
  margin-top: 8px;
  font-size: 1.5rem;
  animation: plxBob 1.6s ease-in-out infinite;
}
@keyframes plxBob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(8px)} }

/* スクロール後の説明 */
.plx-after {
  position: relative;
  z-index: 6;
  background: #0a0a14;
  padding: 60px 26px 90px;
  text-align: center;
  max-width: 560px;
  margin: 0 auto;
}
.plx-after h2 { font-size: 1.5rem; margin-bottom: 12px; }
.plx-after p { color: #b9b9d0; line-height: 1.7; }

@media (prefers-reduced-motion: reduce) {
  .plx-arrow { animation: none; }
}

【JavaScript】
// 自前スクロール領域のスクロール量に係数を掛けて各層をずらす
(() => {
  const scroller = document.getElementById('plxScroller');
  const layers = Array.from(document.querySelectorAll('[data-plx]'));
  if (!scroller || !layers.length) return; // null安全

  const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  function render() {
    const y = scroller.scrollTop;
    layers.forEach(el => {
      const speed = parseFloat(el.dataset.plx) || 0;
      el.style.transform = `translate3d(0, ${y * speed}px, 0)`;
    });
  }

  let ticking = false;
  scroller.addEventListener('scroll', () => {
    if (ticking) return;
    ticking = true;
    requestAnimationFrame(() => { render(); ticking = false; });
  }, { passive: true });
  render(); // 初期位置

  // 操作がなくても奥行きが伝わるよう、ゆっくり往復スクロール
  let auto = !reduce;
  let dir = 1;
  const stop = () => { auto = false; };
  ['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
    scroller.addEventListener(ev, stop, { passive: true }));

  if (auto) {
    setTimeout(function step() {
      if (!auto) return;
      const max = scroller.scrollHeight - scroller.clientHeight;
      if (max <= 0) return;
      scroller.scrollTop += dir * 1.6;
      if (scroller.scrollTop >= max - 1) dir = -1;
      else if (scroller.scrollTop <= 1) dir = 1;
      requestAnimationFrame(step);
    }, 600);
  }
})();

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。