mcpField
W
web3d-integration-patterns

web3d-integration-patterns

github.com/freshtechbro開発1.2k3.2k 回インストール49 週間前に更新
リポジトリ

web3d-integration-patterns について

Web アプリで 3D 描画ライブラリとアニメーションライブラリを組み合わせる開発者向けのメタスキルです。アーキテクチャ、状態管理、パフォーマンス最適化、統合例を扱います。

主な機能

  1. 01Three.js、GSAP、React の組み合わせパターン
  2. 02React Three Fiber のアニメーション手法
  3. 03ライブラリ間の状態管理戦略
  4. 04パフォーマンスと描画ループの最適化
  5. 05統合例とスキャフォールド用リソース

活用シーン

  1. 01スクロール連動型 3D Web 体験の構築
  2. 02物理ベースのシーン操作の作成
  3. 033D シーンと UI 状態の連携
  4. 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 アプリケーション向けの再利用可能なコンポーネントアーキテクチャの設計
  • アニメーション手法の移行、またはその組み合わせ

主な統合パターン:

  1. Three.js + GSAP - スクロール駆動型の 3D アニメーション、タイムラインの連携
  2. React Three Fiber + Motion - 宣言的アニメーションを用いた状態ベースの 3D
  3. React Three Fiber + GSAP - React での複雑な 3D シーケンス
  4. React Three Fiber + React Spring - 物理ベースの 3D インタラクション
  5. 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 UIGSAPはスクロールのオーケストレーションに優れています
インタラクティブな3D製品ビューアーを備えたReactアプリR3F + Motion宣言的で、状態駆動型かつコンポーネントベースです
複雑なアニメーションシーケンス(タイムラインベース)R3F + GSAPGSAPのタイムライン制御と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ウェブアプリケーションの構築には、このスキルを使用してください。ライブラリ固有の実装の詳細については、個々の基礎スキルを参照してください。

コメント