回転3Dトーラスノット
Three.jsの標準マテリアルと三点照明で質感を出した回転オブジェクト。製品紹介やヒーローセクションの主役要素に使えます。
外部ライブラリ: https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
ライブデモ
使用例(お題: カフェ MOON BREW)
この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- MOON BREW:トーラスノットを豆に見立てたヒーロー。背景に3Dオブジェクト+前景メニューUI -->
<section class="mb-hero" aria-label="MOON BREW トップ">
<!-- 背景に浮かぶ3Dトーラスノット(豆ロースト風) -->
<canvas id="scene" class="mb-hero__canvas" aria-hidden="true"></canvas>
<!-- Three.js非対応時のフォールバック -->
<div class="mb-hero__fallback" id="mb-fallback" hidden></div>
<header class="mb-bar">
<span class="mb-logo">◐ MOON BREW</span>
<nav class="mb-nav">
<a href="#">メニュー</a>
<a href="#">豆を選ぶ</a>
<a href="#">店舗</a>
</nav>
</header>
<div class="mb-hero__body">
<span class="mb-kicker">SINGLE ORIGIN ROAST</span>
<h1 class="mb-title">毎朝に、<br>深煎りのひと粒を。</h1>
<p class="mb-lead">焙煎したての香りをそのまま。<br>今月の一杯はエチオピア・イルガチェフェ。</p>
<div class="mb-cta">
<button class="mb-btn" type="button">今月の豆を見る</button>
<span class="mb-price">¥1,180 / 200g</span>
</div>
</div>
</section>
CSS
/* MOON BREW:深煎りカラーのヒーロー */
:root {
--cream: #f5ede1;
--brown: #2b1d12;
--amber: #c98a3b;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
font-family: "Hiragino Mincho ProN", "Yu Mincho", "Segoe UI", serif;
background: var(--brown);
}
.mb-hero {
position: relative;
width: 100%;
height: 400px;
overflow: hidden;
/* 中央から温かいライトを当てる */
background:
radial-gradient(120% 90% at 70% 30%, #3a281a 0%, #221710 60%, #170f09 100%);
color: var(--cream);
}
/* 3Dキャンバスは背景全面に */
.mb-hero__canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
/* フォールバック:豆色のグラデ円 */
.mb-hero__fallback {
position: absolute;
right: 6%;
top: 50%;
width: 220px;
height: 220px;
transform: translateY(-50%);
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, var(--amber), #5a3a1d 70%, #2b1d12);
filter: blur(2px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
/* 上部バー */
.mb-bar {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 26px;
}
.mb-logo { font-size: 17px; letter-spacing: 0.14em; font-weight: 700; }
.mb-nav { display: flex; gap: 20px; }
.mb-nav a {
color: rgba(245, 237, 225, 0.82);
text-decoration: none;
font-size: 13px;
letter-spacing: 0.06em;
transition: color 0.2s ease;
}
.mb-nav a:hover { color: var(--amber); }
/* 本文(左寄せ・3Dは右側で映える) */
.mb-hero__body {
position: relative;
z-index: 2;
max-width: 440px;
padding: 34px 26px;
}
.mb-kicker {
font-size: 11px;
letter-spacing: 0.28em;
color: var(--amber);
font-family: "Segoe UI", system-ui, sans-serif;
font-weight: 600;
}
.mb-title {
margin: 14px 0 16px;
font-size: 34px;
line-height: 1.32;
font-weight: 700;
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
}
.mb-lead {
margin: 0 0 24px;
font-size: 14px;
line-height: 1.9;
color: rgba(245, 237, 225, 0.86);
}
.mb-cta { display: flex; align-items: center; gap: 18px; }
.mb-btn {
font: inherit;
font-family: "Segoe UI", system-ui, sans-serif;
font-size: 14px;
font-weight: 700;
color: var(--brown);
background: linear-gradient(135deg, #e0a85a, var(--amber));
border: none;
padding: 12px 26px;
border-radius: 999px;
cursor: pointer;
box-shadow: 0 8px 22px rgba(201, 138, 59, 0.45);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.mb-btn:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(201, 138, 59, 0.6); }
.mb-btn:active { transform: translateY(0); }
.mb-price {
font-family: "Segoe UI", system-ui, sans-serif;
font-size: 13px;
color: rgba(245, 237, 225, 0.7);
}
@media (prefers-reduced-motion: reduce) {
.mb-btn { transition: none; }
}
JavaScript
// MOON BREW ヒーロー:背景で回る琥珀色のトーラスノット(焙煎豆のイメージ)
(function () {
"use strict";
const canvas = document.getElementById("scene");
const fallback = document.getElementById("mb-fallback");
// Three.js未読込やcanvas不在なら安全にフォールバック表示
if (!canvas || typeof THREE === "undefined") {
if (fallback) fallback.hidden = false;
return;
}
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let renderer;
try {
renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
} catch (e) {
if (fallback) fallback.hidden = false;
return;
}
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 6);
// 焙煎豆のように艶のある琥珀色ノットを右側に配置
const geometry = new THREE.TorusKnotGeometry(1, 0.36, 200, 32);
const material = new THREE.MeshStandardMaterial({
color: 0xc98a3b,
metalness: 0.35,
roughness: 0.3,
});
const knot = new THREE.Mesh(geometry, material);
knot.position.x = 1.7; // 前景テキストを避けて右に寄せる
scene.add(knot);
// 温かみのある三点照明
scene.add(new THREE.AmbientLight(0x3a2616, 1.4));
const key = new THREE.DirectionalLight(0xffe6c2, 1.6);
key.position.set(4, 4, 5);
scene.add(key);
const rim = new THREE.PointLight(0xc98a3b, 1.8, 30);
rim.position.set(-4, -2, 2);
scene.add(rim);
function resize() {
const w = canvas.clientWidth || 1;
const h = canvas.clientHeight || 1;
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
resize();
window.addEventListener("resize", resize);
let raf = 0;
let running = true;
function animate() {
if (!reduceMotion) {
knot.rotation.x += 0.005;
knot.rotation.y += 0.009;
}
renderer.render(scene, camera);
raf = requestAnimationFrame(animate);
}
animate();
// タブ非表示で停止、復帰で再開
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
if (running) { cancelAnimationFrame(raf); running = false; }
} else if (!running) {
running = true;
raf = requestAnimationFrame(animate);
}
});
window.addEventListener("beforeunload", () => cancelAnimationFrame(raf));
})();
コード
HTML
<!-- 回転する3Dトーラスノット。Three.jsの基本シーン構成のデモ -->
<div class="stage">
<canvas id="scene" aria-label="回転する3Dトーラスノット"></canvas>
<div class="caption">
<span class="badge">Three.js</span>
<h2>Rotating Torus Knot</h2>
<p>ライティングとマテリアルで質感を表現した回転オブジェクト</p>
</div>
</div>
CSS
/* カスタムプロパティで配色を一元管理 */
:root {
--bg-top: #1a1340;
--bg-bottom: #06030f;
--ink: #f4f1ff;
--accent: #8b7bff;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: #06030f;
overflow: hidden;
}
.stage {
position: relative;
width: 100%;
height: 360px;
/* 放射状グラデで奥行きのある背景 */
background: radial-gradient(circle at 50% 38%, var(--bg-top), var(--bg-bottom) 72%);
}
#scene {
display: block;
width: 100%;
height: 100%;
}
.caption {
position: absolute;
left: 28px;
bottom: 24px;
color: var(--ink);
text-shadow: 0 2px 12px rgba(0, 0, 0, .6);
pointer-events: none;
}
.badge {
display: inline-block;
font-size: 11px;
letter-spacing: .14em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
background: rgba(139, 123, 255, .22);
border: 1px solid rgba(139, 123, 255, .5);
color: var(--accent);
margin-bottom: 10px;
}
.caption h2 {
font-size: 22px;
font-weight: 700;
letter-spacing: .01em;
}
.caption p {
margin-top: 4px;
font-size: 13px;
opacity: .72;
}
JavaScript
// 回転する3Dトーラスノットのデモ
(function () {
"use strict";
const canvas = document.getElementById("scene");
// Three.jsとcanvasが無ければ何もしない(null安全)
if (!canvas || typeof THREE === "undefined") return;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 5.4);
// トーラスノット本体:標準マテリアルで金属的な質感
const geometry = new THREE.TorusKnotGeometry(1, 0.34, 180, 32);
const material = new THREE.MeshStandardMaterial({
color: 0x8b7bff,
metalness: 0.55,
roughness: 0.22,
});
const knot = new THREE.Mesh(geometry, material);
scene.add(knot);
// 三点照明で立体感を演出
scene.add(new THREE.AmbientLight(0x404060, 1.2));
const key = new THREE.DirectionalLight(0xffffff, 1.4);
key.position.set(3, 4, 5);
scene.add(key);
const rim = new THREE.PointLight(0xff7ad9, 1.6, 30);
rim.position.set(-4, -2, 2);
scene.add(rim);
// コンテナサイズに追従してリサイズ
function resize() {
const w = canvas.clientWidth || 1;
const h = canvas.clientHeight || 1;
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
resize();
window.addEventListener("resize", resize);
// 描画ループ
let raf = 0;
function animate() {
knot.rotation.x += reduceMotion ? 0 : 0.006;
knot.rotation.y += reduceMotion ? 0 : 0.011;
renderer.render(scene, camera);
raf = requestAnimationFrame(animate);
}
// reduce-motionでも一度は描画する
animate();
window.addEventListener("beforeunload", () => cancelAnimationFrame(raf));
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「回転3Dトーラスノット」の効果を追加してください。
# 追加してほしい効果
回転3Dトーラスノット(WebGL / Three.js)
Three.jsの標準マテリアルと三点照明で質感を出した回転オブジェクト。製品紹介やヒーローセクションの主役要素に使えます。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 回転する3Dトーラスノット。Three.jsの基本シーン構成のデモ -->
<div class="stage">
<canvas id="scene" aria-label="回転する3Dトーラスノット"></canvas>
<div class="caption">
<span class="badge">Three.js</span>
<h2>Rotating Torus Knot</h2>
<p>ライティングとマテリアルで質感を表現した回転オブジェクト</p>
</div>
</div>
【CSS】
/* カスタムプロパティで配色を一元管理 */
:root {
--bg-top: #1a1340;
--bg-bottom: #06030f;
--ink: #f4f1ff;
--accent: #8b7bff;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: #06030f;
overflow: hidden;
}
.stage {
position: relative;
width: 100%;
height: 360px;
/* 放射状グラデで奥行きのある背景 */
background: radial-gradient(circle at 50% 38%, var(--bg-top), var(--bg-bottom) 72%);
}
#scene {
display: block;
width: 100%;
height: 100%;
}
.caption {
position: absolute;
left: 28px;
bottom: 24px;
color: var(--ink);
text-shadow: 0 2px 12px rgba(0, 0, 0, .6);
pointer-events: none;
}
.badge {
display: inline-block;
font-size: 11px;
letter-spacing: .14em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
background: rgba(139, 123, 255, .22);
border: 1px solid rgba(139, 123, 255, .5);
color: var(--accent);
margin-bottom: 10px;
}
.caption h2 {
font-size: 22px;
font-weight: 700;
letter-spacing: .01em;
}
.caption p {
margin-top: 4px;
font-size: 13px;
opacity: .72;
}
【JavaScript】
// 回転する3Dトーラスノットのデモ
(function () {
"use strict";
const canvas = document.getElementById("scene");
// Three.jsとcanvasが無ければ何もしない(null安全)
if (!canvas || typeof THREE === "undefined") return;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 5.4);
// トーラスノット本体:標準マテリアルで金属的な質感
const geometry = new THREE.TorusKnotGeometry(1, 0.34, 180, 32);
const material = new THREE.MeshStandardMaterial({
color: 0x8b7bff,
metalness: 0.55,
roughness: 0.22,
});
const knot = new THREE.Mesh(geometry, material);
scene.add(knot);
// 三点照明で立体感を演出
scene.add(new THREE.AmbientLight(0x404060, 1.2));
const key = new THREE.DirectionalLight(0xffffff, 1.4);
key.position.set(3, 4, 5);
scene.add(key);
const rim = new THREE.PointLight(0xff7ad9, 1.6, 30);
rim.position.set(-4, -2, 2);
scene.add(rim);
// コンテナサイズに追従してリサイズ
function resize() {
const w = canvas.clientWidth || 1;
const h = canvas.clientHeight || 1;
renderer.setSize(w, h, false);
camera.aspect = w / h;
camera.updateProjectionMatrix();
}
resize();
window.addEventListener("resize", resize);
// 描画ループ
let raf = 0;
function animate() {
knot.rotation.x += reduceMotion ? 0 : 0.006;
knot.rotation.y += reduceMotion ? 0 : 0.011;
renderer.render(scene, camera);
raf = requestAnimationFrame(animate);
}
// reduce-motionでも一度は描画する
animate();
window.addEventListener("beforeunload", () => cancelAnimationFrame(raf));
})();
# 外部ライブラリ
https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。