Hooks
Hooks
❌ You cannot expect something like this to work:
import { useThree } from '@react-three/fiber'
function App() {
const { size } = useThree() // This will just crash
return (
<Canvas>
<mesh>
✅ Do this instead:
function Foo() {
const { size } = useThree()
...
}
function App() {
return (
<Canvas>
<Foo />
useThree
这个
import { useThree } from '@react-three/fiber'
function Foo() {
const state = useThree()
这个
PROP | DESCRIPTION | TYPE |
---|---|---|
gl | Renderer | THREE.WebGLRenderer |
scene | Scene | THREE.Scene |
camera | Camera | THREE.PerspectiveCamera |
raycaster | Default raycaster | THREE.Raycaster |
pointer | Contains updated, normalized, centric pointer coordinates | THREE.Vector2 |
mouse | Note: this is deprecated, use pointer instead! Normalized event coordinates |
THREE.Vector2 |
clock | Running system clock | THREE.Clock |
linear | True when the colorspace is linear | boolean |
flat | True when no tonemapping is used | boolean |
legacy | Disables global color management via THREE.ColorManagement |
boolean |
frameloop | Render mode: always, demand, never | always , demand , never |
performance | System regression | { current: number, min: number, max: number, debounce: number, regress: () => void } |
size | Canvas size in pixels | { width: number, height: number, top: number, left: number, updateStyle?: boolean } |
viewport | Viewport size in three.js units | { width: number, height: number, initialDpr: number, dpr: number, factor: number, distance: number, aspect: number, getCurrentViewport: (camera?: Camera, target?: THREE.Vector3, size?: Size) => Viewport } |
xr | XR interface, manages WebXR rendering | { connect: () => void, disconnect: () => void } |
set | Allows you to set any state property | (state: SetState<RootState>) => void |
get | Allows you to retrieve any state property non-reactively | () => GetState<RootState> |
invalidate | Request a new render, given that frameloop === 'demand' |
() => void |
advance | Advance one tick, given that frameloop === 'never' |
(timestamp: number, runGlobalEffects?: boolean) => void |
setSize | Resize the canvas | (width: number, height: number, updateStyle?: boolean, top?: number, left?: number) => void |
setDpr | Set the pixel-ratio | (dpr: number) => void |
setFrameloop | Shortcut to set the current render mode | (frameloop?: 'always', 'demand', 'never') => void |
setEvents | Shortcut to setting the event layer | (events: Partial<EventManager<any>>) => void |
onPointerMissed | Response for pointer clicks that have missed a target | () => void |
events | Pointer-event handling | { connected: TargetNode, handlers: Events, connect: (target: TargetNode) => void, disconnect: () => void } |
你也可以选择属性,这可以让你避免对那些只对特定内容感兴趣的组件进行不必要的重新渲染。
// Will only trigger re-render when the default camera is exchanged
const camera = useThree((state) => state.camera);
// Will only re-render on resize changes
const viewport = useThree((state) => state.viewport);
// ❌ You cannot expect reactivity from three.js internals!
const zoom = useThree((state) => state.camera.zoom);
function Foo() {
const get = useThree((state) => state.get)
...
get() // Get fresh state from anywhere you want
function Foo() {
const set = useThree((state) => state.set)
...
useEffect(() => {
set({ camera: new THREE.OrthographicCamera(...) })
}, [])
useFrame
这个
import { useFrame } from '@react-three/fiber'
function Foo() {
useFrame((state, delta, xrFrame) => {
// This function runs at the native refresh rate inside of a shared render-loop
})
小心你在
function Render() {
// Takes over the render-loop, the user has the responsibility to render
useFrame(({ gl, scene, camera }) => {
gl.render(scene, camera)
}, 1)
function RenderOnTop() {
// This will execute *after* Render's useframe
useFrame(({ gl, ... }) => {
gl.render(...)
}, 2)
回调将按照优先级升值的顺序执行(最低的在前,最高的在后
function A() {
// This will execute first
useFrame(() => ..., -2)
function B() {
// This useFrame will execute *after* A's
useFrame(() => ..., -1)
useLoader
这个
import { Suspense } from "react";
import { useLoader } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
function Model() {
const result = useLoader(GLTFLoader, "/model.glb");
// You don't need to check for the presence of the result, when we're here
// the result is guaranteed to be present since useLoader suspends the component
return <primitive object={result.scene} />;
}
function App() {
return (
<Suspense fallback={<FallbackComponent /> /* or null */}>
<Model />
</Suspense>
);
}
用
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
useLoader(GLTFLoader, url, (loader) => {
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("/draco-gltf/");
loader.setDRACOLoader(dracoLoader);
});
它还可以并行地提出多个请求。
const [bumpMap, specMap, normalMap] = useLoader(TextureLoader, [
url1,
url2,
url2,
]);
你可以从你提供的作为第四个参数的回调中获得加载状态。不过可以考虑像
useLoader(loader, url, extensions, (xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
});
如果发现一个
const { nodes, material } = useLoader(GLTFLoader, url);
你可以在全局空间中预加载资产,这样模型就可以在组件树中安装之前预期地加载。
useLoader.preload(GLTFLoader, "/model.glb" /* extensions */);
useGraph
方便的钩子,可以从任何
import { useLoader, useGraph } from "@react-three/fiber";
function Model(url) {
const scene = useLoader(OBJLoader, url);
const { nodes, materials } = useGraph(scene);
return <mesh geometry={nodes.robot.geometry} material={materials.metal} />;
}