@lovo/matter 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +5 -1
- package/dist/index.cjs +112 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -58
- package/dist/index.d.ts +58 -58
- package/dist/index.js +113 -109
- package/dist/index.js.map +1 -1
- package/package.json +26 -26
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WebGPURenderer, Node } from 'three/webgpu';
|
|
2
1
|
import { Color } from 'three';
|
|
2
|
+
import { WebGPURenderer, Node } from 'three/webgpu';
|
|
3
3
|
import { ShaderNodeObject } from 'three/tsl';
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type GpuBackend = 'webgpu' | 'webgl2';
|
|
6
6
|
interface CreateRendererOptions {
|
|
7
7
|
/** Anti-alias the framebuffer. Default: true. */
|
|
8
8
|
antialias?: boolean;
|
|
@@ -15,11 +15,11 @@ interface CreateRendererOptions {
|
|
|
15
15
|
/** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */
|
|
16
16
|
maxDPR?: number;
|
|
17
17
|
}
|
|
18
|
-
interface
|
|
18
|
+
interface GpuRenderer {
|
|
19
19
|
/** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */
|
|
20
20
|
three: WebGPURenderer;
|
|
21
21
|
/** Which backend the renderer initialized with. */
|
|
22
|
-
backend:
|
|
22
|
+
backend: GpuBackend;
|
|
23
23
|
/** Tear down the renderer and release GPU resources. */
|
|
24
24
|
dispose: () => void;
|
|
25
25
|
/** Resize the renderer to the canvas's current client dimensions. */
|
|
@@ -32,57 +32,7 @@ interface MatterRenderer {
|
|
|
32
32
|
* unavailable on the host. The returned object exposes the underlying
|
|
33
33
|
* three renderer plus a small wrapper for resize and disposal.
|
|
34
34
|
*/
|
|
35
|
-
declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<
|
|
36
|
-
|
|
37
|
-
interface SchedulerTick {
|
|
38
|
-
/** Seconds since the previous tick. 0 on the first call. */
|
|
39
|
-
delta: number;
|
|
40
|
-
/** Total seconds since the scheduler started its current run. */
|
|
41
|
-
elapsed: number;
|
|
42
|
-
/** The raw `performance.now()` timestamp the rAF callback received. */
|
|
43
|
-
now: number;
|
|
44
|
-
}
|
|
45
|
-
type SchedulerClient = (tick: SchedulerTick) => void;
|
|
46
|
-
/**
|
|
47
|
-
* Batches `requestAnimationFrame` calls across all clients registered with
|
|
48
|
-
* a single scheduler. One scheduler is created per <MatterScene>; clients
|
|
49
|
-
* are typically a Three.js renderer's render call.
|
|
50
|
-
*/
|
|
51
|
-
declare class MatterScheduler {
|
|
52
|
-
private readonly clients;
|
|
53
|
-
private rafId;
|
|
54
|
-
private running;
|
|
55
|
-
private paused;
|
|
56
|
-
private idle;
|
|
57
|
-
private flushPending;
|
|
58
|
-
private startedAt;
|
|
59
|
-
private lastTickAt;
|
|
60
|
-
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
61
|
-
start(): void;
|
|
62
|
-
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
63
|
-
stop(): void;
|
|
64
|
-
/** Temporarily skip ticks without losing client registrations. */
|
|
65
|
-
pause(): void;
|
|
66
|
-
/** Resume after pause(). */
|
|
67
|
-
resume(): void;
|
|
68
|
-
/** Register a client to be called every frame. */
|
|
69
|
-
add(client: SchedulerClient): void;
|
|
70
|
-
/** Unregister a client. */
|
|
71
|
-
remove(client: SchedulerClient): void;
|
|
72
|
-
/** Permanent teardown: stop the loop and drop all clients. */
|
|
73
|
-
dispose(): void;
|
|
74
|
-
/**
|
|
75
|
-
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
76
|
-
* uniform changes that triggered the idle state are rendered), then the
|
|
77
|
-
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
78
|
-
*/
|
|
79
|
-
setIdle(idle: boolean): void;
|
|
80
|
-
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
81
|
-
requestRender(): void;
|
|
82
|
-
private maybeQueue;
|
|
83
|
-
private cancel;
|
|
84
|
-
private readonly frame;
|
|
85
|
-
}
|
|
35
|
+
declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<GpuRenderer>;
|
|
86
36
|
|
|
87
37
|
type Vec2 = readonly [number, number];
|
|
88
38
|
interface CursorInputOptions {
|
|
@@ -122,7 +72,7 @@ interface CursorInputOptions {
|
|
|
122
72
|
type ChangeListener = (value: Vec2) => void;
|
|
123
73
|
/**
|
|
124
74
|
* Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.
|
|
125
|
-
* Implements the
|
|
75
|
+
* Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)
|
|
126
76
|
* so it composes with Motion's `useTransform` and similar tools.
|
|
127
77
|
*/
|
|
128
78
|
declare class CursorInput {
|
|
@@ -136,7 +86,7 @@ declare class CursorInput {
|
|
|
136
86
|
private readonly handleMouseMove;
|
|
137
87
|
private disposed;
|
|
138
88
|
constructor(opts?: CursorInputOptions);
|
|
139
|
-
/** Current smoothed position. Implements
|
|
89
|
+
/** Current smoothed position. Implements AnimatableSignal protocol. */
|
|
140
90
|
get(): Vec2;
|
|
141
91
|
/** Subscribe to change events. Returns an unsubscribe function. */
|
|
142
92
|
on(_event: 'change', cb: ChangeListener): () => void;
|
|
@@ -407,4 +357,54 @@ interface IntersectionWatcher {
|
|
|
407
357
|
*/
|
|
408
358
|
declare function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher;
|
|
409
359
|
|
|
410
|
-
|
|
360
|
+
interface SchedulerTick {
|
|
361
|
+
/** Seconds since the previous tick. 0 on the first call. */
|
|
362
|
+
delta: number;
|
|
363
|
+
/** Total seconds since the scheduler started its current run. */
|
|
364
|
+
elapsed: number;
|
|
365
|
+
/** The raw `performance.now()` timestamp the rAF callback received. */
|
|
366
|
+
now: number;
|
|
367
|
+
}
|
|
368
|
+
type SchedulerClient = (tick: SchedulerTick) => void;
|
|
369
|
+
/**
|
|
370
|
+
* Batches `requestAnimationFrame` calls across all clients registered with
|
|
371
|
+
* a single scheduler. One scheduler is created per <ShaderScene>; clients
|
|
372
|
+
* are typically a Three.js renderer's render call.
|
|
373
|
+
*/
|
|
374
|
+
declare class FrameScheduler {
|
|
375
|
+
private readonly clients;
|
|
376
|
+
private rafId;
|
|
377
|
+
private running;
|
|
378
|
+
private paused;
|
|
379
|
+
private idle;
|
|
380
|
+
private flushPending;
|
|
381
|
+
private startedAt;
|
|
382
|
+
private lastTickAt;
|
|
383
|
+
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
384
|
+
start(): void;
|
|
385
|
+
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
386
|
+
stop(): void;
|
|
387
|
+
/** Temporarily skip ticks without losing client registrations. */
|
|
388
|
+
pause(): void;
|
|
389
|
+
/** Resume after pause(). */
|
|
390
|
+
resume(): void;
|
|
391
|
+
/** Register a client to be called every frame. */
|
|
392
|
+
add(client: SchedulerClient): void;
|
|
393
|
+
/** Unregister a client. */
|
|
394
|
+
remove(client: SchedulerClient): void;
|
|
395
|
+
/** Permanent teardown: stop the loop and drop all clients. */
|
|
396
|
+
dispose(): void;
|
|
397
|
+
/**
|
|
398
|
+
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
399
|
+
* uniform changes that triggered the idle state are rendered), then the
|
|
400
|
+
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
401
|
+
*/
|
|
402
|
+
setIdle(idle: boolean): void;
|
|
403
|
+
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
404
|
+
requestRender(): void;
|
|
405
|
+
private maybeQueue;
|
|
406
|
+
private cancel;
|
|
407
|
+
private readonly frame;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type IntersectionWatcher, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, filmGrain, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WebGPURenderer, Node } from 'three/webgpu';
|
|
2
1
|
import { Color } from 'three';
|
|
2
|
+
import { WebGPURenderer, Node } from 'three/webgpu';
|
|
3
3
|
import { ShaderNodeObject } from 'three/tsl';
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type GpuBackend = 'webgpu' | 'webgl2';
|
|
6
6
|
interface CreateRendererOptions {
|
|
7
7
|
/** Anti-alias the framebuffer. Default: true. */
|
|
8
8
|
antialias?: boolean;
|
|
@@ -15,11 +15,11 @@ interface CreateRendererOptions {
|
|
|
15
15
|
/** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */
|
|
16
16
|
maxDPR?: number;
|
|
17
17
|
}
|
|
18
|
-
interface
|
|
18
|
+
interface GpuRenderer {
|
|
19
19
|
/** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */
|
|
20
20
|
three: WebGPURenderer;
|
|
21
21
|
/** Which backend the renderer initialized with. */
|
|
22
|
-
backend:
|
|
22
|
+
backend: GpuBackend;
|
|
23
23
|
/** Tear down the renderer and release GPU resources. */
|
|
24
24
|
dispose: () => void;
|
|
25
25
|
/** Resize the renderer to the canvas's current client dimensions. */
|
|
@@ -32,57 +32,7 @@ interface MatterRenderer {
|
|
|
32
32
|
* unavailable on the host. The returned object exposes the underlying
|
|
33
33
|
* three renderer plus a small wrapper for resize and disposal.
|
|
34
34
|
*/
|
|
35
|
-
declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<
|
|
36
|
-
|
|
37
|
-
interface SchedulerTick {
|
|
38
|
-
/** Seconds since the previous tick. 0 on the first call. */
|
|
39
|
-
delta: number;
|
|
40
|
-
/** Total seconds since the scheduler started its current run. */
|
|
41
|
-
elapsed: number;
|
|
42
|
-
/** The raw `performance.now()` timestamp the rAF callback received. */
|
|
43
|
-
now: number;
|
|
44
|
-
}
|
|
45
|
-
type SchedulerClient = (tick: SchedulerTick) => void;
|
|
46
|
-
/**
|
|
47
|
-
* Batches `requestAnimationFrame` calls across all clients registered with
|
|
48
|
-
* a single scheduler. One scheduler is created per <MatterScene>; clients
|
|
49
|
-
* are typically a Three.js renderer's render call.
|
|
50
|
-
*/
|
|
51
|
-
declare class MatterScheduler {
|
|
52
|
-
private readonly clients;
|
|
53
|
-
private rafId;
|
|
54
|
-
private running;
|
|
55
|
-
private paused;
|
|
56
|
-
private idle;
|
|
57
|
-
private flushPending;
|
|
58
|
-
private startedAt;
|
|
59
|
-
private lastTickAt;
|
|
60
|
-
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
61
|
-
start(): void;
|
|
62
|
-
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
63
|
-
stop(): void;
|
|
64
|
-
/** Temporarily skip ticks without losing client registrations. */
|
|
65
|
-
pause(): void;
|
|
66
|
-
/** Resume after pause(). */
|
|
67
|
-
resume(): void;
|
|
68
|
-
/** Register a client to be called every frame. */
|
|
69
|
-
add(client: SchedulerClient): void;
|
|
70
|
-
/** Unregister a client. */
|
|
71
|
-
remove(client: SchedulerClient): void;
|
|
72
|
-
/** Permanent teardown: stop the loop and drop all clients. */
|
|
73
|
-
dispose(): void;
|
|
74
|
-
/**
|
|
75
|
-
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
76
|
-
* uniform changes that triggered the idle state are rendered), then the
|
|
77
|
-
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
78
|
-
*/
|
|
79
|
-
setIdle(idle: boolean): void;
|
|
80
|
-
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
81
|
-
requestRender(): void;
|
|
82
|
-
private maybeQueue;
|
|
83
|
-
private cancel;
|
|
84
|
-
private readonly frame;
|
|
85
|
-
}
|
|
35
|
+
declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<GpuRenderer>;
|
|
86
36
|
|
|
87
37
|
type Vec2 = readonly [number, number];
|
|
88
38
|
interface CursorInputOptions {
|
|
@@ -122,7 +72,7 @@ interface CursorInputOptions {
|
|
|
122
72
|
type ChangeListener = (value: Vec2) => void;
|
|
123
73
|
/**
|
|
124
74
|
* Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.
|
|
125
|
-
* Implements the
|
|
75
|
+
* Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)
|
|
126
76
|
* so it composes with Motion's `useTransform` and similar tools.
|
|
127
77
|
*/
|
|
128
78
|
declare class CursorInput {
|
|
@@ -136,7 +86,7 @@ declare class CursorInput {
|
|
|
136
86
|
private readonly handleMouseMove;
|
|
137
87
|
private disposed;
|
|
138
88
|
constructor(opts?: CursorInputOptions);
|
|
139
|
-
/** Current smoothed position. Implements
|
|
89
|
+
/** Current smoothed position. Implements AnimatableSignal protocol. */
|
|
140
90
|
get(): Vec2;
|
|
141
91
|
/** Subscribe to change events. Returns an unsubscribe function. */
|
|
142
92
|
on(_event: 'change', cb: ChangeListener): () => void;
|
|
@@ -407,4 +357,54 @@ interface IntersectionWatcher {
|
|
|
407
357
|
*/
|
|
408
358
|
declare function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher;
|
|
409
359
|
|
|
410
|
-
|
|
360
|
+
interface SchedulerTick {
|
|
361
|
+
/** Seconds since the previous tick. 0 on the first call. */
|
|
362
|
+
delta: number;
|
|
363
|
+
/** Total seconds since the scheduler started its current run. */
|
|
364
|
+
elapsed: number;
|
|
365
|
+
/** The raw `performance.now()` timestamp the rAF callback received. */
|
|
366
|
+
now: number;
|
|
367
|
+
}
|
|
368
|
+
type SchedulerClient = (tick: SchedulerTick) => void;
|
|
369
|
+
/**
|
|
370
|
+
* Batches `requestAnimationFrame` calls across all clients registered with
|
|
371
|
+
* a single scheduler. One scheduler is created per <ShaderScene>; clients
|
|
372
|
+
* are typically a Three.js renderer's render call.
|
|
373
|
+
*/
|
|
374
|
+
declare class FrameScheduler {
|
|
375
|
+
private readonly clients;
|
|
376
|
+
private rafId;
|
|
377
|
+
private running;
|
|
378
|
+
private paused;
|
|
379
|
+
private idle;
|
|
380
|
+
private flushPending;
|
|
381
|
+
private startedAt;
|
|
382
|
+
private lastTickAt;
|
|
383
|
+
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
384
|
+
start(): void;
|
|
385
|
+
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
386
|
+
stop(): void;
|
|
387
|
+
/** Temporarily skip ticks without losing client registrations. */
|
|
388
|
+
pause(): void;
|
|
389
|
+
/** Resume after pause(). */
|
|
390
|
+
resume(): void;
|
|
391
|
+
/** Register a client to be called every frame. */
|
|
392
|
+
add(client: SchedulerClient): void;
|
|
393
|
+
/** Unregister a client. */
|
|
394
|
+
remove(client: SchedulerClient): void;
|
|
395
|
+
/** Permanent teardown: stop the loop and drop all clients. */
|
|
396
|
+
dispose(): void;
|
|
397
|
+
/**
|
|
398
|
+
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
399
|
+
* uniform changes that triggered the idle state are rendered), then the
|
|
400
|
+
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
401
|
+
*/
|
|
402
|
+
setIdle(idle: boolean): void;
|
|
403
|
+
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
404
|
+
requestRender(): void;
|
|
405
|
+
private maybeQueue;
|
|
406
|
+
private cancel;
|
|
407
|
+
private readonly frame;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type IntersectionWatcher, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, filmGrain, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/runtime/createRenderer.ts
|
|
2
|
-
import { WebGPURenderer } from "three/webgpu";
|
|
3
2
|
import { Color } from "three";
|
|
3
|
+
import { WebGPURenderer } from "three/webgpu";
|
|
4
4
|
async function createRenderer(canvas, opts = {}) {
|
|
5
5
|
const {
|
|
6
6
|
antialias = true,
|
|
@@ -26,7 +26,8 @@ async function createRenderer(canvas, opts = {}) {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
resize();
|
|
29
|
-
const
|
|
29
|
+
const isWebGL = "isWebGLBackend" in three.backend && three.backend.isWebGLBackend === true;
|
|
30
|
+
const backend = forceWebGL || isWebGL ? "webgl2" : "webgpu";
|
|
30
31
|
return {
|
|
31
32
|
three,
|
|
32
33
|
backend,
|
|
@@ -35,104 +36,6 @@ async function createRenderer(canvas, opts = {}) {
|
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// src/runtime/MatterScheduler.ts
|
|
39
|
-
var MatterScheduler = class {
|
|
40
|
-
clients = /* @__PURE__ */ new Set();
|
|
41
|
-
rafId = null;
|
|
42
|
-
running = false;
|
|
43
|
-
paused = false;
|
|
44
|
-
idle = false;
|
|
45
|
-
flushPending = false;
|
|
46
|
-
startedAt = 0;
|
|
47
|
-
lastTickAt = 0;
|
|
48
|
-
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
49
|
-
start() {
|
|
50
|
-
this.running = true;
|
|
51
|
-
this.paused = false;
|
|
52
|
-
this.maybeQueue();
|
|
53
|
-
}
|
|
54
|
-
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
55
|
-
stop() {
|
|
56
|
-
this.running = false;
|
|
57
|
-
this.cancel();
|
|
58
|
-
}
|
|
59
|
-
/** Temporarily skip ticks without losing client registrations. */
|
|
60
|
-
pause() {
|
|
61
|
-
this.paused = true;
|
|
62
|
-
}
|
|
63
|
-
/** Resume after pause(). */
|
|
64
|
-
resume() {
|
|
65
|
-
this.paused = false;
|
|
66
|
-
if (this.running) this.maybeQueue();
|
|
67
|
-
}
|
|
68
|
-
/** Register a client to be called every frame. */
|
|
69
|
-
add(client) {
|
|
70
|
-
this.clients.add(client);
|
|
71
|
-
if (this.running) this.maybeQueue();
|
|
72
|
-
}
|
|
73
|
-
/** Unregister a client. */
|
|
74
|
-
remove(client) {
|
|
75
|
-
this.clients.delete(client);
|
|
76
|
-
}
|
|
77
|
-
/** Permanent teardown: stop the loop and drop all clients. */
|
|
78
|
-
dispose() {
|
|
79
|
-
this.stop();
|
|
80
|
-
this.clients.clear();
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
84
|
-
* uniform changes that triggered the idle state are rendered), then the
|
|
85
|
-
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
86
|
-
*/
|
|
87
|
-
setIdle(idle) {
|
|
88
|
-
if (this.idle === idle) return;
|
|
89
|
-
this.idle = idle;
|
|
90
|
-
if (idle) {
|
|
91
|
-
this.flushPending = true;
|
|
92
|
-
this.maybeQueue();
|
|
93
|
-
} else {
|
|
94
|
-
this.flushPending = false;
|
|
95
|
-
this.maybeQueue();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
99
|
-
requestRender() {
|
|
100
|
-
if (!this.idle) return;
|
|
101
|
-
this.flushPending = true;
|
|
102
|
-
this.maybeQueue();
|
|
103
|
-
}
|
|
104
|
-
maybeQueue() {
|
|
105
|
-
if (this.rafId !== null) return;
|
|
106
|
-
if (!this.running) return;
|
|
107
|
-
if (this.clients.size === 0) return;
|
|
108
|
-
if (this.idle && !this.flushPending) return;
|
|
109
|
-
this.rafId = requestAnimationFrame(this.frame);
|
|
110
|
-
}
|
|
111
|
-
cancel() {
|
|
112
|
-
if (this.rafId !== null) {
|
|
113
|
-
cancelAnimationFrame(this.rafId);
|
|
114
|
-
this.rafId = null;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
frame = (now) => {
|
|
118
|
-
this.rafId = null;
|
|
119
|
-
if (!this.running || this.paused) return;
|
|
120
|
-
if (this.startedAt === 0) {
|
|
121
|
-
this.startedAt = now;
|
|
122
|
-
this.lastTickAt = now;
|
|
123
|
-
}
|
|
124
|
-
const delta = (now - this.lastTickAt) / 1e3;
|
|
125
|
-
const elapsed = (now - this.startedAt) / 1e3;
|
|
126
|
-
this.lastTickAt = now;
|
|
127
|
-
const tick = { delta, elapsed, now };
|
|
128
|
-
for (const client of this.clients) {
|
|
129
|
-
client(tick);
|
|
130
|
-
}
|
|
131
|
-
this.flushPending = false;
|
|
132
|
-
this.maybeQueue();
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
39
|
// src/inputs/CursorInput.ts
|
|
137
40
|
var CursorInput = class {
|
|
138
41
|
value;
|
|
@@ -152,6 +55,7 @@ var CursorInput = class {
|
|
|
152
55
|
this.eventTarget = target ?? (typeof window !== "undefined" ? window : new EventTarget());
|
|
153
56
|
this.element = element;
|
|
154
57
|
this.handleMouseMove = (e) => {
|
|
58
|
+
if (!(e instanceof MouseEvent)) return;
|
|
155
59
|
const me = e;
|
|
156
60
|
if (this.element) {
|
|
157
61
|
const r = this.element.getBoundingClientRect();
|
|
@@ -167,7 +71,7 @@ var CursorInput = class {
|
|
|
167
71
|
};
|
|
168
72
|
this.eventTarget.addEventListener("mousemove", this.handleMouseMove);
|
|
169
73
|
}
|
|
170
|
-
/** Current smoothed position. Implements
|
|
74
|
+
/** Current smoothed position. Implements AnimatableSignal protocol. */
|
|
171
75
|
get() {
|
|
172
76
|
return this.value;
|
|
173
77
|
}
|
|
@@ -210,12 +114,14 @@ var lerp = (a, b, t) => a + (b - a) * t;
|
|
|
210
114
|
import { mix, vec3 } from "three/tsl";
|
|
211
115
|
import { clamp, div, sub } from "three/tsl";
|
|
212
116
|
function colorRamp(t, stops) {
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
117
|
+
const first = stops[0];
|
|
118
|
+
if (first === void 0) return vec3(0, 0, 0);
|
|
119
|
+
if (stops.length === 1) return mix(first.color, first.color, 0);
|
|
120
|
+
let result = mix(first.color, first.color, 0);
|
|
121
|
+
for (let i = 1; i < stops.length; i += 1) {
|
|
217
122
|
const prev = stops[i - 1];
|
|
218
123
|
const next = stops[i];
|
|
124
|
+
if (prev === void 0 || next === void 0) continue;
|
|
219
125
|
const span = next.position - prev.position;
|
|
220
126
|
if (span <= 0) continue;
|
|
221
127
|
const localT = clamp(div(sub(t, prev.position), span), 0, 1);
|
|
@@ -240,7 +146,7 @@ function fbm(p, opts = {}) {
|
|
|
240
146
|
let amp = 1;
|
|
241
147
|
let freq = 1;
|
|
242
148
|
let total = amp;
|
|
243
|
-
for (let i = 1; i < octaves; i
|
|
149
|
+
for (let i = 1; i < octaves; i += 1) {
|
|
244
150
|
freq *= lacunarity;
|
|
245
151
|
amp *= gain;
|
|
246
152
|
total += amp;
|
|
@@ -279,7 +185,7 @@ function displace(p, by) {
|
|
|
279
185
|
}
|
|
280
186
|
|
|
281
187
|
// src/primitives/cursorRipple.ts
|
|
282
|
-
import {
|
|
188
|
+
import { length as length2, sin, smoothstep, sub as sub2 } from "three/tsl";
|
|
283
189
|
|
|
284
190
|
// src/primitives/time.ts
|
|
285
191
|
import { time as _builtinTime } from "three/tsl";
|
|
@@ -387,7 +293,7 @@ function cursorRipple(p, center, opts = {}) {
|
|
|
387
293
|
}
|
|
388
294
|
|
|
389
295
|
// src/primitives/filmGrain.ts
|
|
390
|
-
import {
|
|
296
|
+
import { fract, length as length3, sin as sin2, vec2 } from "three/tsl";
|
|
391
297
|
function filmGrain(uvNode, intensity, timeOffset = 0) {
|
|
392
298
|
const HASH_C1 = vec2(2127.1, 81.17);
|
|
393
299
|
const HASH_C2 = vec2(1269.5, 283.37);
|
|
@@ -461,9 +367,107 @@ function createIntersectionWatcher(canvas) {
|
|
|
461
367
|
}
|
|
462
368
|
};
|
|
463
369
|
}
|
|
370
|
+
|
|
371
|
+
// src/runtime/frame-scheduler.ts
|
|
372
|
+
var FrameScheduler = class {
|
|
373
|
+
clients = /* @__PURE__ */ new Set();
|
|
374
|
+
rafId = null;
|
|
375
|
+
running = false;
|
|
376
|
+
paused = false;
|
|
377
|
+
idle = false;
|
|
378
|
+
flushPending = false;
|
|
379
|
+
startedAt = 0;
|
|
380
|
+
lastTickAt = 0;
|
|
381
|
+
/** Activate the scheduler. The rAF loop starts on the first client added. */
|
|
382
|
+
start() {
|
|
383
|
+
this.running = true;
|
|
384
|
+
this.paused = false;
|
|
385
|
+
this.maybeQueue();
|
|
386
|
+
}
|
|
387
|
+
/** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
|
|
388
|
+
stop() {
|
|
389
|
+
this.running = false;
|
|
390
|
+
this.cancel();
|
|
391
|
+
}
|
|
392
|
+
/** Temporarily skip ticks without losing client registrations. */
|
|
393
|
+
pause() {
|
|
394
|
+
this.paused = true;
|
|
395
|
+
}
|
|
396
|
+
/** Resume after pause(). */
|
|
397
|
+
resume() {
|
|
398
|
+
this.paused = false;
|
|
399
|
+
if (this.running) this.maybeQueue();
|
|
400
|
+
}
|
|
401
|
+
/** Register a client to be called every frame. */
|
|
402
|
+
add(client) {
|
|
403
|
+
this.clients.add(client);
|
|
404
|
+
if (this.running) this.maybeQueue();
|
|
405
|
+
}
|
|
406
|
+
/** Unregister a client. */
|
|
407
|
+
remove(client) {
|
|
408
|
+
this.clients.delete(client);
|
|
409
|
+
}
|
|
410
|
+
/** Permanent teardown: stop the loop and drop all clients. */
|
|
411
|
+
dispose() {
|
|
412
|
+
this.stop();
|
|
413
|
+
this.clients.clear();
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Mark the scheduler idle. The next tick still fires (a final flush so
|
|
417
|
+
* uniform changes that triggered the idle state are rendered), then the
|
|
418
|
+
* rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
|
|
419
|
+
*/
|
|
420
|
+
setIdle(idle) {
|
|
421
|
+
if (this.idle === idle) return;
|
|
422
|
+
this.idle = idle;
|
|
423
|
+
if (idle) {
|
|
424
|
+
this.flushPending = true;
|
|
425
|
+
this.maybeQueue();
|
|
426
|
+
} else {
|
|
427
|
+
this.flushPending = false;
|
|
428
|
+
this.maybeQueue();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/** Force a single tick while idle. Useful for prop-change invalidation. */
|
|
432
|
+
requestRender() {
|
|
433
|
+
if (!this.idle) return;
|
|
434
|
+
this.flushPending = true;
|
|
435
|
+
this.maybeQueue();
|
|
436
|
+
}
|
|
437
|
+
maybeQueue() {
|
|
438
|
+
if (this.rafId !== null) return;
|
|
439
|
+
if (!this.running) return;
|
|
440
|
+
if (this.clients.size === 0) return;
|
|
441
|
+
if (this.idle && !this.flushPending) return;
|
|
442
|
+
this.rafId = requestAnimationFrame(this.frame);
|
|
443
|
+
}
|
|
444
|
+
cancel() {
|
|
445
|
+
if (this.rafId !== null) {
|
|
446
|
+
cancelAnimationFrame(this.rafId);
|
|
447
|
+
this.rafId = null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
frame = (now) => {
|
|
451
|
+
this.rafId = null;
|
|
452
|
+
if (!this.running || this.paused) return;
|
|
453
|
+
if (this.startedAt === 0) {
|
|
454
|
+
this.startedAt = now;
|
|
455
|
+
this.lastTickAt = now;
|
|
456
|
+
}
|
|
457
|
+
const delta = (now - this.lastTickAt) / 1e3;
|
|
458
|
+
const elapsed = (now - this.startedAt) / 1e3;
|
|
459
|
+
this.lastTickAt = now;
|
|
460
|
+
const tick = { delta, elapsed, now };
|
|
461
|
+
for (const client of this.clients) {
|
|
462
|
+
client(tick);
|
|
463
|
+
}
|
|
464
|
+
this.flushPending = false;
|
|
465
|
+
this.maybeQueue();
|
|
466
|
+
};
|
|
467
|
+
};
|
|
464
468
|
export {
|
|
465
469
|
CursorInput,
|
|
466
|
-
|
|
470
|
+
FrameScheduler,
|
|
467
471
|
colorRamp,
|
|
468
472
|
createIntersectionWatcher,
|
|
469
473
|
createReducedMotionWatcher,
|