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. 03协调 3D 场景与 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)

适用场景: 以 React 为核心,采用声明式 3D 和动画的架构

架构:

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. 使用 Zustand 管理全局 3D 状态

最适合: 在 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!

解决方案: 每个属性只选用一个库,或协调动画时间

// ✅ 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!

解决方案: 使用 refs 或状态管理

// ✅ 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 的返回函数中清理

// ✅ 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 + GSAP使用 GSAP 时间轴控制 R3F 组件
基于物理效果的交互(拖动、惯性)R3F + React Spring弹簧物理效果让手势更自然
高性能粒子系统Three.js + GSAP命令式控制、实例化、开销极小
快速原型设计和快速迭代R3F + Drei + Motion高级抽象,加快开发
带物理效果的游戏式体验R3F + React Spring + Cannon(物理)物理引擎 + 基于弹簧的 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 Web 应用时,请使用此技能。有关特定库的实现详情,请参考各个基础技能。

评论

支持 Markdown