3D回転キューブ
6面を3D空間に配置したキューブが自動回転し、ドラッグで手動回転もできます。ローディングやヒーロー演出のアクセントに最適です。
ライブデモ
使用例(お題: アイドルグループ Sakura)
この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- Sakura:新譜MVを6面に貼った回転キューブのヒーロー演出 -->
<section class="sk-hero" aria-label="Sakura 新曲ヒーロー">
<div class="sk-hero__copy">
<span class="sk-hero__tag">NEW SINGLE</span>
<h1 class="sk-hero__title">花びらラプソディ</h1>
<p class="sk-hero__sub">Sakura 4th Single / 2026.06.07 配信開始</p>
<span class="sk-hero__hint">キューブをドラッグして回せます</span>
</div>
<div class="sk-stage">
<!-- 6面を3D空間に配置。自動回転+ドラッグ対応 -->
<div class="sk-cube" id="skCube">
<span class="sk-face sk-face--f">MV</span>
<span class="sk-face sk-face--b">LIVE</span>
<span class="sk-face sk-face--r">桜</span>
<span class="sk-face sk-face--l">♪</span>
<span class="sk-face sk-face--u">★</span>
<span class="sk-face sk-face--d">2026</span>
</div>
</div>
</section>
CSS
/* Sakura:桜ピンクの回転キューブ・ヒーロー */
:root {
--pink: #ffd1e0;
--pink-deep: #ff8fb3;
--gray: #eef0f3;
--size: 130px;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
background:
radial-gradient(circle at 80% 20%, #fff 0%, var(--pink) 55%, #ffc0d8 100%);
color: #7a3553;
overflow: hidden;
}
.sk-hero { display: contents; }
/* 左:コピー */
.sk-hero__copy { padding: 0 12px 0 30px; }
.sk-hero__tag {
display: inline-block;
font-size: 11px; letter-spacing: 0.24em; font-weight: 800;
color: #fff; background: linear-gradient(135deg, #ff9cc0, var(--pink-deep));
padding: 5px 12px; border-radius: 999px;
box-shadow: 0 6px 14px rgba(255,122,168,.4);
}
.sk-hero__title {
margin: 14px 0 6px; font-size: 30px; font-weight: 900;
letter-spacing: 0.02em; color: #c94d7d;
}
.sk-hero__sub { margin: 0 0 14px; font-size: 12.5px; color: #9a6076; line-height: 1.7; }
.sk-hero__hint { font-size: 11px; color: #b07089; }
/* 右:3Dステージ。perspective が立体の肝 */
.sk-stage {
height: 100%;
display: grid;
place-items: center;
perspective: 800px;
}
.sk-cube {
position: relative;
width: var(--size);
height: var(--size);
transform-style: preserve-3d; /* 6面を3D空間に */
transform: rotateX(-18deg) rotateY(0deg);
cursor: grab;
}
.sk-cube:active { cursor: grabbing; }
.sk-face {
position: absolute;
width: var(--size);
height: var(--size);
display: grid;
place-items: center;
font-size: 28px;
font-weight: 900;
letter-spacing: 0.05em;
color: #fff;
border-radius: 14px;
border: 2px solid rgba(255,255,255,.7);
background: linear-gradient(150deg, rgba(255,156,192,.92), rgba(255,138,177,.92));
box-shadow: inset 0 0 24px rgba(255,255,255,.35);
backface-visibility: visible;
}
/* 面ごとに濃淡を変えて立体感 */
.sk-face--f { transform: translateZ(65px); }
.sk-face--b { transform: rotateY(180deg) translateZ(65px); font-size: 20px; }
.sk-face--r { transform: rotateY(90deg) translateZ(65px); background: linear-gradient(150deg, #ffb0cf, #ff96bb); }
.sk-face--l { transform: rotateY(-90deg) translateZ(65px); background: linear-gradient(150deg, #ffb0cf, #ff96bb); }
.sk-face--u { transform: rotateX(90deg) translateZ(65px); background: linear-gradient(150deg, #ffc4da, #ffa6c5); }
.sk-face--d { transform: rotateX(-90deg) translateZ(65px); font-size: 18px; background: linear-gradient(150deg, #ffc4da, #ffa6c5); }
@media (prefers-reduced-motion: reduce) {
.sk-cube { animation: none; }
}
JavaScript
// Sakura 回転キューブ:自動回転+ドラッグで手動回転
(() => {
const cube = document.getElementById("skCube");
if (!cube) return; // null安全
let rotX = -18, rotY = 0; // 現在の回転角(度)
let auto = 0.4; // 自動回転速度
let dragging = false, lastX = 0, lastY = 0;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// 角度を反映
const apply = () => {
cube.style.transform = `rotateX(${rotX}deg) rotateY(${rotY}deg)`;
};
const onDown = (e) => {
dragging = true; auto = 0;
lastX = e.clientX; lastY = e.clientY;
cube.setPointerCapture?.(e.pointerId);
};
const onMove = (e) => {
if (!dragging) return;
rotY += (e.clientX - lastX) * 0.5;
rotX -= (e.clientY - lastY) * 0.5;
lastX = e.clientX; lastY = e.clientY;
apply();
};
const onUp = () => {
if (!dragging) return;
dragging = false;
// 手を離したら少し待って自動回転を再開
setTimeout(() => { if (!dragging && !reduce) auto = 0.4; }, 1000);
};
cube.addEventListener("pointerdown", onDown);
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
// 自動回転ループ
const tick = () => {
if (auto) { rotY += auto; apply(); }
requestAnimationFrame(tick);
};
apply();
if (!reduce) tick();
})();
コード
HTML
<div class="cube-wrap" aria-label="3D回転キューブのデモ">
<div class="stage">
<!-- 6面を持つキューブ。自動回転+ドラッグで手動回転 -->
<div class="cube" id="cube">
<div class="cube__face cube__face--front">前</div>
<div class="cube__face cube__face--back">後</div>
<div class="cube__face cube__face--right">右</div>
<div class="cube__face cube__face--left">左</div>
<div class="cube__face cube__face--top">上</div>
<div class="cube__face cube__face--bottom">下</div>
</div>
</div>
<p class="cube-hint">ドラッグで回転</p>
</div>
CSS
/* ===== 3D回転キューブ ===== */
:root {
--size: 130px; /* キューブの辺の長さ */
--half: calc(var(--size) / 2);
--hue: 265;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 360px;
display: grid;
place-items: center;
font-family: "Segoe UI", system-ui, sans-serif;
background:
radial-gradient(circle at 50% 120%, hsl(var(--hue) 60% 22%), transparent 60%),
linear-gradient(160deg, #0e0b1e 0%, #05040c 100%);
overflow: hidden;
user-select: none;
}
.cube-wrap { display: grid; place-items: center; gap: 18px; }
.stage {
width: var(--size);
height: var(--size);
perspective: 720px; /* 視点までの距離 */
}
.cube {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
/* JSがCSS変数 --rx / --ry を更新して回転 */
transform: rotateX(var(--rx, -22deg)) rotateY(var(--ry, 32deg));
transition: transform .08s linear;
}
.cube.is-dragging { transition: none; cursor: grabbing; }
.cube__face {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-size: 34px;
font-weight: 800;
color: #fff;
border: 1px solid hsl(var(--hue) 90% 75% / .55);
background: hsl(var(--hue) 70% 55% / .18);
backdrop-filter: blur(2px);
box-shadow: inset 0 0 30px hsl(var(--hue) 90% 70% / .25);
}
.cube__face::after {
content: "";
position: absolute; inset: 0;
background: linear-gradient(135deg, hsl(var(--hue) 90% 80% / .25), transparent 60%);
}
/* 各面を3D空間に配置 */
.cube__face--front { transform: translateZ(var(--half)); }
.cube__face--back { transform: rotateY(180deg) translateZ(var(--half)); }
.cube__face--right { transform: rotateY(90deg) translateZ(var(--half)); }
.cube__face--left { transform: rotateY(-90deg) translateZ(var(--half)); }
.cube__face--top { transform: rotateX(90deg) translateZ(var(--half)); }
.cube__face--bottom { transform: rotateX(-90deg) translateZ(var(--half)); }
.cube-hint {
margin: 0;
font-size: 12px;
letter-spacing: .2em;
color: hsl(var(--hue) 50% 80% / .7);
}
@media (prefers-reduced-motion: reduce) {
.cube { transition: none; }
}
JavaScript
// 3D回転キューブ: 自動回転 + ポインタードラッグで手動操作
(() => {
const cube = document.getElementById('cube');
if (!cube) return; // null安全
let rx = -22; // X軸回転(度)
let ry = 32; // Y軸回転(度)
let autoSpin = true; // 自動回転フラグ
let dragging = false;
let lastX = 0, lastY = 0;
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const apply = () => {
cube.style.setProperty('--rx', rx.toFixed(2) + 'deg');
cube.style.setProperty('--ry', ry.toFixed(2) + 'deg');
};
// 自動回転ループ(reduced-motion時は止める)
const tick = () => {
if (autoSpin && !reduce) {
ry += 0.45;
apply();
}
requestAnimationFrame(tick);
};
const onDown = (e) => {
dragging = true;
autoSpin = false;
cube.classList.add('is-dragging');
lastX = e.clientX;
lastY = e.clientY;
cube.setPointerCapture?.(e.pointerId);
};
const onMove = (e) => {
if (!dragging) return;
ry += (e.clientX - lastX) * 0.5;
rx -= (e.clientY - lastY) * 0.5;
rx = Math.max(-90, Math.min(90, rx)); // 上下の回転を制限
lastX = e.clientX;
lastY = e.clientY;
apply();
};
const onUp = () => {
if (!dragging) return;
dragging = false;
cube.classList.remove('is-dragging');
// 少し待ってから自動回転を再開
setTimeout(() => { if (!dragging) autoSpin = true; }, 1400);
};
cube.addEventListener('pointerdown', onDown);
window.addEventListener('pointermove', onMove);
window.addEventListener('pointerup', onUp);
apply();
requestAnimationFrame(tick);
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「3D回転キューブ」の効果を追加してください。
# 追加してほしい効果
3D回転キューブ(3D & パースペクティブ)
6面を3D空間に配置したキューブが自動回転し、ドラッグで手動回転もできます。ローディングやヒーロー演出のアクセントに最適です。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<div class="cube-wrap" aria-label="3D回転キューブのデモ">
<div class="stage">
<!-- 6面を持つキューブ。自動回転+ドラッグで手動回転 -->
<div class="cube" id="cube">
<div class="cube__face cube__face--front">前</div>
<div class="cube__face cube__face--back">後</div>
<div class="cube__face cube__face--right">右</div>
<div class="cube__face cube__face--left">左</div>
<div class="cube__face cube__face--top">上</div>
<div class="cube__face cube__face--bottom">下</div>
</div>
</div>
<p class="cube-hint">ドラッグで回転</p>
</div>
【CSS】
/* ===== 3D回転キューブ ===== */
:root {
--size: 130px; /* キューブの辺の長さ */
--half: calc(var(--size) / 2);
--hue: 265;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 360px;
display: grid;
place-items: center;
font-family: "Segoe UI", system-ui, sans-serif;
background:
radial-gradient(circle at 50% 120%, hsl(var(--hue) 60% 22%), transparent 60%),
linear-gradient(160deg, #0e0b1e 0%, #05040c 100%);
overflow: hidden;
user-select: none;
}
.cube-wrap { display: grid; place-items: center; gap: 18px; }
.stage {
width: var(--size);
height: var(--size);
perspective: 720px; /* 視点までの距離 */
}
.cube {
position: relative;
width: 100%;
height: 100%;
transform-style: preserve-3d;
/* JSがCSS変数 --rx / --ry を更新して回転 */
transform: rotateX(var(--rx, -22deg)) rotateY(var(--ry, 32deg));
transition: transform .08s linear;
}
.cube.is-dragging { transition: none; cursor: grabbing; }
.cube__face {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-size: 34px;
font-weight: 800;
color: #fff;
border: 1px solid hsl(var(--hue) 90% 75% / .55);
background: hsl(var(--hue) 70% 55% / .18);
backdrop-filter: blur(2px);
box-shadow: inset 0 0 30px hsl(var(--hue) 90% 70% / .25);
}
.cube__face::after {
content: "";
position: absolute; inset: 0;
background: linear-gradient(135deg, hsl(var(--hue) 90% 80% / .25), transparent 60%);
}
/* 各面を3D空間に配置 */
.cube__face--front { transform: translateZ(var(--half)); }
.cube__face--back { transform: rotateY(180deg) translateZ(var(--half)); }
.cube__face--right { transform: rotateY(90deg) translateZ(var(--half)); }
.cube__face--left { transform: rotateY(-90deg) translateZ(var(--half)); }
.cube__face--top { transform: rotateX(90deg) translateZ(var(--half)); }
.cube__face--bottom { transform: rotateX(-90deg) translateZ(var(--half)); }
.cube-hint {
margin: 0;
font-size: 12px;
letter-spacing: .2em;
color: hsl(var(--hue) 50% 80% / .7);
}
@media (prefers-reduced-motion: reduce) {
.cube { transition: none; }
}
【JavaScript】
// 3D回転キューブ: 自動回転 + ポインタードラッグで手動操作
(() => {
const cube = document.getElementById('cube');
if (!cube) return; // null安全
let rx = -22; // X軸回転(度)
let ry = 32; // Y軸回転(度)
let autoSpin = true; // 自動回転フラグ
let dragging = false;
let lastX = 0, lastY = 0;
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const apply = () => {
cube.style.setProperty('--rx', rx.toFixed(2) + 'deg');
cube.style.setProperty('--ry', ry.toFixed(2) + 'deg');
};
// 自動回転ループ(reduced-motion時は止める)
const tick = () => {
if (autoSpin && !reduce) {
ry += 0.45;
apply();
}
requestAnimationFrame(tick);
};
const onDown = (e) => {
dragging = true;
autoSpin = false;
cube.classList.add('is-dragging');
lastX = e.clientX;
lastY = e.clientY;
cube.setPointerCapture?.(e.pointerId);
};
const onMove = (e) => {
if (!dragging) return;
ry += (e.clientX - lastX) * 0.5;
rx -= (e.clientY - lastY) * 0.5;
rx = Math.max(-90, Math.min(90, rx)); // 上下の回転を制限
lastX = e.clientX;
lastY = e.clientY;
apply();
};
const onUp = () => {
if (!dragging) return;
dragging = false;
cube.classList.remove('is-dragging');
// 少し待ってから自動回転を再開
setTimeout(() => { if (!dragging) autoSpin = true; }, 1400);
};
cube.addEventListener('pointerdown', onDown);
window.addEventListener('pointermove', onMove);
window.addEventListener('pointerup', onUp);
apply();
requestAnimationFrame(tick);
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。