lightweight-3d-effects について
Lightweight 3D Effects Skill は、Zdog、Vanta.js、Vanilla-Tilt.js を使い、Web ページに装飾的な 3D グラフィックやアニメーション背景、傾きのインタラクションを加えます。ページやカードに奥行きを加えたいデザイナーや開発者向けです。
主な機能
- 01Zdog による擬似 3D ベクターイラスト
- 02Canvas または SVG での描画
- 03操作可能な Vanta.js アニメーション背景
- 04Vanilla-Tilt.js による傾きと視差効果
- 05ジャイロスコープとグレア効果
活用シーン
- 01ランディングページのヒーローに動く背景を追加
- 02ドラッグ回転できる装飾イラストの作成
- 03カードや画像に傾きのインタラクションを追加
- 04背景エフェクトと傾くカードを組み合わせる
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ freshtechbro/ claudedesignskills --skill lightweight-3d-effectsインストール後、Claude Code で/lightweight-3d-effectsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/examples/README.mdassets/starter_lightweight/index.htmlmain.jsREADME.mdstyle.cssreferences/tilt_patterns.mdvantajs_effects.mdzdog_api.mdscripts/generate_zdog.pysetup_vanta.py詳細
軽量な 3D エフェクトスキル
概要
このスキルでは、装飾的な 3D 要素やマイクロインタラクション向けに、強力な 3 つのライブラリを組み合わせます。
- Zdog:デザイナーに使いやすいベクターイラスト向けの疑似 3D エンジン
- Vanta.js:Three.js/p5.js を活用したアニメーション 3D 背景
- Vanilla-Tilt.js:マウスやジャイロスコープに反応する滑らかな視差チルトエフェクト
このスキルを使用する場面
- 重いフレームワークを使わずに装飾的な 3D イラストを追加する
- ヒーローセクションにアニメーション背景を作成する
- カードや画像に控えめな視差チルトエフェクトを実装する
- 視覚的な奥行きのある軽量なランディングページを構築する
- パフォーマンスに影響を与えず、UX を高めるマイクロインタラクションを追加する
Zdog - 疑似 3D イラスト
基本概念
Zdog は、Canvas または SVG を使用して、平面的で丸みのあるデザインを 3D 空間に描画する疑似 3D エンジンです。
主な機能:
- デザイナーに使いやすい宣言型 API
- 小さなファイルサイズ(圧縮時に約 28kb)
- Canvas または SVG による描画
- ドラッグによる回転機能を内蔵
- 滑らかなアニメーション
基本設定
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<style>
.zdog-canvas {
display: block;
margin: 0 auto;
background: #FDB;
cursor: move;
}
</style>
</head>
<body>
<canvas class="zdog-canvas" width="240" height="240"></canvas>
<script>
let isSpinning = true;
let illo = new Zdog.Illustration({
element: '.zdog-canvas',
zoom: 4,
dragRotate: true,
onDragStart: function() {
isSpinning = false;
},
});
// Add shapes
new Zdog.Ellipse({
addTo: illo,
diameter: 20,
translate: { z: 10 },
stroke: 5,
color: '#636',
});
new Zdog.Rect({
addTo: illo,
width: 20,
height: 20,
translate: { z: -10 },
stroke: 3,
color: '#E62',
fill: true,
});
function animate() {
illo.rotate.y += isSpinning ? 0.03 : 0;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Zdog の図形
基本図形:
// Circle
new Zdog.Ellipse({
addTo: illo,
diameter: 80,
stroke: 20,
color: '#636',
});
// Rectangle
new Zdog.Rect({
addTo: illo,
width: 80,
height: 60,
stroke: 10,
color: '#E62',
fill: true,
});
// Rounded Rectangle
new Zdog.RoundedRect({
addTo: illo,
width: 60,
height: 40,
cornerRadius: 10,
stroke: 4,
color: '#C25',
fill: true,
});
// Polygon
new Zdog.Polygon({
addTo: illo,
radius: 40,
sides: 5,
stroke: 8,
color: '#EA0',
fill: true,
});
// Line
new Zdog.Shape({
addTo: illo,
path: [
{ x: -40, y: 0 },
{ x: 40, y: 0 },
],
stroke: 6,
color: '#636',
});
// Bezier Curve
new Zdog.Shape({
addTo: illo,
path: [
{ x: -40, y: -20 },
{
bezier: [
{ x: -40, y: 20 },
{ x: 40, y: 20 },
{ x: 40, y: -20 },
],
},
],
stroke: 4,
color: '#C25',
closed: false,
});
Zdog のグループ
複雑なモデルを作成するには、図形をグループにまとめます。
// Create a group
let head = new Zdog.Group({
addTo: illo,
translate: { y: -40 },
});
// Add shapes to group
new Zdog.Ellipse({
addTo: head,
diameter: 60,
stroke: 30,
color: '#FED',
});
// Eyes
new Zdog.Ellipse({
addTo: head,
diameter: 8,
stroke: 4,
color: '#333',
translate: { x: -10, z: 15 },
});
new Zdog.Ellipse({
addTo: head,
diameter: 8,
stroke: 4,
color: '#333',
translate: { x: 10, z: 15 },
});
// Mouth
new Zdog.Shape({
addTo: head,
path: [
{ x: -10, y: 0 },
{
bezier: [
{ x: -5, y: 5 },
{ x: 5, y: 5 },
{ x: 10, y: 0 },
],
},
],
stroke: 2,
color: '#333',
translate: { y: 5, z: 15 },
closed: false,
});
// Rotate entire group
head.rotate.y = Math.PI / 4;
Zdog のアニメーション
// Continuous rotation
function animate() {
illo.rotate.y += 0.03;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
// Bounce animation
let t = 0;
function bounceAnimate() {
t += 0.05;
illo.translate.y = Math.sin(t) * 20;
illo.updateRenderGraph();
requestAnimationFrame(bounceAnimate);
}
bounceAnimate();
// Interactive rotation with easing
let targetRotateY = 0;
let currentRotateY = 0;
document.addEventListener('mousemove', (event) => {
targetRotateY = (event.clientX / window.innerWidth - 0.5) * Math.PI;
});
function smoothAnimate() {
// Ease towards target
currentRotateY += (targetRotateY - currentRotateY) * 0.1;
illo.rotate.y = currentRotateY;
illo.updateRenderGraph();
requestAnimationFrame(smoothAnimate);
}
smoothAnimate();
Vanta.js - アニメーション 3D 背景
基本概念
Vanta.js は、Three.js または p5.js を利用した、最小限の設定で使えるアニメーション WebGL 背景を提供します。
主な機能:
- 14 種類以上のアニメーションエフェクト(Waves、Birds、Net、Clouds など)
- マウスやタッチ操作に対応
- 色や設定をカスタマイズ可能
- 合計約 120KB(Three.js を含む)
- ほとんどのデバイスで 60fps
基本設定
<!DOCTYPE html>
<html>
<head>
<style>
#vanta-bg {
width: 100%;
height: 100vh;
}
.content {
position: relative;
z-index: 1;
color: white;
text-align: center;
padding: 100px 20px;
}
</style>
</head>
<body>
<div id="vanta-bg">
<div class="content">
<h1>My Animated Background</h1>
<p>Content goes here</p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanta.waves.min.js"></script>
<script>
VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x23153c,
shininess: 30.00,
waveHeight: 15.00,
waveSpeed: 0.75,
zoom: 0.65
});
</script>
</body>
</html>
利用可能なエフェクト
1. WAVES(Three.js)
VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c,
shininess: 30,
waveHeight: 15,
waveSpeed: 0.75,
zoom: 0.65
});
2. CLOUDS(Three.js)
VANTA.CLOUDS({
el: "#vanta-bg",
skyColor: 0x68b8d7,
cloudColor: 0xadc1de,
cloudShadowColor: 0x183550,
sunColor: 0xff9919,
sunGlareColor: 0xff6633,
sunlightColor: 0xff9933,
speed: 1.0
});
3. BIRDS(p5.js が必要)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanta.birds.min.js"></script>
<script>
VANTA.BIRDS({
el: "#vanta-bg",
backgroundColor: 0x23153c,
color1: 0xff0000,
color2: 0x0000ff,
birdSize: 1.5,
wingSpan: 20,
speedLimit: 5,
separation: 40,
alignment: 40,
cohesion: 40,
quantity: 3
});
</script>
4. NET(Three.js)
VANTA.NET({
el: "#vanta-bg",
color: 0x3fff00,
backgroundColor: 0x23153c,
points: 10,
maxDistance: 20,
spacing: 15,
showDots: true
});
5. CELLS(p5.js が必要)
VANTA.CELLS({
el: "#vanta-bg",
color1: 0x00ff00,
color2: 0xff0000,
size: 1.5,
speed: 1.0,
scale: 1.0
});
6. FOG(Three.js)
VANTA.FOG({
el: "#vanta-bg",
highlightColor: 0xff3f81,
midtoneColor: 0x1d004d,
lowlightColor: 0x2b1a5e,
baseColor: 0x000000,
blurFactor: 0.6,
speed: 1.0,
zoom: 1.0
});
その他のエフェクト: GLOBE、TRUNK、TOPOLOGY、DOTS、HALO、RINGS
設定オプション
// Common options for all effects
{
el: "#element-id", // Required: target element
mouseControls: true, // Enable mouse interaction
touchControls: true, // Enable touch interaction
gyroControls: false, // Device orientation
minHeight: 200.00, // Minimum height
minWidth: 200.00, // Minimum width
scale: 1.00, // Size scale
scaleMobile: 1.00, // Mobile scale
// Colors (hex numbers, not strings)
color: 0x23153c,
backgroundColor: 0x000000,
// Performance
forceAnimate: false, // Force animation even when hidden
// Effect-specific options vary by effect
}
Vanta.js のメソッド
// Initialize and store reference
const vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
// ... options
});
// Destroy when done (important for SPAs)
vantaEffect.destroy();
// Update options dynamically
vantaEffect.setOptions({
color: 0xff0000,
waveHeight: 20
});
// Resize (usually automatic)
vantaEffect.resize();
React との統合
import { useEffect, useRef, useState } from 'react';
import VANTA from 'vanta/dist/vanta.waves.min';
import * as THREE from 'three';
function VantaBackground() {
const vantaRef = useRef(null);
const [vantaEffect, setVantaEffect] = useState(null);
useEffect(() => {
if (!vantaEffect) {
setVantaEffect(VANTA.WAVES({
el: vantaRef.current,
THREE: THREE,
mouseControls: true,
touchControls: true,
color: 0x23153c,
shininess: 30,
waveHeight: 15,
waveSpeed: 0.75
}));
}
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, [vantaEffect]);
return (
<div ref={vantaRef} style={{ width: '100%', height: '100vh' }}>
<div className="content">
<h1>React + Vanta.js</h1>
</div>
</div>
);
}
Vanilla-Tilt.js - 視差チルトエフェクト
基本概念
Vanilla-Tilt.js は、マウスの動きやデバイスの向きに反応する、滑らかな 3D チルトエフェクトを追加します。
主な機能:
- 軽量(圧縮時に約 8.5kb)
- 依存関係なし
- ジャイロスコープに対応
- グレアエフェクトをオプションで追加可能
- 滑らかなトランジション
基本設定
<!DOCTYPE html>
<html>
<head>
<style>
.tilt-card {
width: 300px;
height: 400px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
margin: 50px auto;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
transform-style: preserve-3d;
}
.tilt-inner {
transform: translateZ(60px);
}
</style>
</head>
<body>
<div class="tilt-card" data-tilt>
<div class="tilt-inner">Hover Me!</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanilla-tilt.min.js"></script>
</body>
</html>
設定オプション
VanillaTilt.init(document.querySelector(".tilt-card"), {
// Rotation
max: 25, // Max tilt angle (degrees)
reverse: false, // Reverse tilt direction
startX: 0, // Initial tilt X (degrees)
startY: 0, // Initial tilt Y (degrees)
// Appearance
perspective: 1000, // Transform perspective (lower = more intense)
scale: 1.1, // Scale on hover (1 = no scale)
// Animation
speed: 400, // Transition speed (ms)
transition: true, // Enable smooth transitions
easing: "cubic-bezier(.03,.98,.52,.99)",
// Behavior
axis: null, // Restrict to "x" or "y" axis
reset: true, // Reset on mouse leave
"reset-to-start": true, // Reset to start position vs [0,0]
// Glare effect
glare: true, // Enable glare
"max-glare": 0.5, // Glare opacity (0-1)
"glare-prerender": false, // Pre-render glare elements
// Advanced
full-page-listening: false, // Listen to entire page
gyroscope: true, // Enable device orientation
gyroscopeMinAngleX: -45, // Min X angle
gyroscopeMaxAngleX: 45, // Max X angle
gyroscopeMinAngleY: -45, // Min Y angle
gyroscopeMaxAngleY: 45, // Max Y angle
gyroscopeSamples: 10 // Calibration samples
});
高度な例
グレアエフェクト付きカード:
<div class="tilt-card" data-tilt
data-tilt-glare
data-tilt-max-glare="0.5"
data-tilt-scale="1.1">
<div class="tilt-inner">
<h3>Premium Card</h3>
<p>With glare effect</p>
</div>
</div>
レイヤー化された 3D エフェクト:
<style>
.tilt-card {
transform-style: preserve-3d;
}
.layer-1 {
transform: translateZ(20px);
}
.layer-2 {
transform: translateZ(40px);
}
.layer-3 {
transform: translateZ(60px);
}
</style>
<div class="tilt-card" data-tilt data-tilt-max="15">
<div class="layer-1">Background</div>
<div class="layer-2">Middle</div>
<div class="layer-3">Front</div>
</div>
プログラムによる制御:
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element, {
max: 25,
speed: 400,
glare: true,
"max-glare": 0.5
});
// Get tilt values
element.addEventListener("tiltChange", (e) => {
console.log("Tilt:", e.detail);
});
// Reset programmatically
element.vanillaTilt.reset();
// Destroy instance
element.vanillaTilt.destroy();
// Get current values
const values = element.vanillaTilt.getValues();
console.log(values); // { tiltX, tiltY, percentageX, percentageY, angle }
React との統合
import { useEffect, useRef } from 'react';
import VanillaTilt from 'vanilla-tilt';
function TiltCard({ children, options }) {
const tiltRef = useRef(null);
useEffect(() => {
const element = tiltRef.current;
VanillaTilt.init(element, {
max: 25,
speed: 400,
glare: true,
"max-glare": 0.5,
...options
});
return () => {
element.vanillaTilt.destroy();
};
}, [options]);
return (
<div ref={tiltRef} className="tilt-card">
{children}
</div>
);
}
// Usage
<TiltCard options={{ max: 30, scale: 1.1 }}>
<h3>My Card</h3>
</TiltCard>
よくあるパターン
パターン 1:Vanta とコンテンツを組み合わせたヒーローセクション
<section id="hero">
<div class="hero-content">
<h1>Welcome</h1>
<p>Animated background with content overlay</p>
<button>Get Started</button>
</div>
</section>
<style>
#hero {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
}
.hero-content {
position: relative;
z-index: 1;
color: white;
text-align: center;
padding-top: 20vh;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanta.waves.min.js"></script>
<script>
VANTA.WAVES({
el: "#hero",
mouseControls: true,
touchControls: true,
color: 0x23153c,
waveHeight: 20,
waveSpeed: 1.0
});
</script>
パターン 2:Zdog アイコンのグリッド
<div class="icon-grid">
<canvas class="icon" width="120" height="120"></canvas>
<canvas class="icon" width="120" height="120"></canvas>
<canvas class="icon" width="120" height="120"></canvas>
</div>
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<script>
document.querySelectorAll('.icon').forEach((canvas, index) => {
let illo = new Zdog.Illustration({
element: canvas,
zoom: 3,
dragRotate: true
});
// Create different icon for each canvas
const icons = [
createHeartIcon,
createStarIcon,
createCheckIcon
];
icons[index](illo);
function animate() {
illo.rotate.y += 0.02;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
});
function createHeartIcon(illo) {
new Zdog.Shape({
addTo: illo,
path: [
{ x: 0, y: -10 },
{
bezier: [
{ x: -20, y: -20 },
{ x: -20, y: 0 },
{ x: 0, y: 10 }
]
},
{
bezier: [
{ x: 20, y: 0 },
{ x: 20, y: -20 },
{ x: 0, y: -10 }
]
}
],
stroke: 6,
color: '#E62',
fill: true,
closed: false
});
}
</script>
パターン 3:チルトカードのギャラリー
<div class="card-gallery">
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product1.jpg" alt="Product 1">
<h3>Product 1</h3>
</div>
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product2.jpg" alt="Product 2">
<h3>Product 2</h3>
</div>
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product3.jpg" alt="Product 3">
<h3>Product 3</h3>
</div>
</div>
<style>
.card-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
padding: 50px;
}
.card {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
transform-style: preserve-3d;
}
.card img {
width: 100%;
border-radius: 10px;
transform: translateZ(40px);
}
.card h3 {
margin-top: 15px;
transform: translateZ(60px);
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vanilla-tilt.min.js"></script>
パターン 4:複合エフェクト - Vanta の背景とチルトカード
<div id="vanta-section">
<div class="container">
<h1>Our Services</h1>
<div class="services-grid">
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">🚀</div>
<h3>Fast</h3>
<p>Lightning quick performance</p>
</div>
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">🎨</div>
<h3>Beautiful</h3>
<p>Stunning visual design</p>
</div>
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">💪</div>
<h3>Powerful</h3>
<p>Feature-rich platform</p>
</div>
</div>
</div>
</div>
<script>
// Vanta background
VANTA.NET({
el: "#vanta-section",
color: 0x3fff00,
backgroundColor: 0x23153c,
points: 10,
maxDistance: 20
});
// Tilt cards
VanillaTilt.init(document.querySelectorAll(".service-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.3
});
</script>
パフォーマンスに関するベストプラクティス
Zdog の最適化
- 図形の数を制限する:滑らかな 60fps を維持するため、図形の合計数を 100 未満にします
- グループを使用する:関連する図形をまとめると、管理しやすくなります
- アニメーションループを最適化する:必要な場合にのみ
updateRenderGraph()を呼び出します - Canvas と SVG の使い分け:アニメーションには Canvas の方が高速で、静的なイラストには SVG が適しています
Vanta.js の最適化
- インスタンスを 1 つにする:1 ページにつき Vanta エフェクトは 1~2 個だけ使用します
- モバイル向けの代替手段を用意する:モバイルでは無効にするか、静的な背景を使用します
- アンマウント時に破棄する:SPA では必ず
.destroy()を呼び出します - パーティクル数を減らす:パフォーマンス向上のため、
pointsとquantityの値を下げます
// Mobile detection and fallback
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile) {
VANTA.WAVES({
el: "#hero",
// ... options
});
} else {
document.getElementById('hero').style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
}
Vanilla-Tilt の最適化
- インスタンス数を制限する:表示中の要素にのみ適用します
gyroscopeSamplesの値を下げる:モバイルのパフォーマンス向上のため、値を下げます- ローエンドデバイスでは無効にする:デバイスの性能を確認します
- CSS の
will-changeを使用する:変換が行われることをブラウザーに知らせます
.tilt-card {
will-change: transform;
}
よくある落とし穴
落とし穴 1:Vanta のインスタンスが複数ある
問題:Vanta エフェクトが複数あると、パフォーマンスの問題が発生します
解決策:エフェクトは 1 つだけ使用するか、セクションごとに遅延読み込みします
// Intersection Observer to load Vanta only when visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !entry.target.vantaEffect) {
entry.target.vantaEffect = VANTA.WAVES({
el: entry.target,
// ... options
});
}
});
});
observer.observe(document.getElementById('hero'));
落とし穴 2:SPA でのメモリリーク
問題:コンポーネントのアンマウント時に Vanta/Tilt が破棄されない
解決策:必ずクリーンアップします
// React useEffect cleanup
useEffect(() => {
const effect = VANTA.WAVES({ el: vantaRef.current });
return () => {
effect.destroy(); // Important!
};
}, []);
落とし穴 3:Zdog がレンダリングされない
問題:キャンバスが真っ白に見える
原因:
updateRenderGraph()の呼び出しを忘れている- キャンバスのサイズが 0 になっている
- 図形が表示範囲外にある
解決策:
// Always call updateRenderGraph after shape changes
illo.updateRenderGraph();
// Ensure canvas has dimensions
<canvas width="240" height="240"></canvas>
// Check shape positions are visible
new Zdog.Ellipse({
addTo: illo,
diameter: 20,
translate: { z: 0 }, // Keep close to origin
});
落とし穴 4:モバイルで Tilt が動作しない
問題:モバイルデバイスで Tilt が反応しない
解決策:ジャイロスコープコントロールを有効にします
VanillaTilt.init(element, {
gyroscope: true,
gyroscopeMinAngleX: -45,
gyroscopeMaxAngleX: 45
});
落とし穴 5:色の形式の混乱(Vanta.js)
問題:色が正しく機能しない
原因:Vanta.js では文字列ではなく、16 進数の数値を使用します
// ❌ Wrong
color: "#23153c"
// ✅ Correct
color: 0x23153c
リソース
Zdog:
Vanta.js:
Vanilla-Tilt.js:
関連スキル
- threejs-webgl:装飾効果を超える、より複雑な 3D グラフィックス向け
- gsap-scrolltrigger:スクロール時にこれらの効果をアニメーション化する場合
- motion-framer:これらの効果と併用する React コンポーネントのアニメーション向け
- react-three-fiber:軽量な効果では不十分な場合に使う、高度な 3D