关于 r3f-fundamentals
面向使用 React 和 Three.js 构建 3D 场景的开发者,介绍 React Three Fiber(R3F)的基础概念与用法。它将 Three.js 场景组织为声明式 React 组件,并涵盖生命周期集成、事件和资源加载。
主要功能
- 01以 JSX 声明式构建场景图
- 02Canvas 与相机配置
- 03通过钩子访问场景状态
- 04网格指针事件处理
- 05Suspense 异步资源加载
- 06卸载时自动释放对象
适用场景
- 01搭建 React 3D 场景
- 02配置相机、灯光与场景层级
- 03响应网格点击和悬停
- 04加载 GLTF 模型
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bbeierle12/ skill-mcp-claude --skill r3f-fundamentals安装后在 Claude Code 中输入/r3f-fundamentals确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
_meta.jsonreferences/hooks-api.mdscripts/templates/basic-scene.tsx详细介绍
React Three Fiber 基础
通过 React 组件以声明式方式使用 Three.js。R3F 将 Three.js 对象映射为 JSX 元素,并提供自动释放、响应式更新和 React 生命周期集成。
快速开始
import { Canvas } from '@react-three/fiber';
function App() {
return (
<Canvas>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
</Canvas>
);
}
核心原则:声明式场景图
R3F 将 Three.js 命令式 API 转换为 React 的声明式模型:
| Three.js(命令式) | R3F(声明式) |
|---|---|
new THREE.Mesh() | <mesh> |
mesh.position.set(1, 2, 3) | <mesh position={[1, 2, 3]}> |
scene.add(mesh) | JSX 嵌套会处理层级结构 |
mesh.geometry.dispose() | 卸载时自动处理 |
Canvas 配置
import { Canvas } from '@react-three/fiber';
<Canvas
// Renderer settings
gl={{ antialias: true, alpha: false, powerPreference: 'high-performance' }}
dpr={[1, 2]} // Device pixel ratio range
shadows // Enable shadow maps
// Camera (default: PerspectiveCamera)
camera={{
fov: 75,
near: 0.1,
far: 1000,
position: [0, 0, 5]
}}
// Or use orthographic
orthographic
camera={{ zoom: 50, position: [0, 0, 100] }}
// Performance
frameloop="demand" // 'always' | 'demand' | 'never'
performance={{ min: 0.5 }} // Adaptive performance
// Events
onCreated={({ gl, scene, camera }) => {
// Access Three.js objects after mount
}}
// Sizing
style={{ width: '100vw', height: '100vh' }}
/>
帧循环模式
| 模式 | 适用场景 |
|---|---|
always | 连续动画(游戏、模拟) |
demand | 静态场景,仅在状态变化时重新渲染 |
never | 通过 invalidate() 手动控制 |
// Demand mode with manual invalidation
import { useThree } from '@react-three/fiber';
function Controls() {
const invalidate = useThree(state => state.invalidate);
const handleDrag = () => {
// Update state...
invalidate(); // Request re-render
};
}
场景层级结构
JSX 嵌套 = Three.js 父子关系:
<group position={[0, 2, 0]} rotation={[0, Math.PI / 4, 0]}>
{/* Children inherit parent transforms */}
<mesh position={[1, 0, 0]}>
<sphereGeometry args={[0.5, 32, 32]} />
<meshStandardMaterial color="blue" />
</mesh>
<mesh position={[-1, 0, 0]}>
<boxGeometry args={[0.8, 0.8, 0.8]} />
<meshStandardMaterial color="red" />
</mesh>
</group>
常见容器组件
// Group: Transform container (no rendering)
<group position={[0, 0, 0]} />
// Object3D: Base class, rarely used directly
<object3D />
// Scene: Usually implicit (Canvas creates one)
<scene />
相机系统
默认透视相机
<Canvas camera={{
fov: 75, // Field of view (degrees)
aspect: width/height, // Auto-calculated
near: 0.1, // Near clipping plane
far: 1000, // Far clipping plane
position: [0, 5, 10]
}} />
自定义相机组件
import { PerspectiveCamera } from '@react-three/drei';
function Scene() {
return (
<>
<PerspectiveCamera
makeDefault // Set as active camera
fov={60}
position={[0, 2, 8]}
/>
{/* Scene contents */}
</>
);
}
相机访问
import { useThree } from '@react-three/fiber';
function CameraController() {
const { camera } = useThree();
useEffect(() => {
camera.lookAt(0, 0, 0);
}, [camera]);
return null;
}
灯光
灯光类型
// Ambient: Uniform, directionless
<ambientLight intensity={0.4} color="#ffffff" />
// Directional: Sun-like, parallel rays
<directionalLight
position={[5, 10, 5]}
intensity={1}
castShadow
/>
// Point: Radiates from position
<pointLight
position={[0, 5, 0]}
intensity={1}
distance={20} // Range (0 = infinite)
decay={2} // Physical falloff
/>
// Spot: Cone-shaped
<spotLight
position={[0, 10, 0]}
angle={Math.PI / 6} // Cone angle
penumbra={0.5} // Edge softness
castShadow
/>
// Hemisphere: Sky/ground gradient
<hemisphereLight
skyColor="#87ceeb"
groundColor="#362907"
intensity={0.6}
/>
阴影设置
<Canvas shadows>
<directionalLight
castShadow
position={[10, 10, 10]}
shadow-mapSize={[2048, 2048]}
shadow-camera-far={50}
shadow-camera-left={-10}
shadow-camera-right={10}
shadow-camera-top={10}
shadow-camera-bottom={-10}
/>
<mesh castShadow>
<boxGeometry />
<meshStandardMaterial />
</mesh>
<mesh receiveShadow rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]}>
<planeGeometry args={[20, 20]} />
<meshStandardMaterial />
</mesh>
</Canvas>
渲染循环(useFrame)
useFrame 每帧运行一次(目标帧率为 60 fps)。动画在这里执行。
import { useFrame, useThree } from '@react-three/fiber';
import { useRef } from 'react';
function RotatingBox() {
const meshRef = useRef<THREE.Mesh>(null!);
useFrame((state, delta) => {
// state: R3F state (camera, scene, clock, etc.)
// delta: Time since last frame (seconds)
meshRef.current.rotation.x += delta;
meshRef.current.rotation.y += delta * 0.5;
});
return (
<mesh ref={meshRef}>
<boxGeometry />
<meshNormalMaterial />
</mesh>
);
}
useFrame 状态对象
useFrame((state) => {
state.clock // THREE.Clock
state.clock.elapsedTime // Total time (seconds)
state.camera // Active camera
state.scene // Scene object
state.gl // WebGLRenderer
state.size // { width, height }
state.viewport // { width, height, factor, distance }
state.mouse // Normalized mouse position [-1, 1]
state.raycaster // THREE.Raycaster
});
渲染优先级
// Lower priority runs first, higher runs later
// Default is 0
useFrame(() => {
// Update physics
}, -1); // Runs before default
useFrame(() => {
// Update visuals
}, 0); // Default
useFrame(() => {
// Post-processing / camera
}, 1); // Runs after default
访问 Three.js 对象
useThree Hook
import { useThree } from '@react-three/fiber';
function SceneInfo() {
const {
gl, // WebGLRenderer
scene, // THREE.Scene
camera, // Active camera
size, // Canvas dimensions
viewport, // Viewport in Three.js units
clock, // THREE.Clock
set, // Update state
get, // Get current state
invalidate, // Request re-render (demand mode)
advance, // Advance one frame (never mode)
} = useThree();
return null;
}
用于直接访问的 Refs
import { useRef } from 'react';
import * as THREE from 'three';
function DirectAccess() {
const meshRef = useRef<THREE.Mesh>(null!);
const materialRef = useRef<THREE.MeshStandardMaterial>(null!);
useEffect(() => {
// Direct Three.js API access
meshRef.current.geometry.computeBoundingBox();
materialRef.current.needsUpdate = true;
}, []);
return (
<mesh ref={meshRef}>
<boxGeometry />
<meshStandardMaterial ref={materialRef} />
</mesh>
);
}
事件
R3F 为网格提供指针事件:
<mesh
onClick={(e) => console.log('click', e.point)}
onContextMenu={(e) => console.log('right click')}
onDoubleClick={(e) => console.log('double click')}
onPointerOver={(e) => console.log('hover')}
onPointerOut={(e) => console.log('unhover')}
onPointerDown={(e) => console.log('down')}
onPointerUp={(e) => console.log('up')}
onPointerMove={(e) => console.log('move')}
>
<boxGeometry />
<meshStandardMaterial />
</mesh>
事件对象
onClick={(e) => {
e.stopPropagation(); // Stop event bubbling
e.point // THREE.Vector3 intersection point
e.distance // Distance from camera
e.object // Intersected object
e.face // Intersected face
e.faceIndex // Face index
e.uv // UV coordinates
e.camera // Camera used for raycasting
e.delta // Distance from last event
}}
Suspense 与加载
R3F 与 React Suspense 集成,可用于异步加载:
import { Suspense } from 'react';
import { useLoader } from '@react-three/fiber';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb');
return <primitive object={gltf.scene} />;
}
function App() {
return (
<Canvas>
<Suspense fallback={<LoadingSpinner />}>
<Model />
</Suspense>
</Canvas>
);
}
function LoadingSpinner() {
const meshRef = useRef<THREE.Mesh>(null!);
useFrame((_, delta) => {
meshRef.current.rotation.z += delta * 2;
});
return (
<mesh ref={meshRef}>
<torusGeometry args={[1, 0.2, 16, 32]} />
<meshBasicMaterial color="white" wireframe />
</mesh>
);
}
依赖项
{
"dependencies": {
"@react-three/fiber": "^8.15.0",
"three": "^0.160.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/three": "^0.160.0"
}
}
文件结构
r3f-fundamentals/
├── SKILL.md
├── references/
│ ├── canvas-props.md # Complete Canvas prop reference
│ ├── hooks-api.md # useThree, useFrame, useLoader
│ └── event-system.md # Event handling deep-dive
└── scripts/
├── templates/
│ ├── basic-scene.tsx # Minimal starter
│ ├── lit-scene.tsx # With proper lighting
│ └── interactive.tsx # With events and animation
└── utils/
└── canvas-config.ts # Preset configurations
参考资料
references/canvas-props.md— 完整的 Canvas 配置选项references/hooks-api.md— 所有 R3F Hook 及示例references/event-system.md— 指针事件和射线投射
评论
支持 Markdown