パララックス背景
複数レイヤーをスクロール速度差で動かし奥行きを演出します。ヒーローセクションのビジュアルに。
ライブデモ
使用例(お題: アイドルグループ 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">↓</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">↓</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">↓</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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。