web3d-integration-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWeb 3D Integration Patterns
Web 3D整合模式
Overview
概述
This meta-skill provides architectural patterns, best practices, and integration strategies for combining multiple 3D and animation libraries in web applications. It synthesizes knowledge from the threejs-webgl, gsap-scrolltrigger, react-three-fiber, motion-framer, and react-spring-physics skills into cohesive patterns for building complex, performant 3D web experiences.
When to use this skill:
- Building complex 3D applications that combine multiple libraries
- Creating scroll-driven 3D experiences with animation orchestration
- Implementing physics-based interactions with 3D scenes
- Managing state across 3D rendering and UI animations
- Optimizing performance in multi-library architectures
- Designing reusable component architectures for 3D applications
- Migrating between or combining animation approaches
Core Integration Combinations:
- Three.js + GSAP - Scroll-driven 3D animations, timeline orchestration
- React Three Fiber + Motion - State-based 3D with declarative animations
- React Three Fiber + GSAP - Complex 3D sequences in React
- React Three Fiber + React Spring - Physics-based 3D interactions
- Three.js + GSAP + React - Hybrid imperative/declarative 3D
本元技能提供了在Web应用中整合多个3D与动画库的架构模式、最佳实践和整合策略。它将threejs-webgl、gsap-scrolltrigger、react-three-fiber、motion-framer和react-spring-physics等技能的知识进行整合,形成构建复杂、高性能Web 3D体验的统一模式。
何时使用本技能:
- 构建整合多个库的复杂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
Architecture Patterns
架构模式
Pattern 1: Layered Separation (Three.js + GSAP + React UI)
模式1:分层分离(Three.js + GSAP + React UI)
Use case: 3D scene with overlaid UI, scroll-driven animations
Architecture:
├── 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 interactionsImplementation:
javascript
// 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>
)
}javascript
// 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 }
}javascript
// 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
}
})
}Benefits:
- Clear separation of concerns
- Easy to reason about data flow
- Performance optimization per layer
- Independent testing of layers
Trade-offs:
- More boilerplate
- Manual synchronization between layers
- State management complexity
适用场景: 带有叠加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实现:
javascript
// 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>
)
}javascript
// 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 }
}javascript
// 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
}
})
}优势:
- 清晰的关注点分离
- 易于理解数据流
- 按层进行性能优化
- 各层可独立测试
权衡点:
- 更多样板代码
- 层间需手动同步
- 状态管理复杂度高
Pattern 2: Unified React Component (React Three Fiber + Motion)
模式2:统一React组件(React Three Fiber + Motion)
Use case: React-first architecture with declarative 3D and animations
Architecture:
React Component Tree
├── <Canvas> (R3F)
│ ├── 3D Scene Components
│ ├── Lights
│ ├── Camera
│ └── Effects
└── <motion.div> (UI overlays)
├── HTML content
└── AnimationsImplementation:
jsx
// 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>
)
}jsx
// 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>
)
}Benefits:
- Declarative, React-first approach
- Unified state management
- Component reusability
- Easy testing with React tools
Trade-offs:
- R3F learning curve
- Less control over render loop
- Potential React re-render issues
适用场景: 以React优先的架构,带有声明式3D与动画
架构:
React Component Tree
├── <Canvas> (R3F)
│ ├── 3D Scene Components
│ ├── Lights
│ ├── Camera
│ └── Effects
└── <motion.div> (UI overlays)
├── HTML content
└── Animations实现:
jsx
// 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>
)
}jsx
// 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工具进行测试
权衡点:
- React Three Fiber学习曲线
- 对渲染循环的控制较少
- 可能出现React重渲染问题
Pattern 3: Hybrid Approach (R3F + GSAP Timelines)
模式3:混合方法(R3F + GSAP时间线)
Use case: Complex animation sequences with React state management
Implementation:
jsx
// 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>
)
}适用场景: 带有React状态管理的复杂动画序列
实现:
jsx
// 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>
)
}Pattern 4: Physics-Based 3D (R3F + React Spring)
模式4:基于物理的3D(R3F + React Spring)
Use case: Natural, physics-driven 3D interactions
Implementation:
jsx
// 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>
)
}适用场景: 自然的、基于物理的3D交互
实现:
jsx
// 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>
)
}Common Integration Patterns
常见整合模式
1. Scroll-Driven Camera Movement
1. 滚动驱动的相机移动
Three.js + GSAP:
javascript
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):
jsx
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>
)
}Three.js + GSAP:
javascript
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):
jsx
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. Gesture-Driven 3D Manipulation
2. 手势驱动的3D操作
R3F + Motion (Framer Motion 3D):
jsx
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>
)
}R3F + Motion (Framer Motion 3D):
jsx
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. State-Synchronized Animations
3. 状态同步动画
R3F + Zustand + GSAP:
jsx
// 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 })
}))jsx
// 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>
)
}R3F + Zustand + GSAP:
jsx
// 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 })
}))jsx
// 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>
)
}State Management Strategies
状态管理策略
1. Zustand for Global 3D State
1. 使用Zustand管理全局3D状态
Best for: Shared state across 3D scene and UI
javascript
// 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 })
}))Usage in R3F:
jsx
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>
)
}最适用于: 3D场景与UI之间的共享状态
javascript
// 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 })
}))在React Three Fiber中的使用:
jsx
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>
)
}Performance Optimization
性能优化
Cross-Library Performance Patterns
跨库性能模式
1. Render Loop Optimization
1. 渲染循环优化
Coordinate render loops between Three.js and animation libraries:
javascript
// 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
})协调Three.js与动画库的渲染循环:
javascript
// 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. On-Demand Rendering (R3F)
2. 按需渲染(React Three Fiber)
jsx
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>
}jsx
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>
}Common Pitfalls
常见陷阱
1. Animation Conflicts
1. 动画冲突
Problem: Multiple libraries trying to animate the same property
jsx
// ❌ Wrong: GSAP and React Spring both animating position
gsap.to(meshRef.current.position, { x: 5 })
api.start({ position: [10, 0, 0] }) // Conflict!Solution: Choose one library per property or coordinate timing
jsx
// ✅ Correct: Separate properties
gsap.to(meshRef.current.position, { x: 5 }) // GSAP handles position
api.start({ scale: 1.5 }) // Spring handles scale问题: 多个库尝试动画同一个属性
jsx
// ❌ Wrong: GSAP and React Spring both animating position
gsap.to(meshRef.current.position, { x: 5 })
api.start({ position: [10, 0, 0] }) // Conflict!解决方案: 为每个属性选择一个库,或者协调时间
jsx
// ✅ Correct: Separate properties
gsap.to(meshRef.current.position, { x: 5 }) // GSAP handles position
api.start({ scale: 1.5 }) // Spring handles scale2. State Synchronization Issues
2. 状态同步问题
Problem: React state out of sync with Three.js scene
jsx
// ❌ Wrong: Updating Three.js without updating React state
mesh.position.x = 5 // Three.js updated
// But React state still shows old value!Solution: Use refs or state management
jsx
// ✅ Correct: Update both
const updatePosition = (x) => {
mesh.position.x = x
setPosition(x) // Update React state
}问题: React状态与Three.js场景不同步
jsx
// ❌ Wrong: Updating Three.js without updating React state
mesh.position.x = 5 // Three.js updated
// But React state still shows old value!解决方案: 使用refs或状态管理
jsx
// ✅ Correct: Update both
const updatePosition = (x) => {
mesh.position.x = x
setPosition(x) // Update React state
}3. Memory Leaks from Abandoned Animations
3. 废弃动画导致的内存泄漏
Problem: Not cleaning up animations on unmount
jsx
// ❌ Wrong: No cleanup
useEffect(() => {
gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
}, [])Solution: Always cleanup in useEffect return
jsx
// ✅ Correct: Cleanup on unmount
useEffect(() => {
const tween = gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
return () => {
tween.kill()
}
}, [])问题: 卸载时未清理动画
jsx
// ❌ Wrong: No cleanup
useEffect(() => {
gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
}, [])解决方案: 始终在useEffect返回中进行清理
jsx
// ✅ Correct: Cleanup on unmount
useEffect(() => {
const tween = gsap.to(meshRef.current.rotation, { y: Math.PI * 2, repeat: -1 })
return () => {
tween.kill()
}
}, [])Decision Matrix
决策矩阵
When to Use Which Combination
何时使用哪种组合
| Use Case | Recommended Stack | Rationale |
|---|---|---|
| Marketing landing page with scroll-driven 3D | Three.js + GSAP + React UI | GSAP excels at scroll orchestration |
| React app with interactive 3D product viewer | R3F + Motion | Declarative, state-driven, component-based |
| Complex animation sequences (timeline-based) | R3F + GSAP | GSAP timeline control with R3F components |
| Physics-based interactions (drag, momentum) | R3F + React Spring | Spring physics feel natural for gestures |
| High-performance particle systems | Three.js + GSAP | Imperative control, instancing, minimal overhead |
| Rapid prototyping, quick iterations | R3F + Drei + Motion | High-level abstractions, fast development |
| Game-like experiences with physics | R3F + React Spring + Cannon (physics) | Physics engine + spring-based UI feedback |
| 适用场景 | 推荐技术栈 | 理由 |
|---|---|---|
| 带有滚动驱动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反馈 |
Resources
资源
This skill includes bundled resources for multi-library integration:
本技能包含多库整合的配套资源:
references/
references/
- - Detailed architectural patterns and trade-offs
architecture_patterns.md - - Performance strategies across the stack
performance_optimization.md - - State management patterns for 3D applications
state_management.md
- - 详细的架构模式与权衡
architecture_patterns.md - - 跨栈性能策略
performance_optimization.md - - 3D应用的状态管理模式
state_management.md
scripts/
scripts/
- - Generate integration boilerplate for library combinations
integration_helper.py - - Scaffold common integration patterns
pattern_generator.py
- - 为库组合生成整合样板代码
integration_helper.py - - 搭建常见整合模式的脚手架
pattern_generator.py
assets/
assets/
- - Complete starter template combining R3F + GSAP + Motion
starter_unified/ - - Real-world integration examples
examples/
- - 整合R3F + GSAP + Motion的完整启动模板
starter_unified/ - - 真实世界的整合示例
examples/
Related Skills
相关技能
Foundation Skills (use these for library-specific details):
- threejs-webgl - Three.js fundamentals, scene setup, rendering
- gsap-scrolltrigger - GSAP animations, ScrollTrigger, timelines
- react-three-fiber - R3F components, hooks, Drei helpers
- motion-framer - Motion components, gestures, layout animations
- react-spring-physics - Spring physics, React Spring hooks
When to Reference Foundation Skills:
- Three.js-specific API questions →
threejs-webgl - ScrollTrigger syntax →
gsap-scrolltrigger - R3F hooks and patterns →
react-three-fiber - Motion gesture handling →
motion-framer - Spring configuration →
react-spring-physics
This Meta-Skill Covers:
- Architecture patterns for combining libraries
- State management across libraries
- Performance optimization strategies
- Common integration pitfalls
- Decision-making frameworks
Use this skill when building complex 3D web applications that integrate multiple animation and rendering libraries. For library-specific implementation details, reference the individual foundation skills.
基础技能(如需库特定细节请使用这些技能):
- 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
本元技能涵盖:
- 整合库的架构模式
- 跨库状态管理
- 性能优化策略
- 常见整合陷阱
- 决策框架
当构建整合多个动画与渲染库的复杂Web 3D应用时,使用本技能。如需库特定的实现细节,请参考各个基础技能。