@react-three/fiber 8.8.10 → 8.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/declarations/src/core/hooks.d.ts +8 -5
- package/dist/declarations/src/core/index.d.ts +2 -2
- package/dist/declarations/src/core/loop.d.ts +1 -2
- package/dist/declarations/src/index.d.ts +2 -1
- package/dist/declarations/src/native.d.ts +2 -0
- package/dist/{index-bd4f185a.cjs.prod.js → index-57cb2f85.cjs.prod.js} +4 -3
- package/dist/{index-21f75f82.esm.js → index-6f0019d1.esm.js} +4 -4
- package/dist/{index-f307fe93.cjs.dev.js → index-8539ced2.cjs.dev.js} +4 -3
- package/dist/react-three-fiber.cjs.dev.js +2 -1
- package/dist/react-three-fiber.cjs.prod.js +2 -1
- package/dist/react-three-fiber.esm.js +2 -2
- package/native/dist/react-three-fiber-native.cjs.dev.js +3 -1
- package/native/dist/react-three-fiber-native.cjs.prod.js +3 -1
- package/native/dist/react-three-fiber-native.esm.js +2 -2
- package/package.json +1 -1
- package/readme.md +124 -73
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @react-three/fiber
|
|
2
2
|
|
|
3
|
+
## 8.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a458b4dd: fix(loop): export flush methods and types
|
|
8
|
+
|
|
9
|
+
## 8.8.11
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 2068f0cc: fix: events pointerlock, useLoader extension types
|
|
14
|
+
|
|
3
15
|
## 8.8.10
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -4,13 +4,16 @@ import { StateSelector, EqualityChecker } from 'zustand';
|
|
|
4
4
|
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
5
5
|
import { RootState, RenderCallback } from './store';
|
|
6
6
|
import { ObjectMap } from './utils';
|
|
7
|
-
import { LoadingManager } from 'three';
|
|
8
7
|
import { LocalState } from './renderer';
|
|
9
8
|
export interface Loader<T> extends THREE.Loader {
|
|
10
9
|
load(url: string, onLoad?: (result: T) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): unknown;
|
|
11
10
|
}
|
|
12
|
-
export declare type
|
|
11
|
+
export declare type LoaderProto<T> = new (...args: any) => Loader<T extends unknown ? any : T>;
|
|
12
|
+
export declare type LoaderReturnType<T, L extends LoaderProto<T>> = T extends unknown ? Awaited<ReturnType<InstanceType<L>['loadAsync']>> : T;
|
|
13
13
|
export declare type LoaderResult<T> = T extends any[] ? Loader<T[number]> : Loader<T>;
|
|
14
|
+
export declare type Extensions<T extends {
|
|
15
|
+
prototype: LoaderProto<any>;
|
|
16
|
+
}> = (loader: T['prototype']) => void;
|
|
14
17
|
export declare type ConditionalType<Child, Parent, Truthy, Falsy> = Child extends Parent ? Truthy : Falsy;
|
|
15
18
|
export declare type BranchingReturn<T, Parent, Coerced> = ConditionalType<T, Parent, Coerced, T>;
|
|
16
19
|
export declare function useInstanceHandle<O>(ref: React.MutableRefObject<O>): React.MutableRefObject<LocalState>;
|
|
@@ -18,8 +21,8 @@ export declare function useStore(): import("zustand").UseBoundStore<RootState, i
|
|
|
18
21
|
export declare function useThree<T = RootState>(selector?: StateSelector<RootState, T>, equalityFn?: EqualityChecker<T>): T;
|
|
19
22
|
export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
|
|
20
23
|
export declare function useGraph(object: THREE.Object3D): ObjectMap;
|
|
21
|
-
export declare function useLoader<T, U extends string | string[]
|
|
24
|
+
export declare function useLoader<T, U extends string | string[], L extends LoaderProto<T>, R = LoaderReturnType<T, L>>(Proto: L, input: U, extensions?: Extensions<L>, onProgress?: (event: ProgressEvent<EventTarget>) => void): U extends any[] ? BranchingReturn<R, GLTF, GLTF & ObjectMap>[] : BranchingReturn<R, GLTF, GLTF & ObjectMap>;
|
|
22
25
|
export declare namespace useLoader {
|
|
23
|
-
var preload: <T, U extends string | string[]
|
|
24
|
-
var clear: <T, U extends string | string[]
|
|
26
|
+
var preload: <T, U extends string | string[], L extends LoaderProto<T>>(Proto: L, input: U, extensions?: Extensions<L> | undefined) => undefined;
|
|
27
|
+
var clear: <T, U extends string | string[], L extends LoaderProto<T>>(Proto: L, input: U) => void;
|
|
25
28
|
}
|
|
@@ -5,7 +5,7 @@ import { UseBoundStore } from 'zustand';
|
|
|
5
5
|
import * as ReactThreeFiber from '../three-types';
|
|
6
6
|
import { Renderer, context, RootState, Size, Dpr, Performance, PrivateKeys } from './store';
|
|
7
7
|
import { extend, Root } from './renderer';
|
|
8
|
-
import { addEffect, addAfterEffect, addTail } from './loop';
|
|
8
|
+
import { addEffect, addAfterEffect, addTail, flushGlobalEffects } from './loop';
|
|
9
9
|
import { EventManager, ComputeFunction } from './events';
|
|
10
10
|
import { dispose, getRootState, Camera } from './utils';
|
|
11
11
|
declare const roots: Map<Element, Root>;
|
|
@@ -54,4 +54,4 @@ export declare type InjectState = Partial<Omit<RootState, PrivateKeys> & {
|
|
|
54
54
|
declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): JSX.Element;
|
|
55
55
|
declare const act: any;
|
|
56
56
|
export * from './hooks';
|
|
57
|
-
export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, getRootState, act, roots as _roots, };
|
|
57
|
+
export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, flushGlobalEffects, getRootState, act, roots as _roots, };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { Root } from './renderer';
|
|
3
3
|
import { RootState } from './store';
|
|
4
|
-
declare type GlobalRenderCallback = (timeStamp: number) => void;
|
|
4
|
+
export declare type GlobalRenderCallback = (timeStamp: number) => void;
|
|
5
5
|
export declare const addEffect: (callback: GlobalRenderCallback) => () => void;
|
|
6
6
|
export declare const addAfterEffect: (callback: GlobalRenderCallback) => () => void;
|
|
7
7
|
export declare const addTail: (callback: GlobalRenderCallback) => () => void;
|
|
@@ -12,4 +12,3 @@ export declare function createLoop<TCanvas>(roots: Map<TCanvas, Root>): {
|
|
|
12
12
|
invalidate: (state?: RootState | undefined, frames?: number) => void;
|
|
13
13
|
advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void;
|
|
14
14
|
};
|
|
15
|
-
export {};
|
|
@@ -4,8 +4,9 @@ export { ReactThreeFiber };
|
|
|
4
4
|
export type { BaseInstance, LocalState } from './core/renderer';
|
|
5
5
|
export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
|
|
6
6
|
export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
|
|
7
|
+
export { createEvents } from './core/events';
|
|
7
8
|
export type { ObjectMap, Camera } from './core/utils';
|
|
8
9
|
export * from './web/Canvas';
|
|
9
|
-
export { createEvents } from './core/events';
|
|
10
10
|
export { createPointerEvents as events } from './web/events';
|
|
11
|
+
export type { GlobalRenderCallback, GlobalEffectType } from './core/loop';
|
|
11
12
|
export * from './core';
|
|
@@ -4,7 +4,9 @@ export { ReactThreeFiber };
|
|
|
4
4
|
export type { BaseInstance, LocalState } from './core/renderer';
|
|
5
5
|
export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
|
|
6
6
|
export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
|
|
7
|
+
export { createEvents } from './core/events';
|
|
7
8
|
export type { ObjectMap, Camera } from './core/utils';
|
|
8
9
|
export * from './native/Canvas';
|
|
9
10
|
export { createTouchEvents as events } from './native/events';
|
|
11
|
+
export type { GlobalRenderCallback, GlobalEffectType } from './core/loop';
|
|
10
12
|
export * from './core';
|
|
@@ -579,7 +579,7 @@ function createEvents(store) {
|
|
|
579
579
|
.sort((a, b) => {
|
|
580
580
|
const aState = getRootState(a.object);
|
|
581
581
|
const bState = getRootState(b.object);
|
|
582
|
-
if (!aState || !bState) return
|
|
582
|
+
if (!aState || !bState) return a.distance - b.distance;
|
|
583
583
|
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
584
584
|
}) // Filter out duplicates
|
|
585
585
|
.filter(item => {
|
|
@@ -608,7 +608,7 @@ function createEvents(store) {
|
|
|
608
608
|
|
|
609
609
|
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
610
610
|
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
611
|
-
intersections.push(captureData.intersection);
|
|
611
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
612
612
|
}
|
|
613
613
|
}
|
|
614
614
|
|
|
@@ -777,7 +777,7 @@ function createEvents(store) {
|
|
|
777
777
|
internal
|
|
778
778
|
} = store.getState();
|
|
779
779
|
|
|
780
|
-
if ('pointerId' in event &&
|
|
780
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
781
781
|
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
782
782
|
// object that's getting removed.
|
|
783
783
|
internal.capturedMap.delete(event.pointerId);
|
|
@@ -2195,6 +2195,7 @@ exports.createPortal = createPortal;
|
|
|
2195
2195
|
exports.createRoot = createRoot;
|
|
2196
2196
|
exports.dispose = dispose;
|
|
2197
2197
|
exports.extend = extend;
|
|
2198
|
+
exports.flushGlobalEffects = flushGlobalEffects;
|
|
2198
2199
|
exports.getRootState = getRootState;
|
|
2199
2200
|
exports.invalidate = invalidate;
|
|
2200
2201
|
exports.isRef = isRef;
|
|
@@ -552,7 +552,7 @@ function createEvents(store) {
|
|
|
552
552
|
.sort((a, b) => {
|
|
553
553
|
const aState = getRootState(a.object);
|
|
554
554
|
const bState = getRootState(b.object);
|
|
555
|
-
if (!aState || !bState) return
|
|
555
|
+
if (!aState || !bState) return a.distance - b.distance;
|
|
556
556
|
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
557
557
|
}) // Filter out duplicates
|
|
558
558
|
.filter(item => {
|
|
@@ -581,7 +581,7 @@ function createEvents(store) {
|
|
|
581
581
|
|
|
582
582
|
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
583
583
|
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
584
|
-
intersections.push(captureData.intersection);
|
|
584
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
587
|
|
|
@@ -750,7 +750,7 @@ function createEvents(store) {
|
|
|
750
750
|
internal
|
|
751
751
|
} = store.getState();
|
|
752
752
|
|
|
753
|
-
if ('pointerId' in event &&
|
|
753
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
754
754
|
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
755
755
|
// object that's getting removed.
|
|
756
756
|
internal.capturedMap.delete(event.pointerId);
|
|
@@ -2154,4 +2154,4 @@ reconciler.injectIntoDevTools({
|
|
|
2154
2154
|
});
|
|
2155
2155
|
const act = React.unstable_act;
|
|
2156
2156
|
|
|
2157
|
-
export {
|
|
2157
|
+
export { useFrame as A, Block as B, useGraph as C, useLoader as D, ErrorBoundary as E, createRoot as a, useIsomorphicLayoutEffect as b, createEvents as c, unmountComponentAtNode as d, extend as e, context as f, createPortal as g, reconciler as h, isRef as i, applyProps as j, dispose as k, invalidate as l, advance as m, addEffect as n, addAfterEffect as o, addTail as p, flushGlobalEffects as q, render as r, getRootState as s, threeTypes as t, useMutableCallback as u, act as v, roots as w, useInstanceHandle as x, useStore as y, useThree as z };
|
|
@@ -579,7 +579,7 @@ function createEvents(store) {
|
|
|
579
579
|
.sort((a, b) => {
|
|
580
580
|
const aState = getRootState(a.object);
|
|
581
581
|
const bState = getRootState(b.object);
|
|
582
|
-
if (!aState || !bState) return
|
|
582
|
+
if (!aState || !bState) return a.distance - b.distance;
|
|
583
583
|
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
584
584
|
}) // Filter out duplicates
|
|
585
585
|
.filter(item => {
|
|
@@ -608,7 +608,7 @@ function createEvents(store) {
|
|
|
608
608
|
|
|
609
609
|
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
610
610
|
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
611
|
-
intersections.push(captureData.intersection);
|
|
611
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
612
612
|
}
|
|
613
613
|
}
|
|
614
614
|
|
|
@@ -777,7 +777,7 @@ function createEvents(store) {
|
|
|
777
777
|
internal
|
|
778
778
|
} = store.getState();
|
|
779
779
|
|
|
780
|
-
if ('pointerId' in event &&
|
|
780
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
781
781
|
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
782
782
|
// object that's getting removed.
|
|
783
783
|
internal.capturedMap.delete(event.pointerId);
|
|
@@ -2195,6 +2195,7 @@ exports.createPortal = createPortal;
|
|
|
2195
2195
|
exports.createRoot = createRoot;
|
|
2196
2196
|
exports.dispose = dispose;
|
|
2197
2197
|
exports.extend = extend;
|
|
2198
|
+
exports.flushGlobalEffects = flushGlobalEffects;
|
|
2198
2199
|
exports.getRootState = getRootState;
|
|
2199
2200
|
exports.invalidate = invalidate;
|
|
2200
2201
|
exports.isRef = isRef;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var index = require('./index-
|
|
5
|
+
var index = require('./index-8539ced2.cjs.dev.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -268,6 +268,7 @@ exports.createPortal = index.createPortal;
|
|
|
268
268
|
exports.createRoot = index.createRoot;
|
|
269
269
|
exports.dispose = index.dispose;
|
|
270
270
|
exports.extend = index.extend;
|
|
271
|
+
exports.flushGlobalEffects = index.flushGlobalEffects;
|
|
271
272
|
exports.getRootState = index.getRootState;
|
|
272
273
|
exports.invalidate = index.invalidate;
|
|
273
274
|
exports.reconciler = index.reconciler;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var index = require('./index-
|
|
5
|
+
var index = require('./index-57cb2f85.cjs.prod.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -268,6 +268,7 @@ exports.createPortal = index.createPortal;
|
|
|
268
268
|
exports.createRoot = index.createRoot;
|
|
269
269
|
exports.dispose = index.dispose;
|
|
270
270
|
exports.extend = index.extend;
|
|
271
|
+
exports.flushGlobalEffects = index.flushGlobalEffects;
|
|
271
272
|
exports.getRootState = index.getRootState;
|
|
272
273
|
exports.invalidate = index.invalidate;
|
|
273
274
|
exports.reconciler = index.reconciler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, i as isRef, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-
|
|
2
|
-
export { t as ReactThreeFiber,
|
|
1
|
+
import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, i as isRef, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-6f0019d1.esm.js';
|
|
2
|
+
export { t as ReactThreeFiber, w as _roots, v as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, c as createEvents, g as createPortal, a as createRoot, k as dispose, e as extend, q as flushGlobalEffects, s as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, A as useFrame, C as useGraph, x as useInstanceHandle, D as useLoader, y as useStore, z as useThree } from './index-6f0019d1.esm.js';
|
|
3
3
|
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import * as THREE from 'three';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var index = require('../../dist/index-
|
|
5
|
+
var index = require('../../dist/index-8539ced2.cjs.dev.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -402,10 +402,12 @@ exports.addTail = index.addTail;
|
|
|
402
402
|
exports.advance = index.advance;
|
|
403
403
|
exports.applyProps = index.applyProps;
|
|
404
404
|
exports.context = index.context;
|
|
405
|
+
exports.createEvents = index.createEvents;
|
|
405
406
|
exports.createPortal = index.createPortal;
|
|
406
407
|
exports.createRoot = index.createRoot;
|
|
407
408
|
exports.dispose = index.dispose;
|
|
408
409
|
exports.extend = index.extend;
|
|
410
|
+
exports.flushGlobalEffects = index.flushGlobalEffects;
|
|
409
411
|
exports.getRootState = index.getRootState;
|
|
410
412
|
exports.invalidate = index.invalidate;
|
|
411
413
|
exports.reconciler = index.reconciler;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var index = require('../../dist/index-
|
|
5
|
+
var index = require('../../dist/index-57cb2f85.cjs.prod.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -402,10 +402,12 @@ exports.addTail = index.addTail;
|
|
|
402
402
|
exports.advance = index.advance;
|
|
403
403
|
exports.applyProps = index.applyProps;
|
|
404
404
|
exports.context = index.context;
|
|
405
|
+
exports.createEvents = index.createEvents;
|
|
405
406
|
exports.createPortal = index.createPortal;
|
|
406
407
|
exports.createRoot = index.createRoot;
|
|
407
408
|
exports.dispose = index.dispose;
|
|
408
409
|
exports.extend = index.extend;
|
|
410
|
+
exports.flushGlobalEffects = index.flushGlobalEffects;
|
|
409
411
|
exports.getRootState = index.getRootState;
|
|
410
412
|
exports.invalidate = index.invalidate;
|
|
411
413
|
exports.reconciler = index.reconciler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-
|
|
2
|
-
export { t as ReactThreeFiber,
|
|
1
|
+
import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-6f0019d1.esm.js';
|
|
2
|
+
export { t as ReactThreeFiber, w as _roots, v as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, c as createEvents, g as createPortal, a as createRoot, k as dispose, e as extend, q as flushGlobalEffects, s as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, A as useFrame, C as useGraph, x as useInstanceHandle, D as useLoader, y as useStore, z as useThree } from '../../dist/index-6f0019d1.esm.js';
|
|
3
3
|
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import * as THREE from 'three';
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -10,112 +10,160 @@
|
|
|
10
10
|
|
|
11
11
|
react-three-fiber is a <a href="https://reactjs.org/docs/codebase-overview.html#renderers">React renderer</a> for threejs.
|
|
12
12
|
|
|
13
|
+
Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.
|
|
14
|
+
|
|
13
15
|
```bash
|
|
14
16
|
npm install three @react-three/fiber
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
### Why?
|
|
18
|
-
|
|
19
|
-
Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can tap into React's ecosystem.
|
|
20
|
-
|
|
21
19
|
#### Does it have limitations?
|
|
22
20
|
|
|
23
|
-
None. Everything that works in
|
|
21
|
+
None. Everything that works in Threejs will work here without exception.
|
|
24
22
|
|
|
25
|
-
####
|
|
23
|
+
#### Is it slower than plain Threejs?
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
No. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to Reacts scheduling abilities.
|
|
28
26
|
|
|
29
|
-
####
|
|
27
|
+
#### Can it keep up with frequent feature updates to Threejs?
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
Yes. It merely expresses Threejs in JSX: `<mesh />` becomes `new THREE.Mesh()`, and that happens dynamically. If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.
|
|
32
30
|
|
|
33
31
|
### What does it look like?
|
|
34
32
|
|
|
35
33
|
<table>
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
<tbody>
|
|
35
|
+
<tr>
|
|
36
|
+
<td>Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (<a href="https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js">live demo</a>).</td>
|
|
37
|
+
<td>
|
|
38
|
+
<a href="https://codesandbox.io/s/rrppl0y8l4">
|
|
39
|
+
<img src="https://i.imgur.com/sS4ArrZ.gif" />
|
|
40
|
+
</a>
|
|
41
|
+
</td>
|
|
42
|
+
</tr>
|
|
43
|
+
</tbody>
|
|
43
44
|
</table>
|
|
44
45
|
|
|
45
|
-
#### Imports first
|
|
46
|
-
|
|
47
46
|
```jsx
|
|
47
|
+
import { createRoot } from 'react-dom/client'
|
|
48
48
|
import React, { useRef, useState } from 'react'
|
|
49
|
-
import ReactDOM from 'react-dom'
|
|
50
49
|
import { Canvas, useFrame } from '@react-three/fiber'
|
|
51
|
-
```
|
|
52
50
|
|
|
53
|
-
#### Define a component
|
|
54
|
-
|
|
55
|
-
```jsx
|
|
56
51
|
function Box(props) {
|
|
57
|
-
// This reference
|
|
58
|
-
const
|
|
59
|
-
//
|
|
60
|
-
const [hovered,
|
|
61
|
-
const [
|
|
62
|
-
//
|
|
63
|
-
useFrame(() => (
|
|
64
|
-
|
|
52
|
+
// This reference gives us direct access to the THREE.Mesh object
|
|
53
|
+
const ref = useRef()
|
|
54
|
+
// Hold state for hovered and clicked events
|
|
55
|
+
const [hovered, hover] = useState(false)
|
|
56
|
+
const [clicked, click] = useState(false)
|
|
57
|
+
// Subscribe this component to the render-loop, rotate the mesh every frame
|
|
58
|
+
useFrame((state, delta) => (ref.current.rotation.x += 0.01))
|
|
59
|
+
// Return the view, these are regular Threejs elements expressed in JSX
|
|
65
60
|
return (
|
|
66
61
|
<mesh
|
|
67
62
|
{...props}
|
|
68
|
-
ref={
|
|
69
|
-
scale={
|
|
70
|
-
onClick={(event) =>
|
|
71
|
-
onPointerOver={(event) =>
|
|
72
|
-
onPointerOut={(event) =>
|
|
73
|
-
<boxGeometry args={[1,
|
|
63
|
+
ref={ref}
|
|
64
|
+
scale={clicked ? 1.5 : 1}
|
|
65
|
+
onClick={(event) => click(!clicked)}
|
|
66
|
+
onPointerOver={(event) => hover(true)}
|
|
67
|
+
onPointerOut={(event) => hover(false)}>
|
|
68
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
74
69
|
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
|
|
75
70
|
</mesh>
|
|
76
71
|
)
|
|
77
72
|
}
|
|
73
|
+
|
|
74
|
+
createRoot(document.getElementById('root')).render(
|
|
75
|
+
<Canvas>
|
|
76
|
+
<ambientLight />
|
|
77
|
+
<pointLight position={[10, 10, 10]} />
|
|
78
|
+
<Box position={[-1.2, 0, 0]} />
|
|
79
|
+
<Box position={[1.2, 0, 0]} />
|
|
80
|
+
</Canvas>,
|
|
81
|
+
)
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
<details>
|
|
85
|
+
<summary>Show TypeScript example</summary>
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm install @types/three
|
|
89
|
+
```
|
|
81
90
|
|
|
82
|
-
|
|
91
|
+
```tsx
|
|
92
|
+
import * as THREE from 'three'
|
|
93
|
+
import { createRoot } from 'react-dom/client'
|
|
94
|
+
import React, { useRef, useState } from 'react'
|
|
95
|
+
import { Canvas, useFrame, ThreeElements } from '@react-three/fiber'
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
function Box(props: ThreeElements['mesh']) {
|
|
98
|
+
const ref = useRef<THREE.Mesh>(null!)
|
|
99
|
+
const [hovered, hover] = useState(false)
|
|
100
|
+
const [clicked, click] = useState(false)
|
|
101
|
+
useFrame((state, delta) => (ref.current.rotation.x += 0.01))
|
|
102
|
+
return (
|
|
103
|
+
<mesh
|
|
104
|
+
{...props}
|
|
105
|
+
ref={ref}
|
|
106
|
+
scale={clicked ? 1.5 : 1}
|
|
107
|
+
onClick={(event) => click(!clicked)}
|
|
108
|
+
onPointerOver={(event) => hover(true)}
|
|
109
|
+
onPointerOut={(event) => hover(false)}>
|
|
110
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
111
|
+
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
|
|
112
|
+
</mesh>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
createRoot(document.getElementById('root') as HTMLElement).render(
|
|
86
117
|
<Canvas>
|
|
87
118
|
<ambientLight />
|
|
88
119
|
<pointLight position={[10, 10, 10]} />
|
|
89
120
|
<Box position={[-1.2, 0, 0]} />
|
|
90
121
|
<Box position={[1.2, 0, 0]} />
|
|
91
122
|
</Canvas>,
|
|
92
|
-
document.getElementById('root'),
|
|
93
123
|
)
|
|
94
124
|
```
|
|
95
125
|
|
|
96
|
-
|
|
126
|
+
Live demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx
|
|
97
127
|
|
|
98
|
-
|
|
99
|
-
|
|
128
|
+
</details>
|
|
129
|
+
|
|
130
|
+
<details>
|
|
131
|
+
<summary>Show React Native example</summary>
|
|
132
|
+
|
|
133
|
+
This example relies on react 18 and uses `expo-cli`, but you can create a bare project with their template or with the `react-native` CLI.
|
|
100
134
|
|
|
101
|
-
|
|
135
|
+
```bash
|
|
136
|
+
# Install expo-cli, this will create our app
|
|
137
|
+
npm install expo-cli -g
|
|
138
|
+
# Create app and cd into it
|
|
139
|
+
expo init my-app
|
|
140
|
+
cd my-app
|
|
141
|
+
# Install dependencies
|
|
142
|
+
npm install three @react-three/fiber@beta react@rc
|
|
143
|
+
# Start
|
|
144
|
+
expo start
|
|
102
145
|
```
|
|
103
146
|
|
|
104
|
-
|
|
105
|
-
<summary>Show TypeScript example</summary>
|
|
147
|
+
Some configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:
|
|
106
148
|
|
|
107
|
-
```
|
|
108
|
-
|
|
149
|
+
```js
|
|
150
|
+
// metro.config.js
|
|
151
|
+
module.exports = {
|
|
152
|
+
resolver: {
|
|
153
|
+
sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],
|
|
154
|
+
assetExts: ['glb', 'png', 'jpg'],
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
```
|
|
109
158
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
import React, { useRef, useState } from 'react'
|
|
161
|
+
import { Canvas, useFrame } from '@react-three/fiber/native'
|
|
162
|
+
function Box(props) {
|
|
163
|
+
const mesh = useRef(null)
|
|
114
164
|
const [hovered, setHover] = useState(false)
|
|
115
165
|
const [active, setActive] = useState(false)
|
|
116
|
-
|
|
117
|
-
useFrame(() => (mesh.current.rotation.x += 0.01))
|
|
118
|
-
|
|
166
|
+
useFrame((state, delta) => (mesh.current.rotation.x += 0.01))
|
|
119
167
|
return (
|
|
120
168
|
<mesh
|
|
121
169
|
{...props}
|
|
@@ -124,32 +172,32 @@ const Box: React.FC<MeshProps> = (props) => {
|
|
|
124
172
|
onClick={(event) => setActive(!active)}
|
|
125
173
|
onPointerOver={(event) => setHover(true)}
|
|
126
174
|
onPointerOut={(event) => setHover(false)}>
|
|
127
|
-
<boxGeometry args={[1,
|
|
175
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
128
176
|
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
|
|
129
177
|
</mesh>
|
|
130
178
|
)
|
|
131
179
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
180
|
+
export default function App() {
|
|
181
|
+
return (
|
|
182
|
+
<Canvas>
|
|
183
|
+
<ambientLight />
|
|
184
|
+
<pointLight position={[10, 10, 10]} />
|
|
185
|
+
<Box position={[-1.2, 0, 0]} />
|
|
186
|
+
<Box position={[1.2, 0, 0]} />
|
|
187
|
+
</Canvas>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
142
190
|
```
|
|
143
191
|
|
|
144
192
|
</details>
|
|
145
193
|
|
|
146
194
|
---
|
|
147
195
|
|
|
148
|
-
# Documentation
|
|
196
|
+
# Documentation, tutorials, examples
|
|
197
|
+
|
|
198
|
+
Visit [docs.pmnd.rs](https://docs.pmnd.rs/react-three-fiber)
|
|
149
199
|
|
|
150
|
-
|
|
151
|
-
- [pitfalls.md](/markdown/pitfalls.md)
|
|
152
|
-
- [testing.md](/packages/test-renderer)
|
|
200
|
+
<a href="https://docs.pmnd.rs/react-three-fiber"><img src="/docs/preview.jpg"></a>
|
|
153
201
|
|
|
154
202
|
# Fundamentals
|
|
155
203
|
|
|
@@ -158,13 +206,14 @@ You need to be versed in both React and Threejs before rushing into this. If you
|
|
|
158
206
|
1. Make sure you have a [basic grasp of Threejs](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene). Keep that site open.
|
|
159
207
|
2. When you know what a scene is, a camera, mesh, geometry, material, fork the [demo above](https://github.com/pmndrs/react-three-fiber#what-does-it-look-like).
|
|
160
208
|
3. [Look up](https://threejs.org/docs/index.html#api/en/objects/Mesh) the JSX elements that you see (mesh, ambientLight, etc), _all_ threejs exports are native to three-fiber.
|
|
161
|
-
4. Try changing some values, scroll
|
|
209
|
+
4. Try changing some values, scroll through our [API](https://docs.pmnd.rs/react-three-fiber/API) to see what the various settings and hooks do.
|
|
162
210
|
|
|
163
211
|
Some reading material:
|
|
164
212
|
|
|
165
213
|
- [Threejs-docs](https://threejs.org/docs)
|
|
166
214
|
- [Threejs-examples](https://threejs.org/examples)
|
|
167
215
|
- [Threejs-fundamentals](https://threejs.org/manual/#en/fundamentals)
|
|
216
|
+
- [three.js-journey](https://threejs-journey.com)
|
|
168
217
|
- [Discover Threejs](https://discoverthreejs.com)
|
|
169
218
|
- [Do's and don'ts](https://discoverthreejs.com/tips-and-tricks) for performance and best practices
|
|
170
219
|
- [react-three-fiber alligator.io tutorial](https://alligator.io/react/react-with-threejs) by [@dghez\_](https://twitter.com/dghez_)
|
|
@@ -177,9 +226,11 @@ Some reading material:
|
|
|
177
226
|
- [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) – flexbox for react-three-fiber
|
|
178
227
|
- [`@react-three/xr`](https://github.com/pmndrs/react-xr) – VR/AR controllers and events
|
|
179
228
|
- [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) – physics based hooks
|
|
229
|
+
- [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) – real a11y for your scene
|
|
180
230
|
- [`zustand`](https://github.com/pmndrs/zustand) – state management
|
|
181
231
|
- [`react-spring`](https://github.com/pmndrs/react-spring) – a spring-physics-based animation library
|
|
182
232
|
- [`react-use-gesture`](https://github.com/pmndrs/react-use-gesture) – mouse/touch gestures
|
|
233
|
+
- [`leva`](https://github.com/pmndrs/leva) – create GUI controls in seconds
|
|
183
234
|
|
|
184
235
|
# How to contribute
|
|
185
236
|
|