@motion-core/motion-gpu
Version:
Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.
92 lines (91 loc) • 3.4 kB
JavaScript
import { createCurrentWritable } from "../core/current-value.js";
import { useMotionGPU } from "./motiongpu-context.js";
import { createContext, useContext, useEffect, useRef } from "react";
//#region src/lib/react/frame-context.ts
/**
* Placeholder stage used before a frame task registration becomes available.
*/
var PENDING_STAGE_KEY = Symbol("motiongpu-react-pending-stage");
/**
* React context container for the active frame registry.
*/
var FrameRegistryReactContext = createContext(null);
/**
* Registers a callback in the active frame registry and auto-unsubscribes on unmount.
*
* @param keyOrCallback - Task key or callback for auto-key registration.
* @param callbackOrOptions - Callback (keyed overload) or options (auto-key overload).
* @param maybeOptions - Optional registration options for keyed overload.
* Registration key/options are frozen on first render; subsequent renders do not re-register.
* @returns Registration control API with task, start/stop controls and started state.
* @throws {Error} When called outside `<FragCanvas>`.
* @throws {Error} When callback is missing in keyed overload.
*/
function useFrame(keyOrCallback, callbackOrOptions, maybeOptions) {
const registry = useContext(FrameRegistryReactContext);
if (!registry) throw new Error("useFrame must be used inside <FragCanvas>");
const motiongpu = useMotionGPU();
const resolved = typeof keyOrCallback === "function" ? {
key: void 0,
callback: keyOrCallback,
options: callbackOrOptions
} : {
key: keyOrCallback,
callback: callbackOrOptions,
options: maybeOptions
};
if (typeof resolved.callback !== "function") throw new Error("useFrame requires a callback");
const callbackRef = useRef(resolved.callback);
callbackRef.current = resolved.callback;
const registrationConfigRef = useRef(null);
if (!registrationConfigRef.current) registrationConfigRef.current = {
key: resolved.key,
options: resolved.options
};
const registrationConfig = registrationConfigRef.current;
const registrationRef = useRef(null);
const taskRef = useRef({
key: registrationConfig.key !== void 0 ? registrationConfig.key : Symbol("motiongpu-react-pending-task-key"),
stage: PENDING_STAGE_KEY
});
const startedStore = useRef(createCurrentWritable(false)).current;
useEffect(() => {
const wrappedCallback = (state) => {
callbackRef.current(state);
};
const registration = registrationConfig.key === void 0 ? registry.register(wrappedCallback, registrationConfig.options) : registry.register(registrationConfig.key, wrappedCallback, registrationConfig.options);
registrationRef.current = registration;
taskRef.current = registration.task;
const unsubscribeStarted = registration.started.subscribe((value) => {
startedStore.set(value);
});
return () => {
unsubscribeStarted();
registration.unsubscribe();
if (registrationRef.current === registration) registrationRef.current = null;
startedStore.set(false);
};
}, [
registrationConfig,
registry,
startedStore
]);
useEffect(() => {
motiongpu.invalidate();
}, [motiongpu, resolved.callback]);
return {
get task() {
return taskRef.current;
},
start: () => {
registrationRef.current?.start();
},
stop: () => {
registrationRef.current?.stop();
},
started: startedStore
};
}
//#endregion
export { FrameRegistryReactContext, useFrame };
//# sourceMappingURL=frame-context.js.map