Favivon - Correção

This commit is contained in:
2026-05-30 19:59:39 -03:00
parent 76ddaa815d
commit d7dfd221f0
32859 changed files with 5459654 additions and 404 deletions
+1192
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
import * as THREE from 'three';
import { type Properties } from "./utils.js";
import type { RootState, RootStore } from "./store.js";
export interface Intersection extends THREE.Intersection {
/** The event source (the object which registered the handler) */
eventObject: THREE.Object3D;
}
export interface IntersectionEvent<TSourceEvent> extends Intersection {
/** The event source (the object which registered the handler) */
eventObject: THREE.Object3D;
/** An array of intersections */
intersections: Intersection[];
/** vec3.set(pointer.x, pointer.y, 0).unproject(camera) */
unprojectedPoint: THREE.Vector3;
/** Normalized event coordinates */
pointer: THREE.Vector2;
/** Delta between first click and this event */
delta: number;
/** The ray that pierced it */
ray: THREE.Ray;
/** The camera that was used by the raycaster */
camera: Camera;
/** stopPropagation will stop underlying handlers from firing */
stopPropagation: () => void;
/** The original host event */
nativeEvent: TSourceEvent;
/** If the event was stopped by calling stopPropagation */
stopped: boolean;
}
export type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
export type ThreeEvent<TEvent> = IntersectionEvent<TEvent> & Properties<TEvent>;
export type DomEvent = PointerEvent | MouseEvent | WheelEvent;
export interface Events {
onClick: EventListener;
onContextMenu: EventListener;
onDoubleClick: EventListener;
onWheel: EventListener;
onPointerDown: EventListener;
onPointerUp: EventListener;
onPointerLeave: EventListener;
onPointerMove: EventListener;
onPointerCancel: EventListener;
onLostPointerCapture: EventListener;
}
export interface EventHandlers {
onClick?: (event: ThreeEvent<MouseEvent>) => void;
onContextMenu?: (event: ThreeEvent<MouseEvent>) => void;
onDoubleClick?: (event: ThreeEvent<MouseEvent>) => void;
onPointerUp?: (event: ThreeEvent<PointerEvent>) => void;
onPointerDown?: (event: ThreeEvent<PointerEvent>) => void;
onPointerOver?: (event: ThreeEvent<PointerEvent>) => void;
onPointerOut?: (event: ThreeEvent<PointerEvent>) => void;
onPointerEnter?: (event: ThreeEvent<PointerEvent>) => void;
onPointerLeave?: (event: ThreeEvent<PointerEvent>) => void;
onPointerMove?: (event: ThreeEvent<PointerEvent>) => void;
onPointerMissed?: (event: MouseEvent) => void;
onPointerCancel?: (event: ThreeEvent<PointerEvent>) => void;
onWheel?: (event: ThreeEvent<WheelEvent>) => void;
onLostPointerCapture?: (event: ThreeEvent<PointerEvent>) => void;
}
export type FilterFunction = (items: THREE.Intersection[], state: RootState) => THREE.Intersection[];
export type ComputeFunction = (event: DomEvent, root: RootState, previous?: RootState) => void;
export interface EventManager<TTarget> {
/** Determines if the event layer is active */
enabled: boolean;
/** Event layer priority, higher prioritized layers come first and may stop(-propagate) lower layer */
priority: number;
/** The compute function needs to set up the raycaster and an xy- pointer */
compute?: ComputeFunction;
/** The filter can re-order or re-structure the intersections */
filter?: FilterFunction;
/** The target node the event layer is tied to */
connected?: TTarget;
/** All the pointer event handlers through which the host forwards native events */
handlers?: Events;
/** Allows re-connecting to another target */
connect?: (target: TTarget) => void;
/** Removes all existing events handlers from the target */
disconnect?: () => void;
/** Triggers a onPointerMove with the last known event. This can be useful to enable raycasting without
* explicit user interaction, for instance when the camera moves a hoverable object underneath the cursor.
*/
update?: () => void;
}
export interface PointerCaptureTarget {
intersection: Intersection;
target: Element;
}
export declare function removeInteractivity(store: RootStore, object: THREE.Object3D): void;
export declare function createEvents(store: RootStore): {
handlePointer: (name: string) => (event: DomEvent) => void;
};
+53
View File
@@ -0,0 +1,53 @@
import * as THREE from 'three';
import * as React from 'react';
import { RootState, RenderCallback, RootStore } from "./store.js";
import { ObjectMap } from "./utils.js";
import type { Instance, ConstructorRepresentation } from "./reconciler.js";
/**
* Exposes an object's {@link Instance}.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
*
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
*/
export declare function useInstanceHandle<T>(ref: React.RefObject<T>): React.RefObject<Instance<T>>;
/**
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
*/
export declare function useStore(): RootStore;
/**
* Accesses R3F's internal state, containing renderer, canvas, scene, etc.
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
*/
export declare function useThree<T = RootState>(selector?: (state: RootState) => T, equalityFn?: <T>(state: T, newState: T) => boolean): T;
/**
* Executes a callback before render in a shared frame loop.
* Can order effects with render priority or manually render with a positive priority.
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
*/
export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
/**
* Returns a node graph of an object with named nodes & materials.
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
*/
export declare function useGraph(object: THREE.Object3D): ObjectMap;
type InputLike = string | string[] | string[][] | Readonly<string | string[] | string[][]>;
type LoaderLike = THREE.Loader<any, InputLike>;
type GLTFLike = {
scene: THREE.Object3D;
};
type LoaderInstance<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> = T extends ConstructorRepresentation<LoaderLike> ? InstanceType<T> : T;
export type LoaderResult<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> = Awaited<ReturnType<LoaderInstance<T>['loadAsync']>> extends infer R ? R extends GLTFLike ? R & ObjectMap : R : never;
export type Extensions<T extends LoaderLike | ConstructorRepresentation<LoaderLike>> = (loader: LoaderInstance<T>) => void;
/**
* Synchronously loads and caches assets with a three loader.
*
* Note: this hook's caller must be wrapped with `React.Suspense`
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
*/
export declare function useLoader<I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(loader: L, input: I, extensions?: Extensions<L>, onProgress?: (event: ProgressEvent<EventTarget>) => void): I extends any[] ? LoaderResult<L>[] : LoaderResult<L>;
export declare namespace useLoader {
var preload: <I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(loader: L, input: I, extensions?: Extensions<L> | undefined) => void;
var clear: <I extends InputLike, L extends LoaderLike | ConstructorRepresentation<LoaderLike>>(loader: L, input: I) => void;
}
export {};
+13
View File
@@ -0,0 +1,13 @@
export type { Intersection, ThreeEvent, DomEvent, Events, EventHandlers, FilterFunction, ComputeFunction, EventManager, } from "./events.js";
export { createEvents } from "./events.js";
export * from "./hooks.js";
export type { GlobalRenderCallback, GlobalEffectType } from "./loop.js";
export { flushGlobalEffects, addEffect, addAfterEffect, addTail, invalidate, advance } from "./loop.js";
export type { AttachFnType, AttachType, ConstructorRepresentation, Catalogue, Args, InstanceProps, Instance, } from "./reconciler.js";
export { extend, reconciler } from "./reconciler.js";
export type { ReconcilerRoot, GLProps, CameraProps, RenderProps, InjectState } from "./renderer.js";
export { _roots, createRoot, unmountComponentAtNode, createPortal, flushSync } from "./renderer.js";
export type { Subscription, Dpr, Size, Viewport, RenderCallback, Frameloop, Performance, Renderer, XRManager, RootState, RootStore, } from "./store.js";
export { context } from "./store.js";
export type { ObjectMap, Camera, Disposable, Act } from "./utils.js";
export { applyProps, getRootState, dispose, act, buildGraph } from "./utils.js";
+31
View File
@@ -0,0 +1,31 @@
/// <reference types="webxr" />
import type { RootState } from "./store.js";
export type GlobalRenderCallback = (timestamp: number) => void;
/**
* Adds a global render callback which is called each frame.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
*/
export declare const addEffect: (callback: GlobalRenderCallback) => () => void;
/**
* Adds a global after-render callback which is called each frame.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
*/
export declare const addAfterEffect: (callback: GlobalRenderCallback) => () => void;
/**
* Adds a global callback which is called when rendering stops.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
*/
export declare const addTail: (callback: GlobalRenderCallback) => () => void;
export type GlobalEffectType = 'before' | 'after' | 'tail';
export declare function flushGlobalEffects(type: GlobalEffectType, timestamp: number): void;
export declare function loop(timestamp: number): void;
/**
* Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
*/
export declare function invalidate(state?: RootState, frames?: number): void;
/**
* Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
*/
export declare function advance(timestamp: number, runGlobalEffects?: boolean, state?: RootState, frame?: XRFrame): void;
@@ -0,0 +1,50 @@
import * as THREE from 'three';
import * as React from 'react';
import Reconciler from '../../react-reconciler/index.js';
import { IsAllOptional } from "./utils.js";
import type { RootStore } from "./store.js";
import { type EventHandlers } from "./events.js";
import type { ThreeElement } from "../three-types.js";
export interface Root {
fiber: Reconciler.FiberRoot;
store: RootStore;
}
export type AttachFnType<O = any> = (parent: any, self: O) => () => void;
export type AttachType<O = any> = string | AttachFnType<O>;
export type ConstructorRepresentation<T = any> = new (...args: any[]) => T;
export interface Catalogue {
[name: string]: ConstructorRepresentation;
}
export type Args<T> = T extends ConstructorRepresentation ? T extends typeof THREE.Color ? [r: number, g: number, b: number] | [color: THREE.ColorRepresentation] : ConstructorParameters<T> : any[];
type ArgsProp<P> = P extends ConstructorRepresentation ? IsAllOptional<ConstructorParameters<P>> extends true ? {
args?: Args<P>;
} : {
args: Args<P>;
} : {
args: unknown[];
};
export type InstanceProps<T = any, P = any> = ArgsProp<P> & {
object?: T;
dispose?: null;
attach?: AttachType<T>;
onUpdate?: (self: T) => void;
};
export interface Instance<O = any> {
root: RootStore;
type: string;
parent: Instance | null;
children: Instance[];
props: InstanceProps<O> & Record<string, unknown>;
object: O & {
__r3f?: Instance<O>;
};
eventCount: number;
handlers: Partial<EventHandlers>;
attach?: AttachType<O>;
previousAttach?: any;
isHidden: boolean;
}
export declare function extend<T extends ConstructorRepresentation>(objects: T): React.ExoticComponent<ThreeElement<T>>;
export declare function extend<T extends Catalogue>(objects: T): void;
export declare const reconciler: Reconciler.Reconciler<RootStore, Instance<any>, void, Instance<any>, never, any>;
export {};
@@ -0,0 +1,89 @@
import * as React from 'react';
import * as THREE from 'three';
import type { ThreeElement } from "../three-types.js";
import { ComputeFunction, EventManager } from "./events.js";
import { Root } from "./reconciler.js";
import { Dpr, Frameloop, Performance, Renderer, RootState, RootStore, Size } from "./store.js";
import { type Properties, Camera } from "./utils.js";
interface OffscreenCanvas extends EventTarget {
}
export declare const _roots: Map<HTMLCanvasElement | OffscreenCanvas, Root>;
export type DefaultGLProps = Omit<THREE.WebGLRendererParameters, 'canvas'> & {
canvas: HTMLCanvasElement | OffscreenCanvas;
};
export type GLProps = Renderer | ((defaultProps: DefaultGLProps) => Renderer) | ((defaultProps: DefaultGLProps) => Promise<Renderer>) | Partial<Properties<THREE.WebGLRenderer> | THREE.WebGLRendererParameters>;
export type CameraProps = (Camera | Partial<ThreeElement<typeof THREE.Camera> & ThreeElement<typeof THREE.PerspectiveCamera> & ThreeElement<typeof THREE.OrthographicCamera>>) & {
/** Flags the camera as manual, putting projection into your own hands */
manual?: boolean;
};
export interface RenderProps<TCanvas extends HTMLCanvasElement | OffscreenCanvas> {
/** A threejs renderer instance or props that go into the default renderer */
gl?: GLProps;
/** Dimensions to fit the renderer to. Will measure canvas dimensions if omitted */
size?: Size;
/**
* Enables shadows (by default PCFsoft). Can accept `gl.shadowMap` options for fine-tuning,
* but also strings: 'basic' | 'percentage' | 'soft' | 'variance'.
* @see https://threejs.org/docs/#api/en/renderers/WebGLRenderer.shadowMap
*/
shadows?: boolean | 'basic' | 'percentage' | 'soft' | 'variance' | Partial<THREE.WebGLShadowMap>;
/**
* Disables three r139 color management.
* @see https://threejs.org/docs/#manual/en/introduction/Color-management
*/
legacy?: boolean;
/** Switch off automatic sRGB encoding and gamma correction */
linear?: boolean;
/** Use `THREE.NoToneMapping` instead of `THREE.ACESFilmicToneMapping` */
flat?: boolean;
/** Creates an orthographic camera */
orthographic?: boolean;
/**
* R3F's render mode. Set to `demand` to only render on state change or `never` to take control.
* @see https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#on-demand-rendering
*/
frameloop?: Frameloop;
/**
* R3F performance options for adaptive performance.
* @see https://docs.pmnd.rs/react-three-fiber/advanced/scaling-performance#movement-regression
*/
performance?: Partial<Omit<Performance, 'regress'>>;
/** Target pixel ratio. Can clamp between a range: `[min, max]` */
dpr?: Dpr;
/** Props that go into the default raycaster */
raycaster?: Partial<THREE.Raycaster>;
/** A `THREE.Scene` instance or props that go into the default scene */
scene?: THREE.Scene | Partial<THREE.Scene>;
/** A `THREE.Camera` instance or props that go into the default camera */
camera?: CameraProps;
/** An R3F event manager to manage elements' pointer events */
events?: (store: RootStore) => EventManager<HTMLElement>;
/** Callback after the canvas has rendered (but not yet committed) */
onCreated?: (state: RootState) => void;
/** Response for pointer clicks that have missed any target */
onPointerMissed?: (event: MouseEvent) => void;
}
export interface ReconcilerRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas> {
configure: (config?: RenderProps<TCanvas>) => Promise<ReconcilerRoot<TCanvas>>;
render: (element: React.ReactNode) => RootStore;
unmount: () => void;
}
export declare function createRoot<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(canvas: TCanvas): ReconcilerRoot<TCanvas>;
export declare function unmountComponentAtNode<TCanvas extends HTMLCanvasElement | OffscreenCanvas>(canvas: TCanvas, callback?: (canvas: TCanvas) => void): void;
export type InjectState = Partial<Omit<RootState, 'events'> & {
events?: {
enabled?: boolean;
priority?: number;
compute?: ComputeFunction;
connected?: any;
};
}>;
export declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): React.JSX.Element;
/**
* Force React to flush any updates inside the provided callback synchronously and immediately.
* All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
* Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
* having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
*/
export declare function flushSync<R>(fn: () => R): R;
export {};
+130
View File
@@ -0,0 +1,130 @@
/// <reference types="webxr" />
import * as THREE from 'three';
import * as React from 'react';
import { type StoreApi } from 'zustand';
import { type UseBoundStoreWithEqualityFn } from 'zustand/traditional';
import type { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from "./events.js";
import { type Camera } from "./utils.js";
export interface Intersection extends THREE.Intersection {
eventObject: THREE.Object3D;
}
export type Subscription = {
ref: React.RefObject<RenderCallback>;
priority: number;
store: RootStore;
};
export type Dpr = number | [min: number, max: number];
export interface Size {
width: number;
height: number;
top: number;
left: number;
}
export type Frameloop = 'always' | 'demand' | 'never';
export interface Viewport extends Size {
/** The initial pixel ratio */
initialDpr: number;
/** Current pixel ratio */
dpr: number;
/** size.width / viewport.width */
factor: number;
/** Camera distance */
distance: number;
/** Camera aspect ratio: width / height */
aspect: number;
}
export type RenderCallback = (state: RootState, delta: number, frame?: XRFrame) => void;
export interface Performance {
/** Current performance normal, between min and max */
current: number;
/** How low the performance can go, between 0 and max */
min: number;
/** How high the performance can go, between min and max */
max: number;
/** Time until current returns to max in ms */
debounce: number;
/** Sets current to min, puts the system in regression */
regress: () => void;
}
export interface Renderer {
render: (scene: THREE.Scene, camera: THREE.Camera) => any;
}
export declare const isRenderer: (def: any) => boolean;
export interface InternalState {
interaction: THREE.Object3D[];
hovered: Map<string, ThreeEvent<DomEvent>>;
subscribers: Subscription[];
capturedMap: Map<number, Map<THREE.Object3D, PointerCaptureTarget>>;
initialClick: [x: number, y: number];
initialHits: THREE.Object3D[];
lastEvent: React.RefObject<DomEvent | null>;
active: boolean;
priority: number;
frames: number;
subscribe: (callback: React.RefObject<RenderCallback>, priority: number, store: RootStore) => () => void;
}
export interface XRManager {
connect: () => void;
disconnect: () => void;
}
export interface RootState {
/** Set current state */
set: StoreApi<RootState>['setState'];
/** Get current state */
get: StoreApi<RootState>['getState'];
/** The instance of the renderer */
gl: THREE.WebGLRenderer;
/** Default camera */
camera: Camera;
/** Default scene */
scene: THREE.Scene;
/** Default raycaster */
raycaster: THREE.Raycaster;
/** Default clock */
clock: THREE.Clock;
/** Event layer interface, contains the event handler and the node they're connected to */
events: EventManager<any>;
/** XR interface */
xr: XRManager;
/** Currently used controls */
controls: THREE.EventDispatcher | null;
/** Normalized event coordinates */
pointer: THREE.Vector2;
/** @deprecated Normalized event coordinates, use "pointer" instead! */
mouse: THREE.Vector2;
legacy: boolean;
/** Shortcut to gl.outputColorSpace = THREE.LinearSRGBColorSpace */
linear: boolean;
/** Shortcut to gl.toneMapping = NoTonemapping */
flat: boolean;
/** Render loop flags */
frameloop: Frameloop;
performance: Performance;
/** Reactive pixel-size of the canvas */
size: Size;
/** Reactive size of the viewport in threejs units */
viewport: Viewport & {
getCurrentViewport: (camera?: Camera, target?: THREE.Vector3 | Parameters<THREE.Vector3['set']>, size?: Size) => Omit<Viewport, 'dpr' | 'initialDpr'>;
};
/** Flags the canvas for render, but doesn't render in itself */
invalidate: (frames?: number) => void;
/** Advance (render) one step */
advance: (timestamp: number, runGlobalEffects?: boolean) => void;
/** Shortcut to setting the event layer */
setEvents: (events: Partial<EventManager<any>>) => void;
/** Shortcut to manual sizing */
setSize: (width: number, height: number, top?: number, left?: number) => void;
/** Shortcut to manual setting the pixel ratio */
setDpr: (dpr: Dpr) => void;
/** Shortcut to setting frameloop flags */
setFrameloop: (frameloop: Frameloop) => void;
/** When the canvas was clicked but nothing was hit */
onPointerMissed?: (event: MouseEvent) => void;
/** If this state model is layered (via createPortal) then this contains the previous layer */
previousRoot?: RootStore;
/** Internals */
internal: InternalState;
}
export type RootStore = UseBoundStoreWithEqualityFn<StoreApi<RootState>>;
export declare const context: React.Context<RootStore>;
export declare const createStore: (invalidate: (state?: RootState, frames?: number) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState, frame?: XRFrame) => void) => RootStore;
+262
View File
@@ -0,0 +1,262 @@
import * as THREE from 'three';
import * as React from 'react';
import { Instance } from "./reconciler.js";
import type { Dpr, RootStore, Size } from "./store.js";
export type NonFunctionKeys<P> = {
[K in keyof P]-?: P[K] extends Function ? never : K;
}[keyof P];
export type Overwrite<P, O> = Omit<P, NonFunctionKeys<O>> & O;
export type Properties<T> = Pick<T, NonFunctionKeys<T>>;
export type Mutable<P> = {
[K in keyof P]: P[K] | Readonly<P[K]>;
};
export type IsOptional<T> = undefined extends T ? true : false;
export type IsAllOptional<T extends any[]> = T extends [infer First, ...infer Rest] ? IsOptional<First> extends true ? IsAllOptional<Rest> : false : true;
/**
* Returns the instance's initial (outmost) root.
*/
export declare function findInitialRoot<T>(instance: Instance<T>): RootStore;
export type Act = <T = any>(cb: () => Promise<T>) => Promise<T>;
/**
* Safely flush async effects when testing, simulating a legacy root.
* @deprecated Import from React instead. import { act } from 'react'
*/
export declare const act: Act;
export type Camera = (THREE.OrthographicCamera | THREE.PerspectiveCamera) & {
manual?: boolean;
};
export declare const isOrthographicCamera: (def: Camera) => def is THREE.OrthographicCamera;
export declare const isRef: (obj: any) => obj is React.RefObject<unknown>;
export declare const isColorRepresentation: (value: unknown) => value is THREE.ColorRepresentation;
/**
* An SSR-friendly useLayoutEffect.
*
* React currently throws a warning when using useLayoutEffect on the server.
* To get around it, we can conditionally useEffect on the server (no-op) and
* useLayoutEffect elsewhere.
*
* @see https://github.com/facebook/react/issues/14927
*/
export declare const useIsomorphicLayoutEffect: typeof React.useLayoutEffect;
export declare function useMutableCallback<T>(fn: T): React.RefObject<T>;
export type Bridge = React.FC<{
children?: React.ReactNode;
}>;
/**
* Bridges renderer Context and StrictMode from a primary renderer.
*/
export declare function useBridge(): Bridge;
export type SetBlock = false | Promise<null> | null;
export type UnblockProps = {
set: React.Dispatch<React.SetStateAction<SetBlock>>;
children: React.ReactNode;
};
export declare function Block({ set }: Omit<UnblockProps, 'children'>): null;
export declare const ErrorBoundary: {
new (props: {
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}): {
state: {
error: boolean;
};
componentDidCatch(err: Error): void;
render(): React.ReactNode;
context: unknown;
setState<K extends "error">(state: {
error: boolean;
} | ((prevState: Readonly<{
error: boolean;
}>, props: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>) => {
error: boolean;
} | Pick<{
error: boolean;
}, K> | null) | Pick<{
error: boolean;
}, K> | null, callback?: (() => void) | undefined): void;
forceUpdate(callback?: (() => void) | undefined): void;
readonly props: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>;
componentDidMount?(): void;
shouldComponentUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): boolean;
componentWillUnmount?(): void;
getSnapshotBeforeUpdate?(prevProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, prevState: Readonly<{
error: boolean;
}>): any;
componentDidUpdate?(prevProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, prevState: Readonly<{
error: boolean;
}>, snapshot?: any): void;
componentWillMount?(): void;
UNSAFE_componentWillMount?(): void;
componentWillReceiveProps?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextContext: any): void;
UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextContext: any): void;
componentWillUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): void;
UNSAFE_componentWillUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): void;
};
new (props: {
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}, context: any): {
state: {
error: boolean;
};
componentDidCatch(err: Error): void;
render(): React.ReactNode;
context: unknown;
setState<K extends "error">(state: {
error: boolean;
} | ((prevState: Readonly<{
error: boolean;
}>, props: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>) => {
error: boolean;
} | Pick<{
error: boolean;
}, K> | null) | Pick<{
error: boolean;
}, K> | null, callback?: (() => void) | undefined): void;
forceUpdate(callback?: (() => void) | undefined): void;
readonly props: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>;
componentDidMount?(): void;
shouldComponentUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): boolean;
componentWillUnmount?(): void;
getSnapshotBeforeUpdate?(prevProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, prevState: Readonly<{
error: boolean;
}>): any;
componentDidUpdate?(prevProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, prevState: Readonly<{
error: boolean;
}>, snapshot?: any): void;
componentWillMount?(): void;
UNSAFE_componentWillMount?(): void;
componentWillReceiveProps?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextContext: any): void;
UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextContext: any): void;
componentWillUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): void;
UNSAFE_componentWillUpdate?(nextProps: Readonly<{
set: React.Dispatch<Error | undefined>;
children: React.ReactNode;
}>, nextState: Readonly<{
error: boolean;
}>, nextContext: any): void;
};
getDerivedStateFromError: () => {
error: boolean;
};
contextType?: React.Context<any> | undefined;
propTypes?: any;
};
export interface ObjectMap {
nodes: {
[name: string]: THREE.Object3D;
};
materials: {
[name: string]: THREE.Material;
};
meshes: {
[name: string]: THREE.Mesh;
};
}
export declare function calculateDpr(dpr: Dpr): number;
/**
* Returns instance root state
*/
export declare function getRootState<T extends THREE.Object3D = THREE.Object3D>(obj: T): import("./store.js").RootState | undefined;
export interface EquConfig {
/** Compare arrays by reference equality a === b (default), or by shallow equality */
arrays?: 'reference' | 'shallow';
/** Compare objects by reference equality a === b (default), or by shallow equality */
objects?: 'reference' | 'shallow';
/** If true the keys in both a and b must match 1:1 (default), if false a's keys must intersect b's */
strict?: boolean;
}
export declare const is: {
obj: (a: any) => boolean;
fun: (a: any) => a is Function;
str: (a: any) => a is string;
num: (a: any) => a is number;
boo: (a: any) => a is boolean;
und: (a: any) => boolean;
nul: (a: any) => boolean;
arr: (a: any) => boolean;
equ(a: any, b: any, { arrays, objects, strict }?: EquConfig): boolean;
};
export declare function buildGraph(object: THREE.Object3D): ObjectMap;
export interface Disposable {
type?: string;
dispose?: () => void;
}
export declare function dispose<T extends Disposable>(obj: T): void;
export declare const REACT_INTERNAL_PROPS: string[];
export declare function getInstanceProps<T = any>(pendingProps: Record<string, unknown>): Instance<T>['props'];
export declare function prepare<T = any>(target: T, root: RootStore, type: string, props: Instance<T>['props']): Instance<T>;
export declare function resolve(root: any, key: string): {
root: any;
key: string;
target: any;
};
export declare function attach(parent: Instance, child: Instance): void;
export declare function detach(parent: Instance, child: Instance): void;
export declare const RESERVED_PROPS: string[];
export declare function diffProps<T = any>(instance: Instance<T>, newProps: Instance<T>['props']): Instance<T>['props'];
export declare function applyProps<T = any>(object: Instance<T>['object'], props: Instance<T>['props']): Instance<T>['object'];
export declare function invalidateInstance(instance: Instance): void;
export declare function updateCamera(camera: Camera, size: Size): void;
export declare const isObject3D: (object: any) => object is THREE.Object3D<THREE.Object3DEventMap>;
+6
View File
@@ -0,0 +1,6 @@
import * as ReactThreeFiber from "./three-types.js";
export { ReactThreeFiber };
export * from "./three-types.js";
export * from "./core/index.js";
export * from "./web/Canvas.js";
export { createPointerEvents as events } from "./web/events.js";
+6
View File
@@ -0,0 +1,6 @@
import * as ReactThreeFiber from "./three-types.js";
export { ReactThreeFiber };
export * from "./three-types.js";
export * from "./core/index.js";
export * from "./native/Canvas.js";
export { createTouchEvents as events } from "./native/events.js";
@@ -0,0 +1,13 @@
import * as React from 'react';
import { View, type ViewProps, type ViewStyle } from 'react-native';
import { RenderProps } from "../core/index.js";
export interface CanvasProps extends Omit<RenderProps<HTMLCanvasElement>, 'size' | 'dpr'>, Omit<ViewProps, 'children'> {
children?: React.ReactNode;
style?: ViewStyle;
ref?: React.Ref<View>;
}
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
export declare function Canvas(props: CanvasProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
import { RootStore } from "../core/store.js";
import { EventManager } from "../core/events.js";
/** Default R3F event manager for react-native */
export declare function createTouchEvents(store: RootStore): EventManager<HTMLElement>;
+68
View File
@@ -0,0 +1,68 @@
import type * as THREE from 'three';
import type { Args, EventHandlers, InstanceProps, ConstructorRepresentation } from "./core/index.js";
import type { Overwrite, Mutable } from "./core/utils.js";
type MutableOrReadonlyParameters<T extends (...args: any) => any> = Parameters<T> | Readonly<Parameters<T>>;
export interface MathRepresentation {
set(...args: number[]): any;
}
export interface VectorRepresentation extends MathRepresentation {
setScalar(value: number): any;
}
export type MathTypes = MathRepresentation | THREE.Euler | THREE.Color;
export type MathType<T extends MathTypes> = T extends THREE.Color ? Args<typeof THREE.Color> | THREE.ColorRepresentation : T extends VectorRepresentation | THREE.Layers | THREE.Euler ? T | MutableOrReadonlyParameters<T['set']> | number : T | MutableOrReadonlyParameters<T['set']>;
export type MathProps<P> = {
[K in keyof P as P[K] extends MathTypes ? K : never]: P[K] extends MathTypes ? MathType<P[K]> : never;
};
export type Vector2 = MathType<THREE.Vector2>;
export type Vector3 = MathType<THREE.Vector3>;
export type Vector4 = MathType<THREE.Vector4>;
export type Color = MathType<THREE.Color>;
export type Layers = MathType<THREE.Layers>;
export type Quaternion = MathType<THREE.Quaternion>;
export type Euler = MathType<THREE.Euler>;
export type Matrix3 = MathType<THREE.Matrix3>;
export type Matrix4 = MathType<THREE.Matrix4>;
export interface RaycastableRepresentation {
raycast(raycaster: THREE.Raycaster, intersects: THREE.Intersection[]): void;
}
export type EventProps<P> = P extends RaycastableRepresentation ? Partial<EventHandlers> : {};
export interface ReactProps<P> {
children?: React.ReactNode;
ref?: React.Ref<P>;
key?: React.Key;
}
export type ElementProps<T extends ConstructorRepresentation, P = InstanceType<T>> = Partial<Overwrite<P, MathProps<P> & ReactProps<P> & EventProps<P>>>;
export type ThreeElement<T extends ConstructorRepresentation> = Mutable<Overwrite<ElementProps<T>, Omit<InstanceProps<InstanceType<T>, T>, 'object'>>>;
export type ThreeToJSXElements<T extends Record<string, any>> = {
[K in keyof T & string as Uncapitalize<K>]: T[K] extends ConstructorRepresentation ? ThreeElement<T[K]> : never;
};
type ThreeExports = typeof THREE;
type ThreeElementsImpl = ThreeToJSXElements<ThreeExports>;
export interface ThreeElements extends Omit<ThreeElementsImpl, 'audio' | 'source' | 'line' | 'path'> {
primitive: Omit<ThreeElement<any>, 'args'> & {
object: object;
};
threeAudio: ThreeElementsImpl['audio'];
threeSource: ThreeElementsImpl['source'];
threeLine: ThreeElementsImpl['line'];
threePath: ThreeElementsImpl['path'];
}
declare module 'react' {
namespace JSX {
interface IntrinsicElements extends ThreeElements {
}
}
}
declare module 'react/jsx-runtime' {
namespace JSX {
interface IntrinsicElements extends ThreeElements {
}
}
}
declare module 'react/jsx-dev-runtime' {
namespace JSX {
interface IntrinsicElements extends ThreeElements {
}
}
}
export {};
+23
View File
@@ -0,0 +1,23 @@
import * as React from 'react';
import { Options as ResizeOptions } from 'react-use-measure';
import { RenderProps } from "../core/index.js";
export interface CanvasProps extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
ref?: React.Ref<HTMLCanvasElement>;
/** Canvas fallback content, similar to img's alt prop */
fallback?: React.ReactNode;
/**
* Options to pass to useMeasure.
* @see https://github.com/pmndrs/react-use-measure#api
*/
resize?: ResizeOptions;
/** The target where events are being subscribed to, default: the div that wraps canvas */
eventSource?: HTMLElement | React.RefObject<HTMLElement>;
/** The event prefix that is cast into canvas pointer x/y events, default: "offset" */
eventPrefix?: 'offset' | 'client' | 'page' | 'layer' | 'screen';
}
/**
* A DOM canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
export declare function Canvas(props: CanvasProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
import { RootStore } from "../core/store.js";
import { EventManager } from "../core/events.js";
/** Default R3F event manager for web */
export declare function createPointerEvents(store: RootStore): EventManager<HTMLElement>;
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
export * from "./declarations/src/index";
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhY3QtdGhyZWUtZmliZXIuY2pzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuL2RlY2xhcmF0aW9ucy9zcmMvaW5kZXguZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSJ9
+220
View File
@@ -0,0 +1,220 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var events = require('./events-358c3764.cjs.dev.js');
var React = require('react');
var THREE = require('three');
var useMeasure = require('react-use-measure');
var itsFine = require('its-fine');
var jsxRuntime = require('react/jsx-runtime');
require('zustand/traditional');
require('suspend-react');
require('scheduler');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
function CanvasImpl({
ref,
children,
fallback,
resize,
style,
gl,
events: events$1 = events.createPointerEvents,
eventSource,
eventPrefix,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React__namespace.useMemo(() => events.extend(THREE__namespace), []);
const Bridge = events.useBridge();
const [containerRef, containerRect] = useMeasure__default["default"]({
scroll: true,
debounce: {
scroll: 50,
resize: 0
},
...resize
});
const canvasRef = React__namespace.useRef(null);
const divRef = React__namespace.useRef(null);
React__namespace.useImperativeHandle(ref, () => canvasRef.current);
const handlePointerMissed = events.useMutableCallback(onPointerMissed);
const [block, setBlock] = React__namespace.useState(false);
const [error, setError] = React__namespace.useState(false);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const root = React__namespace.useRef(null);
events.useIsomorphicLayoutEffect(() => {
const canvas = canvasRef.current;
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
if (!root.current) root.current = events.createRoot(canvas);
async function run() {
await root.current.configure({
gl,
scene,
events: events$1,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
size: containerRect,
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
onCreated: state => {
// Connect to event source
state.events.connect == null ? void 0 : state.events.connect(eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
// Set up compute function
if (eventPrefix) {
state.setEvents({
compute: (event, state) => {
const x = event[eventPrefix + 'X'];
const y = event[eventPrefix + 'Y'];
state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
}
});
}
// Call onCreated callback
onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsxRuntime.jsx(React__namespace.Suspense, {
fallback: /*#__PURE__*/jsxRuntime.jsx(events.Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React__namespace.useEffect(() => {
const canvas = canvasRef.current;
if (canvas) return () => events.unmountComponentAtNode(canvas);
}, []);
// When the event source is not this div, we need to set pointer-events to none
// Or else the canvas will block events from reaching the event source
const pointerEvents = eventSource ? 'none' : 'auto';
return /*#__PURE__*/jsxRuntime.jsx("div", {
ref: divRef,
style: {
position: 'relative',
width: '100%',
height: '100%',
overflow: 'hidden',
pointerEvents,
...style
},
...props,
children: /*#__PURE__*/jsxRuntime.jsx("div", {
ref: containerRef,
style: {
width: '100%',
height: '100%'
},
children: /*#__PURE__*/jsxRuntime.jsx("canvas", {
ref: canvasRef,
style: {
display: 'block'
},
children: fallback
})
})
});
}
/**
* A DOM canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsxRuntime.jsx(itsFine.FiberProvider, {
children: /*#__PURE__*/jsxRuntime.jsx(CanvasImpl, {
...props
})
});
}
exports.ReactThreeFiber = events.threeTypes;
exports._roots = events._roots;
exports.act = events.act;
exports.addAfterEffect = events.addAfterEffect;
exports.addEffect = events.addEffect;
exports.addTail = events.addTail;
exports.advance = events.advance;
exports.applyProps = events.applyProps;
exports.buildGraph = events.buildGraph;
exports.context = events.context;
exports.createEvents = events.createEvents;
exports.createPortal = events.createPortal;
exports.createRoot = events.createRoot;
exports.dispose = events.dispose;
exports.events = events.createPointerEvents;
exports.extend = events.extend;
exports.flushGlobalEffects = events.flushGlobalEffects;
exports.flushSync = events.flushSync;
exports.getRootState = events.getRootState;
exports.invalidate = events.invalidate;
exports.reconciler = events.reconciler;
exports.unmountComponentAtNode = events.unmountComponentAtNode;
exports.useFrame = events.useFrame;
exports.useGraph = events.useGraph;
exports.useInstanceHandle = events.useInstanceHandle;
exports.useLoader = events.useLoader;
exports.useStore = events.useStore;
exports.useThree = events.useThree;
exports.Canvas = Canvas;
+7
View File
@@ -0,0 +1,7 @@
'use strict';
if (process.env.NODE_ENV === "production") {
module.exports = require("./react-three-fiber.cjs.prod.js");
} else {
module.exports = require("./react-three-fiber.cjs.dev.js");
}
+220
View File
@@ -0,0 +1,220 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var events = require('./events-238e0986.cjs.prod.js');
var React = require('react');
var THREE = require('three');
var useMeasure = require('react-use-measure');
var itsFine = require('its-fine');
var jsxRuntime = require('react/jsx-runtime');
require('zustand/traditional');
require('suspend-react');
require('scheduler');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
function CanvasImpl({
ref,
children,
fallback,
resize,
style,
gl,
events: events$1 = events.createPointerEvents,
eventSource,
eventPrefix,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React__namespace.useMemo(() => events.extend(THREE__namespace), []);
const Bridge = events.useBridge();
const [containerRef, containerRect] = useMeasure__default["default"]({
scroll: true,
debounce: {
scroll: 50,
resize: 0
},
...resize
});
const canvasRef = React__namespace.useRef(null);
const divRef = React__namespace.useRef(null);
React__namespace.useImperativeHandle(ref, () => canvasRef.current);
const handlePointerMissed = events.useMutableCallback(onPointerMissed);
const [block, setBlock] = React__namespace.useState(false);
const [error, setError] = React__namespace.useState(false);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const root = React__namespace.useRef(null);
events.useIsomorphicLayoutEffect(() => {
const canvas = canvasRef.current;
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
if (!root.current) root.current = events.createRoot(canvas);
async function run() {
await root.current.configure({
gl,
scene,
events: events$1,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
size: containerRect,
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
onCreated: state => {
// Connect to event source
state.events.connect == null ? void 0 : state.events.connect(eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
// Set up compute function
if (eventPrefix) {
state.setEvents({
compute: (event, state) => {
const x = event[eventPrefix + 'X'];
const y = event[eventPrefix + 'Y'];
state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
}
});
}
// Call onCreated callback
onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsxRuntime.jsx(React__namespace.Suspense, {
fallback: /*#__PURE__*/jsxRuntime.jsx(events.Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React__namespace.useEffect(() => {
const canvas = canvasRef.current;
if (canvas) return () => events.unmountComponentAtNode(canvas);
}, []);
// When the event source is not this div, we need to set pointer-events to none
// Or else the canvas will block events from reaching the event source
const pointerEvents = eventSource ? 'none' : 'auto';
return /*#__PURE__*/jsxRuntime.jsx("div", {
ref: divRef,
style: {
position: 'relative',
width: '100%',
height: '100%',
overflow: 'hidden',
pointerEvents,
...style
},
...props,
children: /*#__PURE__*/jsxRuntime.jsx("div", {
ref: containerRef,
style: {
width: '100%',
height: '100%'
},
children: /*#__PURE__*/jsxRuntime.jsx("canvas", {
ref: canvasRef,
style: {
display: 'block'
},
children: fallback
})
})
});
}
/**
* A DOM canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsxRuntime.jsx(itsFine.FiberProvider, {
children: /*#__PURE__*/jsxRuntime.jsx(CanvasImpl, {
...props
})
});
}
exports.ReactThreeFiber = events.threeTypes;
exports._roots = events._roots;
exports.act = events.act;
exports.addAfterEffect = events.addAfterEffect;
exports.addEffect = events.addEffect;
exports.addTail = events.addTail;
exports.advance = events.advance;
exports.applyProps = events.applyProps;
exports.buildGraph = events.buildGraph;
exports.context = events.context;
exports.createEvents = events.createEvents;
exports.createPortal = events.createPortal;
exports.createRoot = events.createRoot;
exports.dispose = events.dispose;
exports.events = events.createPointerEvents;
exports.extend = events.extend;
exports.flushGlobalEffects = events.flushGlobalEffects;
exports.flushSync = events.flushSync;
exports.getRootState = events.getRootState;
exports.invalidate = events.invalidate;
exports.reconciler = events.reconciler;
exports.unmountComponentAtNode = events.unmountComponentAtNode;
exports.useFrame = events.useFrame;
exports.useGraph = events.useGraph;
exports.useInstanceHandle = events.useInstanceHandle;
exports.useLoader = events.useLoader;
exports.useStore = events.useStore;
exports.useThree = events.useThree;
exports.Canvas = Canvas;
+165
View File
@@ -0,0 +1,165 @@
import { e as extend, u as useBridge, a as useMutableCallback, b as useIsomorphicLayoutEffect, c as createRoot, i as isRef, E as ErrorBoundary, B as Block, d as unmountComponentAtNode, f as createPointerEvents } from './events-5a94e5eb.esm.js';
export { t as ReactThreeFiber, _ as _roots, x as act, k as addAfterEffect, j as addEffect, l as addTail, n as advance, s as applyProps, y as buildGraph, q as context, g as createEvents, o as createPortal, c as createRoot, w as dispose, f as events, e as extend, h as flushGlobalEffects, p as flushSync, v as getRootState, m as invalidate, r as reconciler, d as unmountComponentAtNode, D as useFrame, F as useGraph, z as useInstanceHandle, G as useLoader, A as useStore, C as useThree } from './events-5a94e5eb.esm.js';
import * as React from 'react';
import * as THREE from 'three';
import useMeasure from 'react-use-measure';
import { FiberProvider } from 'its-fine';
import { jsx } from 'react/jsx-runtime';
import 'zustand/traditional';
import 'suspend-react';
import 'scheduler';
function CanvasImpl({
ref,
children,
fallback,
resize,
style,
gl,
events = createPointerEvents,
eventSource,
eventPrefix,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React.useMemo(() => extend(THREE), []);
const Bridge = useBridge();
const [containerRef, containerRect] = useMeasure({
scroll: true,
debounce: {
scroll: 50,
resize: 0
},
...resize
});
const canvasRef = React.useRef(null);
const divRef = React.useRef(null);
React.useImperativeHandle(ref, () => canvasRef.current);
const handlePointerMissed = useMutableCallback(onPointerMissed);
const [block, setBlock] = React.useState(false);
const [error, setError] = React.useState(false);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const root = React.useRef(null);
useIsomorphicLayoutEffect(() => {
const canvas = canvasRef.current;
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
if (!root.current) root.current = createRoot(canvas);
async function run() {
await root.current.configure({
gl,
scene,
events,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
dpr,
performance,
raycaster,
camera,
size: containerRect,
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
onCreated: state => {
// Connect to event source
state.events.connect == null ? void 0 : state.events.connect(eventSource ? isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
// Set up compute function
if (eventPrefix) {
state.setEvents({
compute: (event, state) => {
const x = event[eventPrefix + 'X'];
const y = event[eventPrefix + 'Y'];
state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
}
});
}
// Call onCreated callback
onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsx(Bridge, {
children: /*#__PURE__*/jsx(ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsx(React.Suspense, {
fallback: /*#__PURE__*/jsx(Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React.useEffect(() => {
const canvas = canvasRef.current;
if (canvas) return () => unmountComponentAtNode(canvas);
}, []);
// When the event source is not this div, we need to set pointer-events to none
// Or else the canvas will block events from reaching the event source
const pointerEvents = eventSource ? 'none' : 'auto';
return /*#__PURE__*/jsx("div", {
ref: divRef,
style: {
position: 'relative',
width: '100%',
height: '100%',
overflow: 'hidden',
pointerEvents,
...style
},
...props,
children: /*#__PURE__*/jsx("div", {
ref: containerRef,
style: {
width: '100%',
height: '100%'
},
children: /*#__PURE__*/jsx("canvas", {
ref: canvasRef,
style: {
display: 'block'
},
children: fallback
})
})
});
}
/**
* A DOM canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsx(FiberProvider, {
children: /*#__PURE__*/jsx(CanvasImpl, {
...props
})
});
}
export { Canvas };
@@ -0,0 +1,2 @@
export * from "../../dist/declarations/src/native";
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhY3QtdGhyZWUtZmliZXItbmF0aXZlLmNqcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vZGlzdC9kZWNsYXJhdGlvbnMvc3JjL25hdGl2ZS5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=
@@ -0,0 +1,567 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var events = require('../../dist/events-358c3764.cjs.dev.js');
var React = require('react');
var THREE = require('three');
var reactNative = require('react-native');
var expoGl = require('expo-gl');
var itsFine = require('its-fine');
var jsxRuntime = require('react/jsx-runtime');
var expoAsset = require('expo-asset');
var base64Js = require('base64-js');
var buffer = require('buffer');
require('zustand/traditional');
require('suspend-react');
require('scheduler');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
// TODO: React 19 needs support from react-native
const _View = reactNative.View;
function CanvasImpl({
children,
style,
gl,
events: events$1 = events.createPointerEvents,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
ref,
pointerEvents,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React__namespace.useMemo(() => events.extend(THREE__namespace), []);
const Bridge = events.useBridge();
const [{
width,
height,
top,
left
}, setSize] = React__namespace.useState({
width: 0,
height: 0,
top: 0,
left: 0
});
const [canvas, setCanvas] = React__namespace.useState(null);
const [bind, setBind] = React__namespace.useState();
React__namespace.useImperativeHandle(ref, () => viewRef.current);
const handlePointerMissed = events.useMutableCallback(onPointerMissed);
const [block, setBlock] = React__namespace.useState(false);
const [error, setError] = React__namespace.useState(undefined);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const viewRef = React__namespace.useRef(null);
const root = React__namespace.useRef(null);
const [antialias, setAntialias] = React__namespace.useState(true);
const onLayout = React__namespace.useCallback(e => {
const {
width,
height,
x,
y
} = e.nativeEvent.layout;
setSize({
width,
height,
top: y,
left: x
});
}, []);
// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React__namespace.useCallback(context => {
const listeners = new Map();
const canvas = {
style: {},
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
clientWidth: context.drawingBufferWidth,
clientHeight: context.drawingBufferHeight,
getContext: (_, {
antialias = false
}) => {
setAntialias(antialias);
return context;
},
addEventListener(type, listener) {
let callbacks = listeners.get(type);
if (!callbacks) {
callbacks = [];
listeners.set(type, callbacks);
}
callbacks.push(listener);
},
removeEventListener(type, listener) {
const callbacks = listeners.get(type);
if (callbacks) {
const index = callbacks.indexOf(listener);
if (index !== -1) callbacks.splice(index, 1);
}
},
dispatchEvent(event) {
Object.assign(event, {
target: this
});
const callbacks = listeners.get(event.type);
if (callbacks) {
for (const callback of callbacks) {
callback(event);
}
}
},
setPointerCapture() {
// TODO
},
releasePointerCapture() {
// TODO
}
};
// TODO: this is wrong but necessary to trick controls
// @ts-ignore
canvas.ownerDocument = canvas;
canvas.getRootNode = () => canvas;
root.current = events.createRoot(canvas);
setCanvas(canvas);
function handleTouch(gestureEvent, type) {
gestureEvent.persist();
canvas.dispatchEvent(Object.assign(gestureEvent.nativeEvent, {
type,
offsetX: gestureEvent.nativeEvent.locationX,
offsetY: gestureEvent.nativeEvent.locationY,
pointerType: 'touch',
pointerId: gestureEvent.nativeEvent.identifier
}));
return true;
}
const responder = reactNative.PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'pointercapture'),
onPanResponderStart: e => handleTouch(e, 'pointerdown'),
onPanResponderMove: e => handleTouch(e, 'pointermove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'pointerup');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'click');
},
onPanResponderRelease: e => handleTouch(e, 'pointerleave'),
onPanResponderTerminate: e => handleTouch(e, 'lostpointercapture'),
onPanResponderReject: e => handleTouch(e, 'lostpointercapture')
});
setBind(responder.panHandlers);
}, []);
events.useIsomorphicLayoutEffect(() => {
if (root.current && width > 0 && height > 0) {
async function run() {
await root.current.configure({
gl,
events: events$1,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: reactNative.PixelRatio.get(),
size: {
width,
height,
top,
left
},
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
// Overwrite onCreated to apply RN bindings
onCreated: state => {
// Bind render to RN bridge
const context = state.gl.getContext();
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (scene, camera) => {
renderFrame(scene, camera);
context.endFrameEXP();
};
return onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsxRuntime.jsx(React__namespace.Suspense, {
fallback: /*#__PURE__*/jsxRuntime.jsx(events.Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React__namespace.useEffect(() => {
if (canvas) {
return () => events.unmountComponentAtNode(canvas);
}
}, [canvas]);
return /*#__PURE__*/jsxRuntime.jsxs(_View, {
...props,
ref: viewRef,
onLayout: onLayout,
style: {
flex: 1,
...style
},
children: [width > 0 && /*#__PURE__*/jsxRuntime.jsx(expoGl.GLView, {
msaaSamples: antialias ? 4 : 0,
onContextCreate: onContextCreate,
style: reactNative.StyleSheet.absoluteFill
}), /*#__PURE__*/jsxRuntime.jsx(_View, {
style: reactNative.StyleSheet.absoluteFill,
pointerEvents: pointerEvents,
...bind
})]
});
}
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsxRuntime.jsx(itsFine.FiberProvider, {
children: /*#__PURE__*/jsxRuntime.jsx(CanvasImpl, {
...props
})
});
}
/** Default R3F event manager for react-native */
function createTouchEvents(store) {
const {
handlePointer
} = events.createEvents(store);
const handleTouch = (event, name) => {
event.persist()
// Apply offset
;
event.nativeEvent.offsetX = event.nativeEvent.locationX;
event.nativeEvent.offsetY = event.nativeEvent.locationY;
// Emulate DOM event
const callback = handlePointer(name);
callback(event.nativeEvent);
return true;
};
const responder = reactNative.PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'onPointerUp');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
},
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
});
return {
priority: 1,
enabled: true,
compute(event, state, previous) {
// https://github.com/pmndrs/react-three-fiber/pull/782
// Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
},
connected: undefined,
handlers: responder.panHandlers,
update: () => {
var _internal$lastEvent;
const {
events,
internal
} = store.getState();
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
handlePointer('onPointerMove')(internal.lastEvent.current);
}
},
connect: () => {
const {
set,
events
} = store.getState();
events.disconnect == null ? void 0 : events.disconnect();
set(state => ({
events: {
...state.events,
connected: true
}
}));
},
disconnect: () => {
const {
set
} = store.getState();
set(state => ({
events: {
...state.events,
connected: false
}
}));
}
};
}
// Conditionally import expo-file-system/legacy to support Expo 54
const getFileSystem = () => {
try {
return require('expo-file-system/legacy');
} catch {
return require('expo-file-system');
}
};
const fs = getFileSystem();
// http://stackoverflow.com/questions/105034
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0,
v = c == 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}
async function getAsset(input) {
if (typeof input === 'string') {
var _NativeModules$BlobMo;
// Don't process storage
if (input.startsWith('file:')) return input;
// Unpack Blobs from react-native BlobManager
// https://github.com/facebook/react-native/issues/22681#issuecomment-523258955
if (input.startsWith('blob:') || input.startsWith((_NativeModules$BlobMo = reactNative.NativeModules.BlobModule) == null ? void 0 : _NativeModules$BlobMo.BLOB_URI_SCHEME)) {
const blob = await new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', input);
xhr.responseType = 'blob';
xhr.onload = () => res(xhr.response);
xhr.onerror = rej;
xhr.send();
});
const data = await new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.onerror = rej;
reader.readAsText(blob);
});
input = `data:${blob.type};base64,${data}`;
}
// Create safe URI for JSI serialization
if (input.startsWith('data:')) {
const [header, data] = input.split(';base64,');
const [, type] = header.split('/');
const uri = fs.cacheDirectory + uuidv4() + `.${type}`;
await fs.writeAsStringAsync(uri, data, {
encoding: fs.EncodingType.Base64
});
return uri;
}
}
// Download bundler module or external URL
const asset = await expoAsset.Asset.fromModule(input).downloadAsync();
let uri = asset.localUri || asset.uri;
// Unpack assets in Android Release Mode
if (!uri.includes(':')) {
const file = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
await fs.copyAsync({
from: uri,
to: file
});
uri = file;
}
return uri;
}
function polyfills() {
// Patch Blob for ArrayBuffer and URL if unsupported
// https://github.com/facebook/react-native/pull/39276
// https://github.com/pmndrs/react-three-fiber/issues/3058
if (reactNative.Platform.OS !== 'web') {
try {
const blob = new Blob([new ArrayBuffer(4)]);
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
} catch (_) {
var _BlobManagerModule$de;
const BlobManagerModule = require('react-native/Libraries/Blob/BlobManager.js');
const BlobManager = (_BlobManagerModule$de = BlobManagerModule.default) != null ? _BlobManagerModule$de : BlobManagerModule;
const createObjectURL = URL.createObjectURL;
URL.createObjectURL = function (blob) {
if (blob.data._base64) {
return `data:${blob.type};base64,${blob.data._base64}`;
}
return createObjectURL(blob);
};
const createFromParts = BlobManager.createFromParts;
BlobManager.createFromParts = function (parts, options) {
parts = parts.map(part => {
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
part = base64Js.fromByteArray(new Uint8Array(part));
}
return part;
});
const blob = createFromParts(parts, options);
// Always enable slow but safe path for iOS (previously for Android unauth)
// https://github.com/pmndrs/react-three-fiber/issues/3075
// if (!NativeModules.BlobModule?.BLOB_URI_SCHEME) {
blob.data._base64 = '';
for (const part of parts) {
var _data$_base, _data;
blob.data._base64 += (_data$_base = (_data = part.data) == null ? void 0 : _data._base64) != null ? _data$_base : part;
}
// }
return blob;
};
}
}
// Don't pre-process urls, let expo-asset generate an absolute URL
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
// There's no Image in native, so create a data texture instead
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
const texture = new THREE__namespace.Texture();
getAsset(url).then(async uri => {
// https://github.com/expo/expo-three/pull/266
const {
width,
height
} = await new Promise((res, rej) => reactNative.Image.getSize(uri, (width, height) => res({
width,
height
}), rej));
texture.image = {
// Special case for EXGLImageUtils::loadImage
data: {
localUri: uri
},
width,
height
};
texture.flipY = true; // Since expo-gl@12.4.0
texture.needsUpdate = true;
// Force non-DOM upload for EXGL texImage2D
// @ts-expect-error
texture.isDataTexture = true;
onLoad == null ? void 0 : onLoad(texture);
}).catch(onError);
return texture;
};
// Fetches assets via FS
THREE__namespace.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
this.manager.itemStart(url);
getAsset(url).then(async uri => {
const base64 = await fs.readAsStringAsync(uri, {
encoding: fs.EncodingType.Base64
});
const data = buffer.Buffer.from(base64, 'base64');
onLoad == null ? void 0 : onLoad(data.buffer);
}).catch(error => {
onError == null ? void 0 : onError(error);
this.manager.itemError(url);
}).finally(() => {
this.manager.itemEnd(url);
});
};
}
if (reactNative.Platform.OS !== 'web') polyfills();
exports.ReactThreeFiber = events.threeTypes;
exports._roots = events._roots;
exports.act = events.act;
exports.addAfterEffect = events.addAfterEffect;
exports.addEffect = events.addEffect;
exports.addTail = events.addTail;
exports.advance = events.advance;
exports.applyProps = events.applyProps;
exports.buildGraph = events.buildGraph;
exports.context = events.context;
exports.createEvents = events.createEvents;
exports.createPortal = events.createPortal;
exports.createRoot = events.createRoot;
exports.dispose = events.dispose;
exports.extend = events.extend;
exports.flushGlobalEffects = events.flushGlobalEffects;
exports.flushSync = events.flushSync;
exports.getRootState = events.getRootState;
exports.invalidate = events.invalidate;
exports.reconciler = events.reconciler;
exports.unmountComponentAtNode = events.unmountComponentAtNode;
exports.useFrame = events.useFrame;
exports.useGraph = events.useGraph;
exports.useInstanceHandle = events.useInstanceHandle;
exports.useLoader = events.useLoader;
exports.useStore = events.useStore;
exports.useThree = events.useThree;
exports.Canvas = Canvas;
exports.events = createTouchEvents;
@@ -0,0 +1,7 @@
'use strict';
if (process.env.NODE_ENV === "production") {
module.exports = require("./react-three-fiber-native.cjs.prod.js");
} else {
module.exports = require("./react-three-fiber-native.cjs.dev.js");
}
@@ -0,0 +1,567 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var events = require('../../dist/events-238e0986.cjs.prod.js');
var React = require('react');
var THREE = require('three');
var reactNative = require('react-native');
var expoGl = require('expo-gl');
var itsFine = require('its-fine');
var jsxRuntime = require('react/jsx-runtime');
var expoAsset = require('expo-asset');
var base64Js = require('base64-js');
var buffer = require('buffer');
require('zustand/traditional');
require('suspend-react');
require('scheduler');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
// TODO: React 19 needs support from react-native
const _View = reactNative.View;
function CanvasImpl({
children,
style,
gl,
events: events$1 = events.createPointerEvents,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
ref,
pointerEvents,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React__namespace.useMemo(() => events.extend(THREE__namespace), []);
const Bridge = events.useBridge();
const [{
width,
height,
top,
left
}, setSize] = React__namespace.useState({
width: 0,
height: 0,
top: 0,
left: 0
});
const [canvas, setCanvas] = React__namespace.useState(null);
const [bind, setBind] = React__namespace.useState();
React__namespace.useImperativeHandle(ref, () => viewRef.current);
const handlePointerMissed = events.useMutableCallback(onPointerMissed);
const [block, setBlock] = React__namespace.useState(false);
const [error, setError] = React__namespace.useState(undefined);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const viewRef = React__namespace.useRef(null);
const root = React__namespace.useRef(null);
const [antialias, setAntialias] = React__namespace.useState(true);
const onLayout = React__namespace.useCallback(e => {
const {
width,
height,
x,
y
} = e.nativeEvent.layout;
setSize({
width,
height,
top: y,
left: x
});
}, []);
// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React__namespace.useCallback(context => {
const listeners = new Map();
const canvas = {
style: {},
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
clientWidth: context.drawingBufferWidth,
clientHeight: context.drawingBufferHeight,
getContext: (_, {
antialias = false
}) => {
setAntialias(antialias);
return context;
},
addEventListener(type, listener) {
let callbacks = listeners.get(type);
if (!callbacks) {
callbacks = [];
listeners.set(type, callbacks);
}
callbacks.push(listener);
},
removeEventListener(type, listener) {
const callbacks = listeners.get(type);
if (callbacks) {
const index = callbacks.indexOf(listener);
if (index !== -1) callbacks.splice(index, 1);
}
},
dispatchEvent(event) {
Object.assign(event, {
target: this
});
const callbacks = listeners.get(event.type);
if (callbacks) {
for (const callback of callbacks) {
callback(event);
}
}
},
setPointerCapture() {
// TODO
},
releasePointerCapture() {
// TODO
}
};
// TODO: this is wrong but necessary to trick controls
// @ts-ignore
canvas.ownerDocument = canvas;
canvas.getRootNode = () => canvas;
root.current = events.createRoot(canvas);
setCanvas(canvas);
function handleTouch(gestureEvent, type) {
gestureEvent.persist();
canvas.dispatchEvent(Object.assign(gestureEvent.nativeEvent, {
type,
offsetX: gestureEvent.nativeEvent.locationX,
offsetY: gestureEvent.nativeEvent.locationY,
pointerType: 'touch',
pointerId: gestureEvent.nativeEvent.identifier
}));
return true;
}
const responder = reactNative.PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'pointercapture'),
onPanResponderStart: e => handleTouch(e, 'pointerdown'),
onPanResponderMove: e => handleTouch(e, 'pointermove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'pointerup');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'click');
},
onPanResponderRelease: e => handleTouch(e, 'pointerleave'),
onPanResponderTerminate: e => handleTouch(e, 'lostpointercapture'),
onPanResponderReject: e => handleTouch(e, 'lostpointercapture')
});
setBind(responder.panHandlers);
}, []);
events.useIsomorphicLayoutEffect(() => {
if (root.current && width > 0 && height > 0) {
async function run() {
await root.current.configure({
gl,
events: events$1,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: reactNative.PixelRatio.get(),
size: {
width,
height,
top,
left
},
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
// Overwrite onCreated to apply RN bindings
onCreated: state => {
// Bind render to RN bridge
const context = state.gl.getContext();
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (scene, camera) => {
renderFrame(scene, camera);
context.endFrameEXP();
};
return onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsxRuntime.jsx(React__namespace.Suspense, {
fallback: /*#__PURE__*/jsxRuntime.jsx(events.Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React__namespace.useEffect(() => {
if (canvas) {
return () => events.unmountComponentAtNode(canvas);
}
}, [canvas]);
return /*#__PURE__*/jsxRuntime.jsxs(_View, {
...props,
ref: viewRef,
onLayout: onLayout,
style: {
flex: 1,
...style
},
children: [width > 0 && /*#__PURE__*/jsxRuntime.jsx(expoGl.GLView, {
msaaSamples: antialias ? 4 : 0,
onContextCreate: onContextCreate,
style: reactNative.StyleSheet.absoluteFill
}), /*#__PURE__*/jsxRuntime.jsx(_View, {
style: reactNative.StyleSheet.absoluteFill,
pointerEvents: pointerEvents,
...bind
})]
});
}
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsxRuntime.jsx(itsFine.FiberProvider, {
children: /*#__PURE__*/jsxRuntime.jsx(CanvasImpl, {
...props
})
});
}
/** Default R3F event manager for react-native */
function createTouchEvents(store) {
const {
handlePointer
} = events.createEvents(store);
const handleTouch = (event, name) => {
event.persist()
// Apply offset
;
event.nativeEvent.offsetX = event.nativeEvent.locationX;
event.nativeEvent.offsetY = event.nativeEvent.locationY;
// Emulate DOM event
const callback = handlePointer(name);
callback(event.nativeEvent);
return true;
};
const responder = reactNative.PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'onPointerUp');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
},
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
});
return {
priority: 1,
enabled: true,
compute(event, state, previous) {
// https://github.com/pmndrs/react-three-fiber/pull/782
// Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
},
connected: undefined,
handlers: responder.panHandlers,
update: () => {
var _internal$lastEvent;
const {
events,
internal
} = store.getState();
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
handlePointer('onPointerMove')(internal.lastEvent.current);
}
},
connect: () => {
const {
set,
events
} = store.getState();
events.disconnect == null ? void 0 : events.disconnect();
set(state => ({
events: {
...state.events,
connected: true
}
}));
},
disconnect: () => {
const {
set
} = store.getState();
set(state => ({
events: {
...state.events,
connected: false
}
}));
}
};
}
// Conditionally import expo-file-system/legacy to support Expo 54
const getFileSystem = () => {
try {
return require('expo-file-system/legacy');
} catch {
return require('expo-file-system');
}
};
const fs = getFileSystem();
// http://stackoverflow.com/questions/105034
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0,
v = c == 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}
async function getAsset(input) {
if (typeof input === 'string') {
var _NativeModules$BlobMo;
// Don't process storage
if (input.startsWith('file:')) return input;
// Unpack Blobs from react-native BlobManager
// https://github.com/facebook/react-native/issues/22681#issuecomment-523258955
if (input.startsWith('blob:') || input.startsWith((_NativeModules$BlobMo = reactNative.NativeModules.BlobModule) == null ? void 0 : _NativeModules$BlobMo.BLOB_URI_SCHEME)) {
const blob = await new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', input);
xhr.responseType = 'blob';
xhr.onload = () => res(xhr.response);
xhr.onerror = rej;
xhr.send();
});
const data = await new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.onerror = rej;
reader.readAsText(blob);
});
input = `data:${blob.type};base64,${data}`;
}
// Create safe URI for JSI serialization
if (input.startsWith('data:')) {
const [header, data] = input.split(';base64,');
const [, type] = header.split('/');
const uri = fs.cacheDirectory + uuidv4() + `.${type}`;
await fs.writeAsStringAsync(uri, data, {
encoding: fs.EncodingType.Base64
});
return uri;
}
}
// Download bundler module or external URL
const asset = await expoAsset.Asset.fromModule(input).downloadAsync();
let uri = asset.localUri || asset.uri;
// Unpack assets in Android Release Mode
if (!uri.includes(':')) {
const file = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
await fs.copyAsync({
from: uri,
to: file
});
uri = file;
}
return uri;
}
function polyfills() {
// Patch Blob for ArrayBuffer and URL if unsupported
// https://github.com/facebook/react-native/pull/39276
// https://github.com/pmndrs/react-three-fiber/issues/3058
if (reactNative.Platform.OS !== 'web') {
try {
const blob = new Blob([new ArrayBuffer(4)]);
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
} catch (_) {
var _BlobManagerModule$de;
const BlobManagerModule = require('react-native/Libraries/Blob/BlobManager.js');
const BlobManager = (_BlobManagerModule$de = BlobManagerModule.default) != null ? _BlobManagerModule$de : BlobManagerModule;
const createObjectURL = URL.createObjectURL;
URL.createObjectURL = function (blob) {
if (blob.data._base64) {
return `data:${blob.type};base64,${blob.data._base64}`;
}
return createObjectURL(blob);
};
const createFromParts = BlobManager.createFromParts;
BlobManager.createFromParts = function (parts, options) {
parts = parts.map(part => {
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
part = base64Js.fromByteArray(new Uint8Array(part));
}
return part;
});
const blob = createFromParts(parts, options);
// Always enable slow but safe path for iOS (previously for Android unauth)
// https://github.com/pmndrs/react-three-fiber/issues/3075
// if (!NativeModules.BlobModule?.BLOB_URI_SCHEME) {
blob.data._base64 = '';
for (const part of parts) {
var _data$_base, _data;
blob.data._base64 += (_data$_base = (_data = part.data) == null ? void 0 : _data._base64) != null ? _data$_base : part;
}
// }
return blob;
};
}
}
// Don't pre-process urls, let expo-asset generate an absolute URL
const extractUrlBase = THREE__namespace.LoaderUtils.extractUrlBase.bind(THREE__namespace.LoaderUtils);
THREE__namespace.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
// There's no Image in native, so create a data texture instead
THREE__namespace.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
const texture = new THREE__namespace.Texture();
getAsset(url).then(async uri => {
// https://github.com/expo/expo-three/pull/266
const {
width,
height
} = await new Promise((res, rej) => reactNative.Image.getSize(uri, (width, height) => res({
width,
height
}), rej));
texture.image = {
// Special case for EXGLImageUtils::loadImage
data: {
localUri: uri
},
width,
height
};
texture.flipY = true; // Since expo-gl@12.4.0
texture.needsUpdate = true;
// Force non-DOM upload for EXGL texImage2D
// @ts-expect-error
texture.isDataTexture = true;
onLoad == null ? void 0 : onLoad(texture);
}).catch(onError);
return texture;
};
// Fetches assets via FS
THREE__namespace.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
this.manager.itemStart(url);
getAsset(url).then(async uri => {
const base64 = await fs.readAsStringAsync(uri, {
encoding: fs.EncodingType.Base64
});
const data = buffer.Buffer.from(base64, 'base64');
onLoad == null ? void 0 : onLoad(data.buffer);
}).catch(error => {
onError == null ? void 0 : onError(error);
this.manager.itemError(url);
}).finally(() => {
this.manager.itemEnd(url);
});
};
}
if (reactNative.Platform.OS !== 'web') polyfills();
exports.ReactThreeFiber = events.threeTypes;
exports._roots = events._roots;
exports.act = events.act;
exports.addAfterEffect = events.addAfterEffect;
exports.addEffect = events.addEffect;
exports.addTail = events.addTail;
exports.advance = events.advance;
exports.applyProps = events.applyProps;
exports.buildGraph = events.buildGraph;
exports.context = events.context;
exports.createEvents = events.createEvents;
exports.createPortal = events.createPortal;
exports.createRoot = events.createRoot;
exports.dispose = events.dispose;
exports.extend = events.extend;
exports.flushGlobalEffects = events.flushGlobalEffects;
exports.flushSync = events.flushSync;
exports.getRootState = events.getRootState;
exports.invalidate = events.invalidate;
exports.reconciler = events.reconciler;
exports.unmountComponentAtNode = events.unmountComponentAtNode;
exports.useFrame = events.useFrame;
exports.useGraph = events.useGraph;
exports.useInstanceHandle = events.useInstanceHandle;
exports.useLoader = events.useLoader;
exports.useStore = events.useStore;
exports.useThree = events.useThree;
exports.Canvas = Canvas;
exports.events = createTouchEvents;
@@ -0,0 +1,515 @@
import { e as extend, u as useBridge, a as useMutableCallback, c as createRoot, b as useIsomorphicLayoutEffect, E as ErrorBoundary, B as Block, d as unmountComponentAtNode, f as createPointerEvents, g as createEvents } from '../../dist/events-5a94e5eb.esm.js';
export { t as ReactThreeFiber, _ as _roots, x as act, k as addAfterEffect, j as addEffect, l as addTail, n as advance, s as applyProps, y as buildGraph, q as context, g as createEvents, o as createPortal, c as createRoot, w as dispose, e as extend, h as flushGlobalEffects, p as flushSync, v as getRootState, m as invalidate, r as reconciler, d as unmountComponentAtNode, D as useFrame, F as useGraph, z as useInstanceHandle, G as useLoader, A as useStore, C as useThree } from '../../dist/events-5a94e5eb.esm.js';
import * as React from 'react';
import * as THREE from 'three';
import { PanResponder, PixelRatio, StyleSheet, View, Platform, Image, NativeModules } from 'react-native';
import { GLView } from 'expo-gl';
import { FiberProvider } from 'its-fine';
import { jsx, jsxs } from 'react/jsx-runtime';
import { Asset } from 'expo-asset';
import { fromByteArray } from 'base64-js';
import { Buffer } from 'buffer';
import 'zustand/traditional';
import 'suspend-react';
import 'scheduler';
// TODO: React 19 needs support from react-native
const _View = View;
function CanvasImpl({
children,
style,
gl,
events = createPointerEvents,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
ref,
pointerEvents,
...props
}) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React.useMemo(() => extend(THREE), []);
const Bridge = useBridge();
const [{
width,
height,
top,
left
}, setSize] = React.useState({
width: 0,
height: 0,
top: 0,
left: 0
});
const [canvas, setCanvas] = React.useState(null);
const [bind, setBind] = React.useState();
React.useImperativeHandle(ref, () => viewRef.current);
const handlePointerMissed = useMutableCallback(onPointerMissed);
const [block, setBlock] = React.useState(false);
const [error, setError] = React.useState(undefined);
// Suspend this component if block is a promise (2nd run)
if (block) throw block;
// Throw exception outwards if anything within canvas throws
if (error) throw error;
const viewRef = React.useRef(null);
const root = React.useRef(null);
const [antialias, setAntialias] = React.useState(true);
const onLayout = React.useCallback(e => {
const {
width,
height,
x,
y
} = e.nativeEvent.layout;
setSize({
width,
height,
top: y,
left: x
});
}, []);
// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React.useCallback(context => {
const listeners = new Map();
const canvas = {
style: {},
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
clientWidth: context.drawingBufferWidth,
clientHeight: context.drawingBufferHeight,
getContext: (_, {
antialias = false
}) => {
setAntialias(antialias);
return context;
},
addEventListener(type, listener) {
let callbacks = listeners.get(type);
if (!callbacks) {
callbacks = [];
listeners.set(type, callbacks);
}
callbacks.push(listener);
},
removeEventListener(type, listener) {
const callbacks = listeners.get(type);
if (callbacks) {
const index = callbacks.indexOf(listener);
if (index !== -1) callbacks.splice(index, 1);
}
},
dispatchEvent(event) {
Object.assign(event, {
target: this
});
const callbacks = listeners.get(event.type);
if (callbacks) {
for (const callback of callbacks) {
callback(event);
}
}
},
setPointerCapture() {
// TODO
},
releasePointerCapture() {
// TODO
}
};
// TODO: this is wrong but necessary to trick controls
// @ts-ignore
canvas.ownerDocument = canvas;
canvas.getRootNode = () => canvas;
root.current = createRoot(canvas);
setCanvas(canvas);
function handleTouch(gestureEvent, type) {
gestureEvent.persist();
canvas.dispatchEvent(Object.assign(gestureEvent.nativeEvent, {
type,
offsetX: gestureEvent.nativeEvent.locationX,
offsetY: gestureEvent.nativeEvent.locationY,
pointerType: 'touch',
pointerId: gestureEvent.nativeEvent.identifier
}));
return true;
}
const responder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'pointercapture'),
onPanResponderStart: e => handleTouch(e, 'pointerdown'),
onPanResponderMove: e => handleTouch(e, 'pointermove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'pointerup');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'click');
},
onPanResponderRelease: e => handleTouch(e, 'pointerleave'),
onPanResponderTerminate: e => handleTouch(e, 'lostpointercapture'),
onPanResponderReject: e => handleTouch(e, 'lostpointercapture')
});
setBind(responder.panHandlers);
}, []);
useIsomorphicLayoutEffect(() => {
if (root.current && width > 0 && height > 0) {
async function run() {
await root.current.configure({
gl,
events,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: PixelRatio.get(),
size: {
width,
height,
top,
left
},
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
// Overwrite onCreated to apply RN bindings
onCreated: state => {
// Bind render to RN bridge
const context = state.gl.getContext();
const renderFrame = state.gl.render.bind(state.gl);
state.gl.render = (scene, camera) => {
renderFrame(scene, camera);
context.endFrameEXP();
};
return onCreated == null ? void 0 : onCreated(state);
}
});
root.current.render( /*#__PURE__*/jsx(Bridge, {
children: /*#__PURE__*/jsx(ErrorBoundary, {
set: setError,
children: /*#__PURE__*/jsx(React.Suspense, {
fallback: /*#__PURE__*/jsx(Block, {
set: setBlock
}),
children: children != null ? children : null
})
})
}));
}
run();
}
});
React.useEffect(() => {
if (canvas) {
return () => unmountComponentAtNode(canvas);
}
}, [canvas]);
return /*#__PURE__*/jsxs(_View, {
...props,
ref: viewRef,
onLayout: onLayout,
style: {
flex: 1,
...style
},
children: [width > 0 && /*#__PURE__*/jsx(GLView, {
msaaSamples: antialias ? 4 : 0,
onContextCreate: onContextCreate,
style: StyleSheet.absoluteFill
}), /*#__PURE__*/jsx(_View, {
style: StyleSheet.absoluteFill,
pointerEvents: pointerEvents,
...bind
})]
});
}
/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
function Canvas(props) {
return /*#__PURE__*/jsx(FiberProvider, {
children: /*#__PURE__*/jsx(CanvasImpl, {
...props
})
});
}
/** Default R3F event manager for react-native */
function createTouchEvents(store) {
const {
handlePointer
} = createEvents(store);
const handleTouch = (event, name) => {
event.persist()
// Apply offset
;
event.nativeEvent.offsetX = event.nativeEvent.locationX;
event.nativeEvent.offsetY = event.nativeEvent.locationY;
// Emulate DOM event
const callback = handlePointer(name);
callback(event.nativeEvent);
return true;
};
const responder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderTerminationRequest: () => true,
onStartShouldSetPanResponderCapture: e => handleTouch(e, 'onPointerCapture'),
onPanResponderStart: e => handleTouch(e, 'onPointerDown'),
onPanResponderMove: e => handleTouch(e, 'onPointerMove'),
onPanResponderEnd: (e, state) => {
handleTouch(e, 'onPointerUp');
if (Math.hypot(state.dx, state.dy) < 20) handleTouch(e, 'onClick');
},
onPanResponderRelease: e => handleTouch(e, 'onPointerLeave'),
onPanResponderTerminate: e => handleTouch(e, 'onLostPointerCapture'),
onPanResponderReject: e => handleTouch(e, 'onLostPointerCapture')
});
return {
priority: 1,
enabled: true,
compute(event, state, previous) {
// https://github.com/pmndrs/react-three-fiber/pull/782
// Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
state.pointer.set(event.offsetX / state.size.width * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
state.raycaster.setFromCamera(state.pointer, state.camera);
},
connected: undefined,
handlers: responder.panHandlers,
update: () => {
var _internal$lastEvent;
const {
events,
internal
} = store.getState();
if ((_internal$lastEvent = internal.lastEvent) != null && _internal$lastEvent.current && events.handlers) {
handlePointer('onPointerMove')(internal.lastEvent.current);
}
},
connect: () => {
const {
set,
events
} = store.getState();
events.disconnect == null ? void 0 : events.disconnect();
set(state => ({
events: {
...state.events,
connected: true
}
}));
},
disconnect: () => {
const {
set
} = store.getState();
set(state => ({
events: {
...state.events,
connected: false
}
}));
}
};
}
// Conditionally import expo-file-system/legacy to support Expo 54
const getFileSystem = () => {
try {
return require('expo-file-system/legacy');
} catch {
return require('expo-file-system');
}
};
const fs = getFileSystem();
// http://stackoverflow.com/questions/105034
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0,
v = c == 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}
async function getAsset(input) {
if (typeof input === 'string') {
var _NativeModules$BlobMo;
// Don't process storage
if (input.startsWith('file:')) return input;
// Unpack Blobs from react-native BlobManager
// https://github.com/facebook/react-native/issues/22681#issuecomment-523258955
if (input.startsWith('blob:') || input.startsWith((_NativeModules$BlobMo = NativeModules.BlobModule) == null ? void 0 : _NativeModules$BlobMo.BLOB_URI_SCHEME)) {
const blob = await new Promise((res, rej) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', input);
xhr.responseType = 'blob';
xhr.onload = () => res(xhr.response);
xhr.onerror = rej;
xhr.send();
});
const data = await new Promise((res, rej) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.onerror = rej;
reader.readAsText(blob);
});
input = `data:${blob.type};base64,${data}`;
}
// Create safe URI for JSI serialization
if (input.startsWith('data:')) {
const [header, data] = input.split(';base64,');
const [, type] = header.split('/');
const uri = fs.cacheDirectory + uuidv4() + `.${type}`;
await fs.writeAsStringAsync(uri, data, {
encoding: fs.EncodingType.Base64
});
return uri;
}
}
// Download bundler module or external URL
const asset = await Asset.fromModule(input).downloadAsync();
let uri = asset.localUri || asset.uri;
// Unpack assets in Android Release Mode
if (!uri.includes(':')) {
const file = `${fs.cacheDirectory}ExponentAsset-${asset.hash}.${asset.type}`;
await fs.copyAsync({
from: uri,
to: file
});
uri = file;
}
return uri;
}
function polyfills() {
// Patch Blob for ArrayBuffer and URL if unsupported
// https://github.com/facebook/react-native/pull/39276
// https://github.com/pmndrs/react-three-fiber/issues/3058
if (Platform.OS !== 'web') {
try {
const blob = new Blob([new ArrayBuffer(4)]);
const url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
} catch (_) {
var _BlobManagerModule$de;
const BlobManagerModule = require('react-native/Libraries/Blob/BlobManager.js');
const BlobManager = (_BlobManagerModule$de = BlobManagerModule.default) != null ? _BlobManagerModule$de : BlobManagerModule;
const createObjectURL = URL.createObjectURL;
URL.createObjectURL = function (blob) {
if (blob.data._base64) {
return `data:${blob.type};base64,${blob.data._base64}`;
}
return createObjectURL(blob);
};
const createFromParts = BlobManager.createFromParts;
BlobManager.createFromParts = function (parts, options) {
parts = parts.map(part => {
if (part instanceof ArrayBuffer || ArrayBuffer.isView(part)) {
part = fromByteArray(new Uint8Array(part));
}
return part;
});
const blob = createFromParts(parts, options);
// Always enable slow but safe path for iOS (previously for Android unauth)
// https://github.com/pmndrs/react-three-fiber/issues/3075
// if (!NativeModules.BlobModule?.BLOB_URI_SCHEME) {
blob.data._base64 = '';
for (const part of parts) {
var _data$_base, _data;
blob.data._base64 += (_data$_base = (_data = part.data) == null ? void 0 : _data._base64) != null ? _data$_base : part;
}
// }
return blob;
};
}
}
// Don't pre-process urls, let expo-asset generate an absolute URL
const extractUrlBase = THREE.LoaderUtils.extractUrlBase.bind(THREE.LoaderUtils);
THREE.LoaderUtils.extractUrlBase = url => typeof url === 'string' ? extractUrlBase(url) : './';
// There's no Image in native, so create a data texture instead
THREE.TextureLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
const texture = new THREE.Texture();
getAsset(url).then(async uri => {
// https://github.com/expo/expo-three/pull/266
const {
width,
height
} = await new Promise((res, rej) => Image.getSize(uri, (width, height) => res({
width,
height
}), rej));
texture.image = {
// Special case for EXGLImageUtils::loadImage
data: {
localUri: uri
},
width,
height
};
texture.flipY = true; // Since expo-gl@12.4.0
texture.needsUpdate = true;
// Force non-DOM upload for EXGL texImage2D
// @ts-expect-error
texture.isDataTexture = true;
onLoad == null ? void 0 : onLoad(texture);
}).catch(onError);
return texture;
};
// Fetches assets via FS
THREE.FileLoader.prototype.load = function load(url, onLoad, onProgress, onError) {
if (this.path && typeof url === 'string') url = this.path + url;
this.manager.itemStart(url);
getAsset(url).then(async uri => {
const base64 = await fs.readAsStringAsync(uri, {
encoding: fs.EncodingType.Base64
});
const data = Buffer.from(base64, 'base64');
onLoad == null ? void 0 : onLoad(data.buffer);
}).catch(error => {
onError == null ? void 0 : onError(error);
this.manager.itemError(url);
}).finally(() => {
this.manager.itemEnd(url);
});
};
}
if (Platform.OS !== 'web') polyfills();
export { Canvas, createTouchEvents as events };
+5
View File
@@ -0,0 +1,5 @@
{
"main": "dist/react-three-fiber-native.cjs.js",
"module": "dist/react-three-fiber-native.esm.js",
"types": "dist/react-three-fiber-native.cjs.d.ts"
}
+91
View File
@@ -0,0 +1,91 @@
{
"name": "@react-three/fiber",
"version": "9.5.0",
"description": "A React renderer for Threejs",
"keywords": [
"react",
"renderer",
"fiber",
"three",
"threejs"
],
"author": "Paul Henschel (https://github.com/drcmda)",
"license": "MIT",
"maintainers": [
"Josh Ellis (https://github.com/joshuaellis)",
"Cody Bennett (https://github.com/codyjasonbennett)",
"Kris Baumgarter (https://github.com/krispya)"
],
"bugs": {
"url": "https://github.com/pmndrs/react-three-fiber/issues"
},
"homepage": "https://github.com/pmndrs/react-three-fiber#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/pmndrs/react-three-fiber.git"
},
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/react-three-fiber"
},
"main": "dist/react-three-fiber.cjs.js",
"module": "dist/react-three-fiber.esm.js",
"types": "dist/react-three-fiber.cjs.d.ts",
"react-native": "native/dist/react-three-fiber-native.cjs.js",
"sideEffects": false,
"preconstruct": {
"entrypoints": [
"index.tsx",
"native.tsx"
]
},
"scripts": {
"prebuild": "cp ../../readme.md readme.md"
},
"devDependencies": {
"@types/react-reconciler": "^0.32.3",
"react-reconciler": "^0.33.0"
},
"dependencies": {
"@babel/runtime": "^7.17.8",
"@types/webxr": "*",
"base64-js": "^1.5.1",
"buffer": "^6.0.3",
"its-fine": "^2.0.0",
"react-use-measure": "^2.1.7",
"scheduler": "^0.27.0",
"suspend-react": "^0.1.3",
"use-sync-external-store": "^1.4.0",
"zustand": "^5.0.3"
},
"peerDependencies": {
"expo": ">=43.0",
"expo-asset": ">=8.4",
"expo-file-system": ">=11.0",
"expo-gl": ">=11.0",
"react": ">=19 <19.3",
"react-dom": ">=19 <19.3",
"react-native": ">=0.78",
"three": ">=0.156"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"expo": {
"optional": true
},
"expo-asset": {
"optional": true
},
"expo-file-system": {
"optional": true
},
"expo-gl": {
"optional": true
}
}
}
+7
View File
@@ -0,0 +1,7 @@
export const DiscreteEventPriority = 0b0000000000000000000000000000001;
export const ContinuousEventPriority = 0b0000000000000000000000000000100;
export const DefaultEventPriority = 0b0000000000000000000000000010000;
export const IdleEventPriority = 0b0100000000000000000000000000000;
export const LegacyRoot = 0;
export const ConcurrentRoot = 1;
export const NoEventPriority = 0;
+9
View File
@@ -0,0 +1,9 @@
/**
* @license React
* react-reconciler-constants.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/const t=1,o=8,r=32,e=2,i=268435456,n=0,s=0;export{t as ConcurrentRoot,o as ContinuousEventPriority,r as DefaultEventPriority,e as DiscreteEventPriority,i as IdleEventPriority,n as LegacyRoot,s as NoEventPriority};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+253
View File
@@ -0,0 +1,253 @@
<h1>react-three-fiber</h1>
[![Version](https://img.shields.io/npm/v/@react-three/fiber?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)
[![Downloads](https://img.shields.io/npm/dt/react-three-fiber.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@react-three/fiber)
[![Twitter](https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000)](https://twitter.com/pmndrs)
[![Discord](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000)](https://discord.gg/ZZjjNvJ)
[![Open Collective](https://img.shields.io/opencollective/all/react-three-fiber?style=flat&colorA=000000&colorB=000000)](https://opencollective.com/react-three-fiber)
[![ETH](https://img.shields.io/badge/ETH-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/eth/address/0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682)
[![BTC](https://img.shields.io/badge/BTC-f5f5f5?style=flat&colorA=000000&colorB=000000)](https://blockchain.com/btc/address/36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH)
react-three-fiber is a <a href="https://reactjs.org/docs/codebase-overview.html#renderers">React renderer</a> for threejs.
Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.
```bash
npm install three @react-three/fiber
```
#### Does it have limitations?
None. Everything that works in Threejs will work here without exception.
#### Is it slower than plain Threejs?
No. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to React's scheduling abilities.
#### Can it keep up with frequent feature updates to Threejs?
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.
### What does it look like?
<table>
<tbody>
<tr>
<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>
<td>
<a href="https://codesandbox.io/s/rrppl0y8l4">
<img src="https://i.imgur.com/sS4ArrZ.gif" />
</a>
</td>
</tr>
</tbody>
</table>
```jsx
import { createRoot } from 'react-dom/client'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
function Box(props) {
// This reference gives us direct access to the THREE.Mesh object
const ref = useRef()
// Hold state for hovered and clicked events
const [hovered, hover] = useState(false)
const [clicked, click] = useState(false)
// Subscribe this component to the render-loop, rotate the mesh every frame
useFrame((state, delta) => (ref.current.rotation.x += delta))
// Return the view, these are regular Threejs elements expressed in JSX
return (
<mesh
{...props}
ref={ref}
scale={clicked ? 1.5 : 1}
onClick={(event) => click(!clicked)}
onPointerOver={(event) => hover(true)}
onPointerOut={(event) => hover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
createRoot(document.getElementById('root')).render(
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>,
)
```
<details>
<summary>Show TypeScript example</summary>
```bash
npm install @types/three
```
```tsx
import * as THREE from 'three'
import { createRoot } from 'react-dom/client'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame, ThreeElements } from '@react-three/fiber'
function Box(props: ThreeElements['mesh']) {
const ref = useRef<THREE.Mesh>(null!)
const [hovered, hover] = useState(false)
const [clicked, click] = useState(false)
useFrame((state, delta) => (ref.current.rotation.x += delta))
return (
<mesh
{...props}
ref={ref}
scale={clicked ? 1.5 : 1}
onClick={(event) => click(!clicked)}
onPointerOver={(event) => hover(true)}
onPointerOut={(event) => hover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
createRoot(document.getElementById('root') as HTMLElement).render(
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>,
)
```
Live demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx
</details>
<details>
<summary>Show React Native example</summary>
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.
```bash
# Install expo-cli, this will create our app
npm install expo-cli -g
# Create app and cd into it
expo init my-app
cd my-app
# Install dependencies
npm install three @react-three/fiber react
# Start
expo start
```
Some configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:
```js
// metro.config.js
module.exports = {
resolver: {
sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],
assetExts: ['glb', 'png', 'jpg'],
},
}
```
```tsx
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '@react-three/fiber/native'
function Box(props) {
const mesh = useRef(null)
const [hovered, setHover] = useState(false)
const [active, setActive] = useState(false)
useFrame((state, delta) => (mesh.current.rotation.x += delta))
return (
<mesh
{...props}
ref={mesh}
scale={active ? 1.5 : 1}
onClick={(event) => setActive(!active)}
onPointerOver={(event) => setHover(true)}
onPointerOut={(event) => setHover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
export default function App() {
return (
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
)
}
```
</details>
---
# Documentation, tutorials, examples
Visit [docs.pmnd.rs](https://docs.pmnd.rs/react-three-fiber)
<a href="https://docs.pmnd.rs/react-three-fiber"><img src="/docs/preview.jpg"></a>
# Fundamentals
You need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official [React docs](https://react.dev/learn), especially [the section about hooks](https://react.dev/reference/react). As for Threejs, make sure you at least glance over the following links:
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.
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).
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.
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.
Some reading material:
- [Threejs-docs](https://threejs.org/docs)
- [Threejs-examples](https://threejs.org/examples)
- [Threejs-fundamentals](https://threejs.org/manual/#en/fundamentals)
- [three.js-journey](https://threejs-journey.com)
- [Discover Threejs](https://discoverthreejs.com)
- [Do's and don'ts](https://discoverthreejs.com/tips-and-tricks) for performance and best practices
- [react-three-fiber alligator.io tutorial](https://alligator.io/react/react-with-threejs) by [@dghez\_](https://twitter.com/dghez_)
# Ecosystem
- [`@react-three/gltfjsx`](https://github.com/pmndrs/gltfjsx) &ndash; turns GLTFs into JSX components
- [`@react-three/drei`](https://github.com/pmndrs/drei) &ndash; useful helpers for react-three-fiber
- [`@react-three/postprocessing`](https://github.com/pmndrs/react-postprocessing) &ndash; post-processing effects
- [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) &ndash; flexbox for react-three-fiber
- [`@react-three/xr`](https://github.com/pmndrs/react-xr) &ndash; VR/AR controllers and events
- [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) &ndash; physics based hooks
- [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) &ndash; real a11y for your scene
- [`zustand`](https://github.com/pmndrs/zustand) &ndash; state management
- [`react-spring`](https://github.com/pmndrs/react-spring) &ndash; a spring-physics-based animation library
- [`react-use-gesture`](https://github.com/pmndrs/react-use-gesture) &ndash; mouse/touch gestures
- [`leva`](https://github.com/pmndrs/leva) &ndash; create GUI controls in seconds
# How to contribute
If you like this project, please consider helping out. All contributions are welcome as well as donations to [Opencollective](https://opencollective.com/react-three-fiber), or in crypto `BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH`, `ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682`.
#### Backers
Thank you to all our backers! 🙏
<a href="https://opencollective.com/react-three-fiber#backers" target="_blank">
<img src="https://opencollective.com/react-three-fiber/backers.svg?width=890"/>
</a>
#### Contributors
This project exists thanks to all the people who contribute.
<a href="https://github.com/pmndrs/react-three-fiber/graphs/contributors">
<img src="https://opencollective.com/react-three-fiber/contributors.svg?width=890" />
</a>