@lovo/matter-react 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -2
- package/README.md +1 -1
- package/dist/index.cjs +194 -138
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -129
- package/dist/index.d.ts +23 -129
- package/dist/index.js +188 -133
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -37,29 +37,29 @@ var baseStyle = {
|
|
|
37
37
|
whiteSpace: "pre"
|
|
38
38
|
};
|
|
39
39
|
function ShaderMonitor({ anchor = "top-right" }) {
|
|
40
|
-
const
|
|
40
|
+
const shaderContext = useContext(ShaderContext);
|
|
41
41
|
const [stats, setStats] = useState2({ fps: 0, ticks: 0, frames: 0 });
|
|
42
42
|
const ticksRef = useRef(0);
|
|
43
43
|
const fpsAccumRef = useRef({ frames: 0, lastSampleAt: 0, fps: 0 });
|
|
44
44
|
useEffect2(() => {
|
|
45
|
-
if (!
|
|
46
|
-
const
|
|
45
|
+
if (!shaderContext) return;
|
|
46
|
+
const schedulerTickHandler = (tick) => {
|
|
47
47
|
ticksRef.current += 1;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
const fpsAccumulator = fpsAccumRef.current;
|
|
49
|
+
fpsAccumulator.frames += 1;
|
|
50
|
+
if (fpsAccumulator.lastSampleAt === 0) fpsAccumulator.lastSampleAt = tick.now;
|
|
51
|
+
const deltaTimeSinceLastSample = tick.now - fpsAccumulator.lastSampleAt;
|
|
52
|
+
if (deltaTimeSinceLastSample >= 500) {
|
|
53
|
+
fpsAccumulator.fps = Math.round(fpsAccumulator.frames * 1e3 / deltaTimeSinceLastSample);
|
|
54
|
+
fpsAccumulator.frames = 0;
|
|
55
|
+
fpsAccumulator.lastSampleAt = tick.now;
|
|
56
56
|
}
|
|
57
|
-
setStats({ fps:
|
|
57
|
+
setStats({ fps: fpsAccumulator.fps, ticks: ticksRef.current, frames: fpsAccumulator.frames });
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
return () =>
|
|
61
|
-
}, [
|
|
62
|
-
if (!
|
|
59
|
+
shaderContext.scheduler.add(schedulerTickHandler);
|
|
60
|
+
return () => shaderContext.scheduler.remove(schedulerTickHandler);
|
|
61
|
+
}, [shaderContext]);
|
|
62
|
+
if (!shaderContext) {
|
|
63
63
|
return /* @__PURE__ */ jsx2("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: "no scene" });
|
|
64
64
|
}
|
|
65
65
|
return /* @__PURE__ */ jsxs("div", { "data-testid": "matter-monitor", style: { ...baseStyle, ...anchorStyle[anchor] }, children: [
|
|
@@ -76,17 +76,50 @@ function ShaderMonitor({ anchor = "top-right" }) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/components/shader-scene/shader-scene.tsx
|
|
79
|
+
import { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
|
|
79
80
|
import {
|
|
80
81
|
createIntersectionWatcher,
|
|
81
82
|
createRenderer,
|
|
82
83
|
createVisibilityWatcher,
|
|
83
84
|
FrameScheduler
|
|
84
85
|
} from "@lovo/matter";
|
|
85
|
-
import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
86
86
|
import { OrthographicCamera, Scene } from "three";
|
|
87
|
-
import { pass } from "three/tsl";
|
|
87
|
+
import { pass, vec4 } from "three/tsl";
|
|
88
88
|
import { PostProcessing } from "three/webgpu";
|
|
89
|
-
|
|
89
|
+
|
|
90
|
+
// src/hooks/use-display-gamut/use-display-gamut.ts
|
|
91
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
92
|
+
var P3_QUERY = "(color-gamut: p3)";
|
|
93
|
+
function detectGamut() {
|
|
94
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
95
|
+
return "srgb";
|
|
96
|
+
}
|
|
97
|
+
return window.matchMedia(P3_QUERY).matches ? "p3" : "srgb";
|
|
98
|
+
}
|
|
99
|
+
function useDisplayGamut(preference) {
|
|
100
|
+
const [resolved, setResolved] = useState3(
|
|
101
|
+
() => preference === "auto" ? detectGamut() : preference
|
|
102
|
+
);
|
|
103
|
+
useEffect3(() => {
|
|
104
|
+
if (preference !== "auto") {
|
|
105
|
+
setResolved(preference);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
109
|
+
setResolved("srgb");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const mediaQuery = window.matchMedia(P3_QUERY);
|
|
113
|
+
const update = () => setResolved(mediaQuery.matches ? "p3" : "srgb");
|
|
114
|
+
update();
|
|
115
|
+
mediaQuery.addEventListener("change", update);
|
|
116
|
+
return () => mediaQuery.removeEventListener("change", update);
|
|
117
|
+
}, [preference]);
|
|
118
|
+
return resolved;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/components/shader-scene/shader-scene.tsx
|
|
122
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
90
123
|
var defaultStyle = {
|
|
91
124
|
position: "absolute",
|
|
92
125
|
inset: 0,
|
|
@@ -95,18 +128,21 @@ var defaultStyle = {
|
|
|
95
128
|
height: "100%"
|
|
96
129
|
};
|
|
97
130
|
function ShaderScene(props) {
|
|
98
|
-
const { children, fallback, className, style, maxDPR } = props;
|
|
131
|
+
const { children, fallback, className, style, maxDPR, gamut = "auto" } = props;
|
|
132
|
+
const resolvedGamut = useDisplayGamut(gamut);
|
|
99
133
|
const canvasRef = useRef2(null);
|
|
100
|
-
const [
|
|
101
|
-
const [error, setError] =
|
|
102
|
-
|
|
134
|
+
const [shaderContext, setShaderContext] = useState4(null);
|
|
135
|
+
const [error, setError] = useState4(null);
|
|
136
|
+
const [firstFramePainted, setFirstFramePainted] = useState4(false);
|
|
137
|
+
useEffect4(() => {
|
|
103
138
|
const canvas = canvasRef.current;
|
|
104
139
|
if (!canvas) return;
|
|
105
140
|
let cancelled = false;
|
|
106
141
|
let cleanup = null;
|
|
142
|
+
let firstPaintRaf = null;
|
|
107
143
|
const setup = async () => {
|
|
108
144
|
try {
|
|
109
|
-
const renderer = await createRenderer(canvas, { maxDPR });
|
|
145
|
+
const renderer = await createRenderer(canvas, { maxDPR, gamut: resolvedGamut });
|
|
110
146
|
if (cancelled) {
|
|
111
147
|
renderer.dispose();
|
|
112
148
|
return;
|
|
@@ -117,12 +153,11 @@ function ShaderScene(props) {
|
|
|
117
153
|
const postProcessing = new PostProcessing(renderer.three);
|
|
118
154
|
const scheduler = new FrameScheduler();
|
|
119
155
|
const overlays = /* @__PURE__ */ new Map();
|
|
120
|
-
const
|
|
156
|
+
const basePassNode = vec4(pass(scene, camera));
|
|
121
157
|
const rebuildOutputNode = () => {
|
|
122
|
-
const seed = basePass;
|
|
123
158
|
postProcessing.outputNode = Array.from(overlays.values()).reduce(
|
|
124
|
-
(
|
|
125
|
-
|
|
159
|
+
(currentPipeline, transform) => transform(currentPipeline),
|
|
160
|
+
basePassNode
|
|
126
161
|
);
|
|
127
162
|
postProcessing.needsUpdate = true;
|
|
128
163
|
};
|
|
@@ -136,7 +171,18 @@ function ShaderScene(props) {
|
|
|
136
171
|
rebuildOutputNode();
|
|
137
172
|
};
|
|
138
173
|
};
|
|
139
|
-
|
|
174
|
+
let firstPaintSignaled = false;
|
|
175
|
+
const renderFrame = () => {
|
|
176
|
+
postProcessing.render();
|
|
177
|
+
if (!firstPaintSignaled && (scene.children.length > 0 || overlays.size > 0)) {
|
|
178
|
+
firstPaintSignaled = true;
|
|
179
|
+
firstPaintRaf = requestAnimationFrame(() => {
|
|
180
|
+
firstPaintRaf = null;
|
|
181
|
+
if (!cancelled) setFirstFramePainted(true);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
scheduler.add(renderFrame);
|
|
140
186
|
scheduler.start();
|
|
141
187
|
const visibility = createVisibilityWatcher();
|
|
142
188
|
const intersection = createIntersectionWatcher(canvas);
|
|
@@ -148,33 +194,38 @@ function ShaderScene(props) {
|
|
|
148
194
|
updatePauseState();
|
|
149
195
|
const unsubVisibility = visibility.subscribe(updatePauseState);
|
|
150
196
|
const unsubIntersection = intersection.subscribe(updatePauseState);
|
|
151
|
-
const
|
|
152
|
-
|
|
197
|
+
const resizeObserver = new ResizeObserver(() => renderer.resize());
|
|
198
|
+
resizeObserver.observe(canvas);
|
|
153
199
|
cleanup = () => {
|
|
154
200
|
unsubVisibility();
|
|
155
201
|
unsubIntersection();
|
|
156
202
|
visibility.dispose();
|
|
157
203
|
intersection.dispose();
|
|
158
|
-
|
|
204
|
+
resizeObserver.disconnect();
|
|
159
205
|
scheduler.dispose();
|
|
160
206
|
renderer.dispose();
|
|
161
207
|
};
|
|
162
|
-
|
|
163
|
-
} catch (
|
|
208
|
+
setShaderContext({ renderer, scene, camera, scheduler, registerOverlay });
|
|
209
|
+
} catch (caughtError) {
|
|
164
210
|
if (cancelled) return;
|
|
165
|
-
const
|
|
166
|
-
console.error("[ShaderScene] renderer init failed:",
|
|
167
|
-
setError(
|
|
211
|
+
const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
|
|
212
|
+
console.error("[ShaderScene] renderer init failed:", normalizedError);
|
|
213
|
+
setError(normalizedError);
|
|
168
214
|
}
|
|
169
215
|
};
|
|
170
216
|
void setup();
|
|
171
217
|
return () => {
|
|
172
218
|
cancelled = true;
|
|
219
|
+
if (firstPaintRaf !== null) {
|
|
220
|
+
cancelAnimationFrame(firstPaintRaf);
|
|
221
|
+
firstPaintRaf = null;
|
|
222
|
+
}
|
|
173
223
|
cleanup?.();
|
|
174
224
|
cleanup = null;
|
|
175
|
-
|
|
225
|
+
setShaderContext(null);
|
|
226
|
+
setFirstFramePainted(false);
|
|
176
227
|
};
|
|
177
|
-
}, [maxDPR]);
|
|
228
|
+
}, [maxDPR, resolvedGamut]);
|
|
178
229
|
let content;
|
|
179
230
|
if (error) {
|
|
180
231
|
content = /* @__PURE__ */ jsxs2(
|
|
@@ -200,10 +251,11 @@ function ShaderScene(props) {
|
|
|
200
251
|
]
|
|
201
252
|
}
|
|
202
253
|
);
|
|
203
|
-
} else if (ctx) {
|
|
204
|
-
content = /* @__PURE__ */ jsx3(ShaderContext.Provider, { value: ctx, children });
|
|
205
254
|
} else {
|
|
206
|
-
content =
|
|
255
|
+
content = /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
256
|
+
shaderContext && /* @__PURE__ */ jsx3(ShaderContext.Provider, { value: shaderContext, children }),
|
|
257
|
+
!firstFramePainted && (fallback ?? null)
|
|
258
|
+
] });
|
|
207
259
|
}
|
|
208
260
|
return /* @__PURE__ */ jsxs2("div", { className, style: { ...defaultStyle, ...style }, children: [
|
|
209
261
|
/* @__PURE__ */ jsx3("canvas", { ref: canvasRef, style: { width: "100%", height: "100%", display: "block" } }),
|
|
@@ -212,7 +264,7 @@ function ShaderScene(props) {
|
|
|
212
264
|
}
|
|
213
265
|
|
|
214
266
|
// src/hooks/use-animatable-uniform/use-animatable-uniform.ts
|
|
215
|
-
import { useEffect as
|
|
267
|
+
import { useEffect as useEffect5, useMemo } from "react";
|
|
216
268
|
import { uniform } from "three/tsl";
|
|
217
269
|
var isSignal = (value) => {
|
|
218
270
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -223,7 +275,7 @@ function useAnimatableUniform(value) {
|
|
|
223
275
|
const initial = isSignal(value) ? value.get() : value;
|
|
224
276
|
return uniform(initial);
|
|
225
277
|
}, []);
|
|
226
|
-
|
|
278
|
+
useEffect5(() => {
|
|
227
279
|
if (isSignal(value)) {
|
|
228
280
|
const unsub = value.on("change", (next) => {
|
|
229
281
|
uniformNode.value = next;
|
|
@@ -237,8 +289,8 @@ function useAnimatableUniform(value) {
|
|
|
237
289
|
}
|
|
238
290
|
|
|
239
291
|
// src/hooks/use-cursor/use-cursor.ts
|
|
292
|
+
import { useEffect as useEffect6, useState as useState5 } from "react";
|
|
240
293
|
import { CursorInput } from "@lovo/matter";
|
|
241
|
-
import { useEffect as useEffect5, useState as useState4 } from "react";
|
|
242
294
|
|
|
243
295
|
// src/hooks/use-shader-context/use-shader-context.ts
|
|
244
296
|
import { useContext as useContext2 } from "react";
|
|
@@ -252,81 +304,91 @@ var STUB_SIGNAL = {
|
|
|
252
304
|
on: () => () => void 0
|
|
253
305
|
};
|
|
254
306
|
function useCursor(opts = {}) {
|
|
255
|
-
const
|
|
256
|
-
const [input, setInput] =
|
|
257
|
-
|
|
258
|
-
const canvas =
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
setInput(
|
|
307
|
+
const shaderContext = useShaderContext();
|
|
308
|
+
const [input, setInput] = useState5(null);
|
|
309
|
+
useEffect6(() => {
|
|
310
|
+
const canvas = shaderContext?.renderer.three.domElement;
|
|
311
|
+
const resolvedElement = opts.element ?? (canvas instanceof HTMLElement ? canvas : void 0);
|
|
312
|
+
const newCursorInput = new CursorInput({ ...opts, element: resolvedElement });
|
|
313
|
+
setInput(newCursorInput);
|
|
262
314
|
let detach = null;
|
|
263
|
-
if (
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
detach = () =>
|
|
315
|
+
if (shaderContext?.scheduler) {
|
|
316
|
+
const schedulerTickHandler = ({ delta }) => newCursorInput.tick(delta);
|
|
317
|
+
shaderContext.scheduler.add(schedulerTickHandler);
|
|
318
|
+
detach = () => shaderContext.scheduler.remove(schedulerTickHandler);
|
|
267
319
|
} else {
|
|
268
|
-
let
|
|
320
|
+
let animationFrameId = null;
|
|
269
321
|
let lastNow = performance.now();
|
|
270
322
|
const loop = (now) => {
|
|
271
323
|
const delta = (now - lastNow) / 1e3;
|
|
272
324
|
lastNow = now;
|
|
273
|
-
|
|
274
|
-
|
|
325
|
+
newCursorInput.tick(delta);
|
|
326
|
+
animationFrameId = requestAnimationFrame(loop);
|
|
275
327
|
};
|
|
276
|
-
|
|
328
|
+
animationFrameId = requestAnimationFrame(loop);
|
|
277
329
|
detach = () => {
|
|
278
|
-
if (
|
|
330
|
+
if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
|
|
279
331
|
};
|
|
280
332
|
}
|
|
281
333
|
return () => {
|
|
282
334
|
detach();
|
|
283
|
-
|
|
335
|
+
newCursorInput.dispose();
|
|
284
336
|
setInput(null);
|
|
285
337
|
};
|
|
286
|
-
}, [
|
|
338
|
+
}, [shaderContext]);
|
|
287
339
|
return input ?? STUB_SIGNAL;
|
|
288
340
|
}
|
|
289
341
|
|
|
290
342
|
// src/hooks/use-overlay-pass/use-overlay-pass.ts
|
|
291
|
-
import { useEffect as
|
|
292
|
-
function
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
if (!
|
|
296
|
-
const unregister =
|
|
343
|
+
import { useEffect as useEffect7 } from "react";
|
|
344
|
+
function usePostProcessPass(transform, deps) {
|
|
345
|
+
const shaderContext = useShaderContext();
|
|
346
|
+
useEffect7(() => {
|
|
347
|
+
if (!shaderContext) return;
|
|
348
|
+
const unregister = shaderContext.registerOverlay(transform);
|
|
297
349
|
return unregister;
|
|
298
|
-
}, [
|
|
350
|
+
}, [shaderContext, ...deps]);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/hooks/use-resize/use-resize.ts
|
|
354
|
+
import { useEffect as useEffect8, useState as useState6 } from "react";
|
|
355
|
+
|
|
356
|
+
// src/internal/create-signal.ts
|
|
357
|
+
function createSignal(getValue) {
|
|
358
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
359
|
+
return {
|
|
360
|
+
listeners,
|
|
361
|
+
signal: {
|
|
362
|
+
get: getValue,
|
|
363
|
+
on: (_event, listener) => {
|
|
364
|
+
listeners.add(listener);
|
|
365
|
+
return () => {
|
|
366
|
+
listeners.delete(listener);
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
};
|
|
299
371
|
}
|
|
300
372
|
|
|
301
373
|
// src/hooks/use-resize/use-resize.ts
|
|
302
|
-
import { useEffect as useEffect7, useState as useState5 } from "react";
|
|
303
374
|
var STUB_SIGNAL2 = {
|
|
304
375
|
get: () => [0, 0, 1],
|
|
305
376
|
on: () => () => void 0
|
|
306
377
|
};
|
|
307
378
|
function useResize() {
|
|
308
|
-
const
|
|
309
|
-
const [signal, setSignal] =
|
|
310
|
-
|
|
311
|
-
if (!
|
|
312
|
-
const canvas =
|
|
379
|
+
const shaderContext = useShaderContext();
|
|
380
|
+
const [signal, setSignal] = useState6(null);
|
|
381
|
+
useEffect8(() => {
|
|
382
|
+
if (!shaderContext) return void 0;
|
|
383
|
+
const canvas = shaderContext.renderer.three.domElement;
|
|
313
384
|
if (!(canvas instanceof HTMLCanvasElement)) return void 0;
|
|
314
385
|
let value = [
|
|
315
386
|
canvas.clientWidth,
|
|
316
387
|
canvas.clientHeight,
|
|
317
388
|
typeof window !== "undefined" ? window.devicePixelRatio : 1
|
|
318
389
|
];
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
get: () => value,
|
|
322
|
-
on: (_event, cb) => {
|
|
323
|
-
listeners.add(cb);
|
|
324
|
-
return () => {
|
|
325
|
-
listeners.delete(cb);
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
setSignal(fresh);
|
|
390
|
+
const { signal: newSignal, listeners } = createSignal(() => value);
|
|
391
|
+
setSignal(newSignal);
|
|
330
392
|
const emit = () => {
|
|
331
393
|
const next = [
|
|
332
394
|
canvas.clientWidth,
|
|
@@ -335,66 +397,59 @@ function useResize() {
|
|
|
335
397
|
];
|
|
336
398
|
if (next[0] === value[0] && next[1] === value[1] && next[2] === value[2]) return;
|
|
337
399
|
value = next;
|
|
338
|
-
for (const
|
|
400
|
+
for (const listener of listeners) listener(next);
|
|
339
401
|
};
|
|
340
402
|
const observer = new ResizeObserver(emit);
|
|
341
403
|
observer.observe(canvas);
|
|
342
|
-
let
|
|
343
|
-
let
|
|
404
|
+
let mediaQueryList = null;
|
|
405
|
+
let mediaQueryListener = null;
|
|
344
406
|
const setupDprWatch = () => {
|
|
345
407
|
if (typeof window === "undefined") return;
|
|
346
408
|
const dpr = window.devicePixelRatio;
|
|
347
|
-
const
|
|
348
|
-
const
|
|
409
|
+
const nextMediaQueryList = window.matchMedia(`(resolution: ${dpr}dppx)`);
|
|
410
|
+
const nextMediaQueryListener = () => {
|
|
349
411
|
emit();
|
|
350
|
-
if (
|
|
412
|
+
if (mediaQueryList && mediaQueryListener)
|
|
413
|
+
mediaQueryList.removeEventListener("change", mediaQueryListener);
|
|
351
414
|
setupDprWatch();
|
|
352
415
|
};
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
416
|
+
nextMediaQueryList.addEventListener("change", nextMediaQueryListener);
|
|
417
|
+
mediaQueryList = nextMediaQueryList;
|
|
418
|
+
mediaQueryListener = nextMediaQueryListener;
|
|
356
419
|
};
|
|
357
420
|
setupDprWatch();
|
|
358
421
|
return () => {
|
|
359
422
|
observer.disconnect();
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
423
|
+
if (mediaQueryList && mediaQueryListener)
|
|
424
|
+
mediaQueryList.removeEventListener("change", mediaQueryListener);
|
|
425
|
+
mediaQueryList = null;
|
|
426
|
+
mediaQueryListener = null;
|
|
363
427
|
listeners.clear();
|
|
364
428
|
setSignal(null);
|
|
365
429
|
};
|
|
366
|
-
}, [
|
|
430
|
+
}, [shaderContext]);
|
|
367
431
|
return signal ?? STUB_SIGNAL2;
|
|
368
432
|
}
|
|
369
433
|
|
|
370
434
|
// src/hooks/use-scroll/use-scroll.ts
|
|
371
|
-
import { useEffect as
|
|
435
|
+
import { useEffect as useEffect9, useState as useState7 } from "react";
|
|
372
436
|
var STUB_SIGNAL3 = {
|
|
373
437
|
get: () => [0, 0],
|
|
374
438
|
on: () => () => void 0
|
|
375
439
|
};
|
|
376
440
|
function useScroll() {
|
|
377
|
-
const [signal, setSignal] =
|
|
378
|
-
|
|
441
|
+
const [signal, setSignal] = useState7(null);
|
|
442
|
+
useEffect9(() => {
|
|
379
443
|
if (typeof window === "undefined") return void 0;
|
|
380
444
|
const compute = () => {
|
|
381
|
-
const
|
|
445
|
+
const scrollYPosition = window.scrollY;
|
|
382
446
|
const max = Math.max(document.documentElement.scrollHeight - window.innerHeight, 1);
|
|
383
|
-
const progress = Math.max(0, Math.min(1,
|
|
384
|
-
return [
|
|
447
|
+
const progress = Math.max(0, Math.min(1, scrollYPosition / max));
|
|
448
|
+
return [scrollYPosition, progress];
|
|
385
449
|
};
|
|
386
450
|
let value = compute();
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
get: () => value,
|
|
390
|
-
on: (_event, cb) => {
|
|
391
|
-
listeners.add(cb);
|
|
392
|
-
return () => {
|
|
393
|
-
listeners.delete(cb);
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
setSignal(fresh);
|
|
451
|
+
const { signal: newSignal, listeners } = createSignal(() => value);
|
|
452
|
+
setSignal(newSignal);
|
|
398
453
|
let rafPending = false;
|
|
399
454
|
const onScroll = () => {
|
|
400
455
|
if (rafPending) return;
|
|
@@ -404,7 +459,7 @@ function useScroll() {
|
|
|
404
459
|
const next = compute();
|
|
405
460
|
if (next[0] === value[0] && next[1] === value[1]) return;
|
|
406
461
|
value = next;
|
|
407
|
-
for (const
|
|
462
|
+
for (const listener of listeners) listener(next);
|
|
408
463
|
});
|
|
409
464
|
};
|
|
410
465
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -418,29 +473,28 @@ function useScroll() {
|
|
|
418
473
|
}
|
|
419
474
|
|
|
420
475
|
// src/hooks/use-shader-material/use-shader-material.ts
|
|
421
|
-
import { useEffect as
|
|
476
|
+
import { useEffect as useEffect10, useMemo as useMemo2 } from "react";
|
|
422
477
|
import { MeshBasicNodeMaterial } from "three/webgpu";
|
|
423
478
|
function useShaderMaterial(build) {
|
|
424
479
|
const material = useMemo2(() => {
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
return
|
|
480
|
+
const nodeMaterial = new MeshBasicNodeMaterial();
|
|
481
|
+
nodeMaterial.colorNode = build();
|
|
482
|
+
return nodeMaterial;
|
|
428
483
|
}, [build]);
|
|
429
|
-
|
|
484
|
+
useEffect10(() => {
|
|
430
485
|
return () => material.dispose();
|
|
431
486
|
}, [material]);
|
|
432
487
|
return material;
|
|
433
488
|
}
|
|
434
489
|
|
|
435
490
|
// src/hooks/use-static-hint/use-static-hint.ts
|
|
436
|
-
import { useEffect as
|
|
437
|
-
function
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
if (!
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
}, [ctx, hint]);
|
|
491
|
+
import { useEffect as useEffect11 } from "react";
|
|
492
|
+
function useStaticSceneHint(isStatic) {
|
|
493
|
+
const shaderContext = useShaderContext();
|
|
494
|
+
useEffect11(() => {
|
|
495
|
+
if (!shaderContext) return;
|
|
496
|
+
return shaderContext.scheduler.setIdle(isStatic);
|
|
497
|
+
}, [shaderContext, isStatic]);
|
|
444
498
|
}
|
|
445
499
|
export {
|
|
446
500
|
FallbackBoundary,
|
|
@@ -448,11 +502,12 @@ export {
|
|
|
448
502
|
ShaderScene,
|
|
449
503
|
useAnimatableUniform,
|
|
450
504
|
useCursor,
|
|
451
|
-
|
|
505
|
+
useDisplayGamut,
|
|
506
|
+
usePostProcessPass,
|
|
452
507
|
useResize,
|
|
453
508
|
useScroll,
|
|
454
509
|
useShaderContext,
|
|
455
510
|
useShaderMaterial,
|
|
456
|
-
|
|
511
|
+
useStaticSceneHint
|
|
457
512
|
};
|
|
458
513
|
//# sourceMappingURL=index.js.map
|