web3d-integration-patterns について
Web アプリで 3D 描画ライブラリとアニメーションライブラリを組み合わせる開発者向けのメタスキルです。アーキテクチャ、状態管理、パフォーマンス最適化、統合例を扱います。
主な機能
- 01Three.js、GSAP、React の組み合わせパターン
- 02React Three Fiber のアニメーション手法
- 03ライブラリ間の状態管理戦略
- 04パフォーマンスと描画ループの最適化
- 05統合例とスキャフォールド用リソース
活用シーン
- 01スクロール連動型 3D Web 体験の構築
- 02物理ベースのシーン操作の作成
- 033D シーンと UI 状態の連携
- 04複数ライブラリ構成の選定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ freshtechbro/ claudedesignskills --skill web3d-integration-patternsインストール後、Claude Code で/web3d-integration-patternsを入力して読み込まれたか確認します。
詳細
Web 3D 統合パターン
概要
このメタスキルでは、Web アプリケーションで複数の 3D ライブラリやアニメーションライブラリを組み合わせるためのアーキテクチャパターン、ベストプラクティス、統合戦略を紹介します。threejs-webgl、gsap-scrolltrigger、react-three-fiber、motion-framer、react-spring-physics の各スキルから得られる知識を統合し、複雑で高性能な 3D Web エクスペリエンスを構築するための一貫したパターンをまとめています。
このスキルを使う場面:
- 複数のライブラリを組み合わせた複雑な 3D アプリケーションの構築
- アニメーションを連携させた、スクロール駆動型の 3D エクスペリエンスの作成
- 物理ベースのインタラクションを 3D シーンに実装
- 3D レンダリングと UI アニメーション間の状態管理
- 複数ライブラリを使用するアーキテクチャでのパフォーマンス最適化
- 3D アプリケーション向けの再利用可能なコンポーネントアーキテクチャの設計
- アニメーション手法の移行、またはその組み合わせ
主な統合パターン:
- Three.js + GSAP - スクロール駆動型の 3D アニメーション、タイムラインの連携
- React Three Fiber + Motion - 宣言的アニメーションを用いた状態ベースの 3D
- React Three Fiber + GSAP - React での複雑な 3D シーケンス
- React Three Fiber + React Spring - 物理ベースの 3D インタラクション
- Three.js + GSAP + React - 命令型と宣言型を組み合わせた 3D
アーキテクチャパターン
パターン 1:レイヤー分離(Three.js + GSAP + React UI)
ユースケース: UI を重ねた 3D シーン、スクロール駆動型アニメーション
アーキテクチャ:
├── 3D Layer (Three.js)
│ ├── Scene management
│ ├── Camera controls
│ └── Render loop
├── Animation Layer (GSAP)
│ ├── ScrollTrigger for 3D properties
│ ├── Timelines for sequences
│ └── UI transitions
└── UI Layer (React + Motion)
├── HTML overlays
├── State management
└── User interactions
実装:
// App.jsx - React root
import { useEffect, useRef } from 'react'
import { initThreeScene } from './three/scene'
import { initScrollAnimations } from './animations/scroll'
import { motion } from 'framer-motion'
function App() {
const canvasRef = useRef()
const sceneRef = useRef()
useEffect(() => {
// Initialize Three.js scene
sceneRef.current = initThreeScene(canvasRef.current)
// Initialize GSAP ScrollTrigger animations
initScrollAnimations(sceneRef.current)
// Cleanup
return () => {
sceneRef.current.dispose()
}
}, [])
return (
<div className="app">
<canvas ref={canvasRef} />
<motion.div
className="overlay"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
<section className="hero">
<h1>3D Experience</h1>
</section>
<section className="content">
{/* Scrollable content */}
</section>
</motion.div>
</div>
)
}
// three/scene.js - Three.js setup
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
export function initThreeScene(canvas) {
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
// Setup scene objects
const geometry = new THREE.BoxGeometry(2, 2, 2)
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 10, 7.5)
scene.add(directionalLight)
camera.position.set(0, 2, 5)
// Animation loop
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
animate()
// Resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
})
return { scene, camera, renderer, cube }
}
// animations/scroll.js - GSAP ScrollTrigger integration
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export function initScrollAnimations(sceneRefs) {
const { camera, cube } = sceneRefs
// Animate camera on scroll
gsap.to(camera.position, {
x: 5,
y: 3,
z: 10,
scrollTrigger: {
trigger: '.content',
start: 'top top',
end: 'bottom center',
scrub: 1,
onUpdate: () => camera.lookAt(cube.position)
}
})
// Animate mesh rotation
gsap.to(cube.rotation, {
y: Math.PI * 2,
x: Math.PI,
scrollTrigger: {
trigger: '.content',
start: 'top bottom',
end: 'bottom top',
scrub: true
}
})
// Animate material properties
gsap.to(cube.material, {
opacity: 0.3,
scrollTrigger: {
trigger: '.content',
start: 'top center',
end: 'center center',
scrub: 1
}
})
}
メリット:
- 関心事を明確に分離できます
- データフローを把握しやすくなります
- レイヤーごとにパフォーマンスを最適化できます
- レイヤーを個別にテストできます
トレードオフ:
- ボイラープレートが増えます
- レイヤー間の同期を手動で行う必要があります
- 状態管理が複雑になります
パターン 2:React コンポーネントへの統合(React Three Fiber + Motion)
ユースケース: 宣言的な 3D とアニメーションを用いた、React を中心とするアーキテクチャ
アーキテクチャ:
React Component Tree
├── <Canvas> (R3F)
│ ├── 3D Scene Components
│ ├── Lights
│ ├── Camera
│ └── Effects
└── <motion.div> (UI overlays)
├── HTML content
└── Animations
実装:
// App.jsx - Unified React approach
import { Canvas } from '@react-three/fiber'
import { Suspense } from 'react'
import { motion } from 'framer-motion'
import { Scene } from './components/Scene'
import { Loader } from './components/Loader'
function App() {
return (
<div className="app">
<Canvas
camera={{ position: [0, 2, 5], fov: 75 }}
dpr={[1, 2]}
shadows
>
<Suspense fallback={<Loader />}>
<Scene />
</Suspense>
</Canvas>
<motion.div
className="ui-overlay"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
>
<h1>React-First 3D Experience</h1>
</motion.div>
</div>
)
}
// components/Scene.jsx - R3F scene
import { useRef, useState } from 'react'
import { useFrame } from '@react-three/fiber'
import { OrbitControls, Environment } from '@react-three/drei'
import { motion } from 'framer-motion-3d'
export function Scene() {
return (
<>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 10, 7.5]} castShadow />
<AnimatedCube />
<Floor />
<OrbitControls enableDamping dampingFactor={0.05} />
<Environment preset="sunset" />
</>
)
}
function AnimatedCube() {
const [hovered, setHovered] = useState(false)
const [active, setActive] = useState(false)
return (
<motion.mesh
scale={active ? 1.5 : 1}
onClick={() => setActive(!active)}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
animate={{
rotateY: hovered ? Math.PI * 2 : 0
}}
transition={{ type: 'spring', stiffness: 200, damping: 20 }}
>
<boxGeometry args={[2, 2, 2]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</motion.mesh>
)
}
function Floor() {
return (
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]} receiveShadow>
<planeGeometry args={[100, 100]} />
<meshStandardMaterial color="#222" />
</mesh>
)
}
メリット:
- 宣言的な、React を中心とするアプローチ
- 状態管理を統合できます
- コンポーネントを再利用できます
- React ツールを使って簡単にテストできます
トレードオフ:
- R3F の学習コストがかかります
- レンダーループを制御しにくくなります
- React の再レンダリングに関する問題が発生する可能性があります
パターン 3:ハイブリッドアプローチ(R3F + GSAP タイムライン)
ユースケース: React の状態管理を用いた、複雑なアニメーションシーケンス
実装:
// components/AnimatedScene.jsx
import { useRef, useEffect } from 'react'
import { useFrame } from '@react-three/fiber'
import gsap from 'gsap'
export function AnimatedScene() {
const groupRef = useRef()
const timelineRef = useRef()
useEffect(() => {
// Create GSAP timeline for complex sequence
const tl = gsap.timeline({ repeat: -1, yoyo: true })
tl.to(groupRef.current.position, {
y: 2,
duration: 1,
ease: 'power2.inOut'
})
.to(groupRef.current.rotation, {
y: Math.PI * 2,
duration: 2,
ease: 'none'
}, 0) // Start at same time
timelineRef.current = tl
return () => tl.kill()
}, [])
return (
<group ref={groupRef}>
<mesh>
<boxGeometry />
<meshStandardMaterial color="cyan" />
</mesh>
</group>
)
}
パターン 4:物理ベースの 3D(R3F + React Spring)
ユースケース: 自然な物理駆動型の 3D インタラクション
実装:
// components/PhysicsCube.jsx
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import { useSpring, animated, config } from '@react-spring/three'
const AnimatedMesh = animated('mesh')
export function PhysicsCube() {
const [springs, api] = useSpring(() => ({
scale: 1,
position: [0, 0, 0],
config: config.wobbly
}), [])
const handleClick = () => {
api.start({
scale: 1.5,
position: [0, 2, 0]
})
// Return to original after delay
setTimeout(() => {
api.start({
scale: 1,
position: [0, 0, 0]
})
}, 1000)
}
return (
<AnimatedMesh
scale={springs.scale}
position={springs.position}
onClick={handleClick}
>
<boxGeometry />
<meshStandardMaterial color="orange" />
</AnimatedMesh>
)
}
よく使われる統合パターン
1. スクロール駆動型のカメラ移動
Three.js + GSAP:
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
// Smooth camera path through multiple points
const cameraPath = [
{ x: 0, y: 2, z: 5, lookAt: { x: 0, y: 0, z: 0 } },
{ x: 5, y: 3, z: 10, lookAt: { x: 0, y: 0, z: 0 } },
{ x: -3, y: 1, z: 8, lookAt: { x: 0, y: 0, z: 0 } }
]
const tl = gsap.timeline({
scrollTrigger: {
trigger: '#container',
start: 'top top',
end: 'bottom bottom',
scrub: 1,
pin: true
}
})
cameraPath.forEach((point, i) => {
tl.to(camera.position, {
x: point.x,
y: point.y,
z: point.z,
duration: 1,
onUpdate: () => camera.lookAt(point.lookAt.x, point.lookAt.y, point.lookAt.z)
}, i)
})
R3F + ScrollControls(Drei):
import { ScrollControls, Scroll, useScroll } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
function CameraRig() {
const scroll = useScroll()
useFrame((state) => {
const offset = scroll.offset
state.camera.position.x = Math.sin(offset * Math.PI * 2) * 5
state.camera.position.z = Math.cos(offset * Math.PI * 2) * 5
state.camera.lookAt(0, 0, 0)
})
return null
}
export function App() {
return (
<Canvas>
<ScrollControls pages={3} damping={0.5}>
<CameraRig />
<Scroll>
<Scene />
</Scroll>
</ScrollControls>
</Canvas>
)
}
2. ジェスチャー駆動型の 3D 操作
R3F + Motion(Framer Motion 3D):
import { motion } from 'framer-motion-3d'
function DraggableObject() {
return (
<motion.mesh
drag
dragElastic={0.1}
dragConstraints={{ left: -5, right: 5, top: 5, bottom: -5 }}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
animate={{
rotateY: [0, Math.PI * 2],
transition: { repeat: Infinity, duration: 4, ease: 'linear' }
}}
>
<sphereGeometry args={[1, 32, 32]} />
<meshStandardMaterial color="hotpink" />
</motion.mesh>
)
}
3. 状態と同期するアニメーション
R3F + Zustand + GSAP:
// store.js
import create from 'zustand'
export const useStore = create((set) => ({
selectedObject: null,
cameraMode: 'orbit',
setSelectedObject: (obj) => set({ selectedObject: obj }),
setCameraMode: (mode) => set({ cameraMode: mode })
}))
// components/InteractiveObject.jsx
import { useRef, useEffect } from 'react'
import { useStore } from '../store'
import gsap from 'gsap'
export function InteractiveObject({ id }) {
const meshRef = useRef()
const selectedObject = useStore((state) => state.selectedObject)
const setSelectedObject = useStore((state) => state.setSelectedObject)
const isSelected = selectedObject === id
useEffect(() => {
if (isSelected) {
gsap.to(meshRef.current.scale, {
x: 1.2,
y: 1.2,
z: 1.2,
duration: 0.3,
ease: 'back.out'
})
gsap.to(meshRef.current.material, {
emissiveIntensity: 0.5,
duration: 0.3
})
} else {
gsap.to(meshRef.current.scale, {
x: 1,
y: 1,
z: 1,
duration: 0.3,
ease: 'power2.inOut'
})
gsap.to(meshRef.current.material, {
emissiveIntensity: 0,
duration: 0.3
})
}
}, [isSelected])
return (
<mesh
ref={meshRef}
onClick={() => setSelectedObject(isSelected ? null : id)}
>
<boxGeometry />
<meshStandardMaterial color="cyan" emissive="cyan" />
</mesh>
)
}
状態管理の戦略
1. グローバルな 3D 状態に Zustand を使用する
最適な用途: 3D シーンと UI で状態を共有する場合
// store/scene.js
import create from 'zustand'
export const useSceneStore = create((set, get) => ({
// State
camera: { position: [0, 2, 5], target: [0, 0, 0] },
objects: {},
selectedId: null,
isAnimating: false,
// Actions
updateCamera: (updates) => set((state) => ({
camera: { ...state.camera, ...updates }
})),
addObject: (id, object) => set((state) => ({
objects: { ...state.objects, [id]: object }
})),
selectObject: (id) => set({ selectedId: id }),
setAnimating: (isAnimating) => set({ isAnimating })
}))
R3F での使用:
import { useSceneStore } from '../store/scene'
function Object3D({ id }) {
const selectedId = useSceneStore((state) => state.selectedId)
const selectObject = useSceneStore((state) => state.selectObject)
const isSelected = selectedId === id
return (
<mesh onClick={() => selectObject(id)}>
<boxGeometry />
<meshStandardMaterial color={isSelected ? 'hotpink' : 'orange'} />
</mesh>
)
}
パフォーマンス最適化
ライブラリ横断のパフォーマンスパターン
1. レンダーループの最適化
Three.js とアニメーションライブラリ間でレンダーループを連携させます:
// Unified render loop with conditional rendering
import { Clock } from 'three'
const clock = new Clock()
let needsRender = true
function animate() {
requestAnimationFrame(animate)
const delta = clock.getDelta()
const elapsed = clock.getElapsedTime()
// Only render when needed
if (needsRender || controls.enabled) {
// Update GSAP animations (handled automatically)
// Update Three.js
controls.update()
renderer.render(scene, camera)
// Reset flag
needsRender = false
}
}
// Trigger re-render on interactions
ScrollTrigger.addEventListener('update', () => {
needsRender = true
})
2. 必要に応じたレンダリング(R3F)
import { Canvas } from '@react-three/fiber'
function App() {
return (
<Canvas
frameloop="demand" // Only renders when needed
dpr={[1, 2]} // Adaptive pixel ratio
>
<Scene />
</Canvas>
)
}
function Scene() {
const invalidate = useThree((state) => state.invalidate)
// Trigger render on state change
const handleClick = () => {
// Update state...
invalidate() // Manually trigger render
}
return <mesh onClick={handleClick}>...</mesh>
}
よくある落とし穴
1. アニメーションの競合
問題: 複数のライブラリが同じプロパティをアニメーションさせようとします
// ❌ Wrong: GSAP and React Spring both animating position
gsap.to(meshRef.current.position, { x: 5 })
api.start({ position: [10, 0, 0] }) // Conflict!
解決策: プロパティごとに使うライブラリを 1 つにするか、タイミングを連携させます
// ✅ Correct: Separate properties
gsap.to(meshRef.current.position, { x: 5 }) // GSAP handles position
api.start({ scale: 1.5 }) // Spring handles scale
2. 状態の同期に関する問題
問題: React の状態と Three.js シーンの状態が一致しません
// ❌ Wrong: Updating Three.js without updating React state
mesh.position.x = 5 // Three.js updated
// But React state still shows old value!
解決策: ref または状態管理を使用します
// ✅ Correct: Update both
const updatePosition = (x) => {
mesh.position.x = x
setPosition(x) // Update React state
}
3. 使用しなくなったアニメーションによるメモリリーク
問題: アンマウント時にアニメーションをクリーンアップしていません
// ❌ Wrong: No cleanup
useEffect(() => {
gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
}, [])
解決策: useEffect の return 内で必ずクリーンアップします
// ✅ Correct: Cleanup on unmount
useEffect(() => {
const tween = gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
return () => {
tween.kill()
}
}, [])
判断マトリクス
用途別の推奨構成
| 用途 | 推奨スタック | 理由 |
|---|---|---|
| スクロール連動型の3Dを使ったマーケティング用ランディングページ | Three.js + GSAP + React UI | GSAPはスクロールのオーケストレーションに優れています |
| インタラクティブな3D製品ビューアーを備えたReactアプリ | R3F + Motion | 宣言的で、状態駆動型かつコンポーネントベースです |
| 複雑なアニメーションシーケンス(タイムラインベース) | R3F + GSAP | GSAPのタイムライン制御とR3Fコンポーネントを組み合わせられます |
| 物理ベースのインタラクション(ドラッグ、慣性) | R3F + React Spring | スプリング物理はジェスチャーに自然な感触をもたらします |
| 高パフォーマンスなパーティクルシステム | Three.js + GSAP | 命令型の制御、インスタンシング、最小限のオーバーヘッドを実現できます |
| 迅速なプロトタイピング、素早い反復開発 | R3F + Drei + Motion | 高水準の抽象化により、すばやく開発できます |
| 物理演算を使ったゲームのような体験 | R3F + React Spring + Cannon (physics) | 物理エンジンとスプリングベースのUIフィードバックを組み合わせられます |
リソース
このスキルには、複数ライブラリの統合に役立つリソースが含まれています。
references/
architecture_patterns.md- 詳細なアーキテクチャパターンとトレードオフperformance_optimization.md- スタック全体にわたるパフォーマンス戦略state_management.md- 3Dアプリケーションの状態管理パターン
scripts/
integration_helper.py- ライブラリの組み合わせに対応する統合用ボイラープレートを生成pattern_generator.py- 一般的な統合パターンのひな形を作成
assets/
starter_unified/- R3F + GSAP + Motionを組み合わせた完全なスターターテンプレートexamples/- 実際の統合例
関連スキル
基礎スキル(ライブラリ固有の詳細については、こちらを使用してください):
- threejs-webgl - Three.jsの基礎、シーンの設定、レンダリング
- gsap-scrolltrigger - GSAPアニメーション、ScrollTrigger、タイムライン
- react-three-fiber - R3Fコンポーネント、フック、Dreiヘルパー
- motion-framer - Motionコンポーネント、ジェスチャー、レイアウトアニメーション
- react-spring-physics - スプリング物理、React Springフック
基礎スキルを参照するタイミング:
- Three.js固有のAPIに関する質問 →
threejs-webgl - ScrollTriggerの構文 →
gsap-scrolltrigger - R3Fのフックとパターン →
react-three-fiber - Motionのジェスチャー処理 →
motion-framer - スプリングの設定 →
react-spring-physics
このメタスキルで扱う内容:
- ライブラリを組み合わせるためのアーキテクチャパターン
- ライブラリ間の状態管理
- パフォーマンス最適化戦略
- よくある統合時の落とし穴
- 意思決定のフレームワーク
複数のアニメーションライブラリやレンダリングライブラリを統合する複雑な3Dウェブアプリケーションの構築には、このスキルを使用してください。ライブラリ固有の実装の詳細については、個々の基礎スキルを参照してください。