@nous-research/ui 0.11.0 → 0.13.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/dist/hooks/use-gpu-tier.d.ts +15 -1
- package/dist/hooks/use-gpu-tier.d.ts.map +1 -1
- package/dist/hooks/use-gpu-tier.js +48 -18
- package/dist/hooks/use-gpu-tier.js.map +1 -1
- package/dist/hooks/use-render-loop.d.ts +42 -0
- package/dist/hooks/use-render-loop.d.ts.map +1 -0
- package/dist/hooks/use-render-loop.js +62 -0
- package/dist/hooks/use-render-loop.js.map +1 -0
- package/dist/ui/components/image-distortion.d.ts.map +1 -1
- package/dist/ui/components/image-distortion.js +27 -2
- package/dist/ui/components/image-distortion.js.map +1 -1
- package/dist/ui/components/overlays/glitch.d.ts.map +1 -1
- package/dist/ui/components/overlays/glitch.js +42 -30
- package/dist/ui/components/overlays/glitch.js.map +1 -1
- package/dist/ui/components/overlays/greys.d.ts.map +1 -1
- package/dist/ui/components/overlays/greys.js +56 -39
- package/dist/ui/components/overlays/greys.js.map +1 -1
- package/dist/ui/components/overlays/noise.d.ts.map +1 -1
- package/dist/ui/components/overlays/noise.js +52 -31
- package/dist/ui/components/overlays/noise.js.map +1 -1
- package/dist/ui/components/scene-canvas.d.ts +9 -1
- package/dist/ui/components/scene-canvas.d.ts.map +1 -1
- package/dist/ui/components/scene-canvas.js +13 -2
- package/dist/ui/components/scene-canvas.js.map +1 -1
- package/dist/ui/components/tv.d.ts.map +1 -1
- package/dist/ui/components/tv.js +31 -4
- package/dist/ui/components/tv.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tiers:
|
|
3
|
-
* 0 — no WebGL / software renderer / prefers-reduced-motion
|
|
3
|
+
* 0 — no WebGL / software renderer / prefers-reduced-motion / WebGL ctx creation failed
|
|
4
4
|
* 1 — low-end GPU (integrated, mobile, or failed perf benchmark)
|
|
5
5
|
* 2 — capable GPU (discrete / high-end integrated)
|
|
6
|
+
*
|
|
7
|
+
* Detection runs **synchronously** the first time this module is evaluated
|
|
8
|
+
* on the client (see the IIFE at the bottom of the file). That means any
|
|
9
|
+
* consumer reading `$gpuTier` during its first render already sees the
|
|
10
|
+
* post-detection value, so WebGL components can avoid trying to create a
|
|
11
|
+
* `THREE.WebGLRenderer` on hardware where context creation will fail.
|
|
12
|
+
*
|
|
13
|
+
* The previous version ran the probe inside `nanostores`'s `onMount`
|
|
14
|
+
* lifecycle, which fires from a microtask after the first listener
|
|
15
|
+
* subscribes — i.e. after the first React commit. By that point overlay
|
|
16
|
+
* components had already executed `new THREE.WebGLRenderer(...)` against
|
|
17
|
+
* the optimistic default, logged `Error creating WebGL context`, and only
|
|
18
|
+
* unmounted on a follow-up render. Eager module-load detection closes that
|
|
19
|
+
* race.
|
|
6
20
|
*/
|
|
7
21
|
export declare const $gpuTier: import("nanostores").PreinitializedWritableAtom<GpuTier> & object;
|
|
8
22
|
export declare function useGpuTier(): GpuTier;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-gpu-tier.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gpu-tier.ts"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"use-gpu-tier.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gpu-tier.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,mEAAmB,CAAA;AAuIxC,wBAAgB,UAAU,YAEzB;AAED,KAAK,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA"}
|
|
@@ -1,49 +1,79 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
|
-
import { atom
|
|
3
|
+
import { atom } from 'nanostores';
|
|
4
4
|
/**
|
|
5
5
|
* Tiers:
|
|
6
|
-
* 0 — no WebGL / software renderer / prefers-reduced-motion
|
|
6
|
+
* 0 — no WebGL / software renderer / prefers-reduced-motion / WebGL ctx creation failed
|
|
7
7
|
* 1 — low-end GPU (integrated, mobile, or failed perf benchmark)
|
|
8
8
|
* 2 — capable GPU (discrete / high-end integrated)
|
|
9
|
+
*
|
|
10
|
+
* Detection runs **synchronously** the first time this module is evaluated
|
|
11
|
+
* on the client (see the IIFE at the bottom of the file). That means any
|
|
12
|
+
* consumer reading `$gpuTier` during its first render already sees the
|
|
13
|
+
* post-detection value, so WebGL components can avoid trying to create a
|
|
14
|
+
* `THREE.WebGLRenderer` on hardware where context creation will fail.
|
|
15
|
+
*
|
|
16
|
+
* The previous version ran the probe inside `nanostores`'s `onMount`
|
|
17
|
+
* lifecycle, which fires from a microtask after the first listener
|
|
18
|
+
* subscribes — i.e. after the first React commit. By that point overlay
|
|
19
|
+
* components had already executed `new THREE.WebGLRenderer(...)` against
|
|
20
|
+
* the optimistic default, logged `Error creating WebGL context`, and only
|
|
21
|
+
* unmounted on a follow-up render. Eager module-load detection closes that
|
|
22
|
+
* race.
|
|
9
23
|
*/
|
|
10
24
|
export const $gpuTier = atom(2);
|
|
11
25
|
const SOFTWARE_PATTERNS = /swiftshader|llvmpipe|softpipe|software|microsoft basic/i;
|
|
12
26
|
const LOW_END_PATTERNS = /intel.*hd|intel.*uhd|intel.*iris|mali|adreno\s?[1-5]|powervr|apple gpu/i;
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
let detected = false;
|
|
28
|
+
function detectGpuTier() {
|
|
29
|
+
if (detected || typeof window === 'undefined') {
|
|
15
30
|
return;
|
|
16
31
|
}
|
|
32
|
+
detected = true;
|
|
17
33
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
18
34
|
$gpuTier.set(0);
|
|
19
35
|
return;
|
|
20
36
|
}
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
let gl = null;
|
|
38
|
+
try {
|
|
39
|
+
const canvas = document.createElement('canvas');
|
|
40
|
+
gl = (canvas.getContext('webgl') ||
|
|
41
|
+
canvas.getContext('experimental-webgl'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Some sandboxed / hardened contexts throw on getContext rather than
|
|
45
|
+
// returning null (e.g. certain corporate browser policies). Treat as
|
|
46
|
+
// "no WebGL available".
|
|
47
|
+
$gpuTier.set(0);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
23
50
|
if (!gl) {
|
|
24
51
|
$gpuTier.set(0);
|
|
25
52
|
return;
|
|
26
53
|
}
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
: glCtx.getParameter(glCtx.RENDERER);
|
|
54
|
+
const ext = gl.getExtension('WEBGL_debug_renderer_info');
|
|
55
|
+
const renderer = String(ext
|
|
56
|
+
? gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)
|
|
57
|
+
: gl.getParameter(gl.RENDERER));
|
|
32
58
|
if (SOFTWARE_PATTERNS.test(renderer)) {
|
|
33
59
|
$gpuTier.set(0);
|
|
34
|
-
|
|
60
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
35
61
|
return;
|
|
36
62
|
}
|
|
37
63
|
if (LOW_END_PATTERNS.test(renderer)) {
|
|
38
64
|
$gpuTier.set(1);
|
|
39
|
-
|
|
65
|
+
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
|
40
66
|
return;
|
|
41
67
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
$gpuTier.set(2);
|
|
69
|
+
runBenchmark(gl)
|
|
70
|
+
.then(fps => $gpuTier.set(fps < 30 ? 1 : 2))
|
|
71
|
+
.catch(() => $gpuTier.set(1))
|
|
72
|
+
.finally(() => gl?.getExtension('WEBGL_lose_context')?.loseContext());
|
|
73
|
+
}
|
|
74
|
+
if (typeof window !== 'undefined') {
|
|
75
|
+
detectGpuTier();
|
|
76
|
+
}
|
|
47
77
|
function runBenchmark(gl) {
|
|
48
78
|
return new Promise(resolve => {
|
|
49
79
|
const vs = gl.createShader(gl.VERTEX_SHADER);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-gpu-tier.js","sourceRoot":"","sources":["../../src/hooks/use-gpu-tier.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"use-gpu-tier.js","sourceRoot":"","sources":["../../src/hooks/use-gpu-tier.ts"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useStore } from '@nanostores/react'\nimport { atom } from 'nanostores'\n\n/**\n * Tiers:\n * 0 — no WebGL / software renderer / prefers-reduced-motion / WebGL ctx creation failed\n * 1 — low-end GPU (integrated, mobile, or failed perf benchmark)\n * 2 — capable GPU (discrete / high-end integrated)\n *\n * Detection runs **synchronously** the first time this module is evaluated\n * on the client (see the IIFE at the bottom of the file). That means any\n * consumer reading `$gpuTier` during its first render already sees the\n * post-detection value, so WebGL components can avoid trying to create a\n * `THREE.WebGLRenderer` on hardware where context creation will fail.\n *\n * The previous version ran the probe inside `nanostores`'s `onMount`\n * lifecycle, which fires from a microtask after the first listener\n * subscribes — i.e. after the first React commit. By that point overlay\n * components had already executed `new THREE.WebGLRenderer(...)` against\n * the optimistic default, logged `Error creating WebGL context`, and only\n * unmounted on a follow-up render. Eager module-load detection closes that\n * race.\n */\nexport const $gpuTier = atom<GpuTier>(2)\n\nconst SOFTWARE_PATTERNS =\n /swiftshader|llvmpipe|softpipe|software|microsoft basic/i\n\nconst LOW_END_PATTERNS =\n /intel.*hd|intel.*uhd|intel.*iris|mali|adreno\\s?[1-5]|powervr|apple gpu/i\n\nlet detected = false\n\nfunction detectGpuTier() {\n if (detected || typeof window === 'undefined') {\n return\n }\n\n detected = true\n\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n $gpuTier.set(0)\n\n return\n }\n\n let gl: null | WebGLRenderingContext = null\n\n try {\n const canvas = document.createElement('canvas')\n gl = (canvas.getContext('webgl') ||\n canvas.getContext('experimental-webgl')) as null | WebGLRenderingContext\n } catch {\n // Some sandboxed / hardened contexts throw on getContext rather than\n // returning null (e.g. certain corporate browser policies). Treat as\n // \"no WebGL available\".\n $gpuTier.set(0)\n\n return\n }\n\n if (!gl) {\n $gpuTier.set(0)\n\n return\n }\n\n const ext = gl.getExtension('WEBGL_debug_renderer_info')\n const renderer = String(\n ext\n ? gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)\n : gl.getParameter(gl.RENDERER)\n )\n\n if (SOFTWARE_PATTERNS.test(renderer)) {\n $gpuTier.set(0)\n gl.getExtension('WEBGL_lose_context')?.loseContext()\n\n return\n }\n\n if (LOW_END_PATTERNS.test(renderer)) {\n $gpuTier.set(1)\n gl.getExtension('WEBGL_lose_context')?.loseContext()\n\n return\n }\n\n $gpuTier.set(2)\n\n runBenchmark(gl)\n .then(fps => $gpuTier.set(fps < 30 ? 1 : 2))\n .catch(() => $gpuTier.set(1))\n .finally(() => gl?.getExtension('WEBGL_lose_context')?.loseContext())\n}\n\nif (typeof window !== 'undefined') {\n detectGpuTier()\n}\n\nfunction runBenchmark(gl: WebGLRenderingContext): Promise<number> {\n return new Promise(resolve => {\n const vs = gl.createShader(gl.VERTEX_SHADER)!\n const fs = gl.createShader(gl.FRAGMENT_SHADER)!\n gl.shaderSource(\n vs,\n 'attribute vec2 a;void main(){gl_Position=vec4(a,0,1);}'\n )\n gl.shaderSource(\n fs,\n 'precision highp float;uniform float t;void main(){float v=0.;for(int i=0;i<64;i++)v+=sin(float(i)*t*.01);gl_FragColor=vec4(v*.001);}'\n )\n gl.compileShader(vs)\n gl.compileShader(fs)\n\n const prog = gl.createProgram()!\n gl.attachShader(prog, vs)\n gl.attachShader(prog, fs)\n gl.linkProgram(prog)\n gl.useProgram(prog)\n\n const buf = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, buf)\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW\n )\n const a = gl.getAttribLocation(prog, 'a')\n gl.enableVertexAttribArray(a)\n gl.vertexAttribPointer(a, 2, gl.FLOAT, false, 0, 0)\n\n const uT = gl.getUniformLocation(prog, 't')\n let frames = 0\n const start = performance.now()\n\n const tick = () => {\n gl.uniform1f(uT, frames)\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n gl.finish()\n frames++\n\n if (performance.now() - start < 200) {\n requestAnimationFrame(tick)\n } else {\n const elapsed = performance.now() - start\n gl.deleteProgram(prog)\n gl.deleteShader(vs)\n gl.deleteShader(fs)\n gl.deleteBuffer(buf)\n resolve((frames / elapsed) * 1000)\n }\n }\n\n requestAnimationFrame(tick)\n })\n}\n\nexport function useGpuTier() {\n return useStore($gpuTier)\n}\n\ntype GpuTier = 0 | 1 | 2\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visibility- and intersection-aware render-loop helper for the WebGL
|
|
3
|
+
* overlays.
|
|
4
|
+
*
|
|
5
|
+
* The overlays were previously running fragment shaders at 60fps for the
|
|
6
|
+
* entire lifetime of the page — including when the tab was hidden, the
|
|
7
|
+
* canvas had been scrolled out of view, or the user had been idle for
|
|
8
|
+
* hours. On retina laptops the compositor cost of mix-blend-mode on a
|
|
9
|
+
* full-viewport canvas plus continuous WebGL rasterisation is enough to
|
|
10
|
+
* keep the GPU hot indefinitely, which is what manifests as "fans go
|
|
11
|
+
* crazy after 2 hours of idle".
|
|
12
|
+
*
|
|
13
|
+
* `runRenderLoop` wraps a frame callback so that it:
|
|
14
|
+
*
|
|
15
|
+
* 1. Pauses entirely when `document.hidden` is true (background tab,
|
|
16
|
+
* minimised window, screen locked).
|
|
17
|
+
* 2. Pauses when the canvas's bounding rect is offscreen (we tell
|
|
18
|
+
* `IntersectionObserver` to look at the canvas itself).
|
|
19
|
+
* 3. Optionally caps the frame rate via a min-interval — the previous
|
|
20
|
+
* `gpuTier === 1 ? setTimeout(loop, 100) : raf` trick is preserved
|
|
21
|
+
* and extended so even tier-2 GPUs cap at e.g. 30fps for overlays
|
|
22
|
+
* that don't need 60.
|
|
23
|
+
*
|
|
24
|
+
* The callback receives the *delta* time in seconds since the last call
|
|
25
|
+
* (so `uTime` advances correctly across pauses without ever skipping
|
|
26
|
+
* forward by hours).
|
|
27
|
+
*/
|
|
28
|
+
interface RunRenderLoopOptions {
|
|
29
|
+
/** Element to observe with IntersectionObserver. When fully out of
|
|
30
|
+
* view, the loop pauses. Pass the canvas element itself. */
|
|
31
|
+
el: Element;
|
|
32
|
+
/** Min ms between frames. 0 = no cap (uses requestAnimationFrame).
|
|
33
|
+
* Anything > 0 uses setTimeout-driven scheduling. */
|
|
34
|
+
minIntervalMs?: number;
|
|
35
|
+
/** Frame callback. Receives the elapsed seconds since the previous
|
|
36
|
+
* *executed* frame (not since the previous scheduled frame), so
|
|
37
|
+
* uniforms keyed off this value will not jump after a long pause. */
|
|
38
|
+
onFrame: (deltaSeconds: number) => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function runRenderLoop({ el, minIntervalMs, onFrame }: RunRenderLoopOptions): () => void;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=use-render-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-render-loop.d.ts","sourceRoot":"","sources":["../../src/hooks/use-render-loop.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,UAAU,oBAAoB;IAC5B;iEAC6D;IAC7D,EAAE,EAAE,OAAO,CAAA;IACX;0DACsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;0EAEsE;IACtE,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,aAAiB,EACjB,OAAO,EACR,EAAE,oBAAoB,cAyEtB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
export function runRenderLoop({ el, minIntervalMs = 0, onFrame }) {
|
|
3
|
+
let running = true;
|
|
4
|
+
let visible = !document.hidden;
|
|
5
|
+
let inView = true;
|
|
6
|
+
let last = performance.now();
|
|
7
|
+
let raf = 0;
|
|
8
|
+
let timer;
|
|
9
|
+
const onVisibility = () => {
|
|
10
|
+
visible = !document.hidden;
|
|
11
|
+
// When we come back from a hidden tab, reset the clock so the next
|
|
12
|
+
// frame's delta is ~one frame, not "hours since I was hidden".
|
|
13
|
+
if (visible) {
|
|
14
|
+
last = performance.now();
|
|
15
|
+
schedule();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const io = new IntersectionObserver(entries => {
|
|
19
|
+
const wasInView = inView;
|
|
20
|
+
inView = entries.some(e => e.isIntersecting);
|
|
21
|
+
if (!wasInView && inView) {
|
|
22
|
+
last = performance.now();
|
|
23
|
+
schedule();
|
|
24
|
+
}
|
|
25
|
+
}, { threshold: 0 });
|
|
26
|
+
io.observe(el);
|
|
27
|
+
document.addEventListener('visibilitychange', onVisibility);
|
|
28
|
+
const tick = () => {
|
|
29
|
+
if (!running)
|
|
30
|
+
return;
|
|
31
|
+
if (!visible || !inView) {
|
|
32
|
+
// Don't reschedule — we'll be re-kicked by visibilitychange or IO.
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const now = performance.now();
|
|
36
|
+
const delta = (now - last) / 1000;
|
|
37
|
+
last = now;
|
|
38
|
+
onFrame(delta);
|
|
39
|
+
schedule();
|
|
40
|
+
};
|
|
41
|
+
function schedule() {
|
|
42
|
+
if (!running || !visible || !inView)
|
|
43
|
+
return;
|
|
44
|
+
if (minIntervalMs > 0) {
|
|
45
|
+
timer = setTimeout(tick, minIntervalMs);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
raf = requestAnimationFrame(tick);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
schedule();
|
|
52
|
+
return () => {
|
|
53
|
+
running = false;
|
|
54
|
+
io.disconnect();
|
|
55
|
+
document.removeEventListener('visibilitychange', onVisibility);
|
|
56
|
+
cancelAnimationFrame(raf);
|
|
57
|
+
if (timer !== undefined) {
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=use-render-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-render-loop.js","sourceRoot":"","sources":["../../src/hooks/use-render-loop.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AA2CZ,MAAM,UAAU,aAAa,CAAC,EAC5B,EAAE,EACF,aAAa,GAAG,CAAC,EACjB,OAAO,EACc;IACrB,IAAI,OAAO,GAAG,IAAI,CAAA;IAClB,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC9B,IAAI,MAAM,GAAG,IAAI,CAAA;IACjB,IAAI,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC5B,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,IAAI,KAAgD,CAAA;IAEpD,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAA;QAE1B,mEAAmE;QACnE,+DAA+D;QAC/D,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;YACxB,QAAQ,EAAE,CAAA;QACZ,CAAC;IACH,CAAC,CAAA;IAED,MAAM,EAAE,GAAG,IAAI,oBAAoB,CACjC,OAAO,CAAC,EAAE;QACR,MAAM,SAAS,GAAG,MAAM,CAAA;QACxB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAA;QAE5C,IAAI,CAAC,SAAS,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;YACxB,QAAQ,EAAE,CAAA;QACZ,CAAC;IACH,CAAC,EACD,EAAE,SAAS,EAAE,CAAC,EAAE,CACjB,CAAA;IAED,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACd,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAA;IAE3D,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpB,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;YACxB,mEAAmE;YACnE,OAAM;QACR,CAAC;QAED,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QAC7B,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;QACjC,IAAI,GAAG,GAAG,CAAA;QAEV,OAAO,CAAC,KAAK,CAAC,CAAA;QACd,QAAQ,EAAE,CAAA;IACZ,CAAC,CAAA;IAED,SAAS,QAAQ;QACf,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM;YAAE,OAAM;QAE3C,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACtB,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;QACzC,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAED,QAAQ,EAAE,CAAA;IAEV,OAAO,GAAG,EAAE;QACV,OAAO,GAAG,KAAK,CAAA;QACf,EAAE,CAAC,UAAU,EAAE,CAAA;QACf,QAAQ,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAA;QAC9D,oBAAoB,CAAC,GAAG,CAAC,CAAA;QAEzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,YAAY,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\n/**\n * Visibility- and intersection-aware render-loop helper for the WebGL\n * overlays.\n *\n * The overlays were previously running fragment shaders at 60fps for the\n * entire lifetime of the page — including when the tab was hidden, the\n * canvas had been scrolled out of view, or the user had been idle for\n * hours. On retina laptops the compositor cost of mix-blend-mode on a\n * full-viewport canvas plus continuous WebGL rasterisation is enough to\n * keep the GPU hot indefinitely, which is what manifests as \"fans go\n * crazy after 2 hours of idle\".\n *\n * `runRenderLoop` wraps a frame callback so that it:\n *\n * 1. Pauses entirely when `document.hidden` is true (background tab,\n * minimised window, screen locked).\n * 2. Pauses when the canvas's bounding rect is offscreen (we tell\n * `IntersectionObserver` to look at the canvas itself).\n * 3. Optionally caps the frame rate via a min-interval — the previous\n * `gpuTier === 1 ? setTimeout(loop, 100) : raf` trick is preserved\n * and extended so even tier-2 GPUs cap at e.g. 30fps for overlays\n * that don't need 60.\n *\n * The callback receives the *delta* time in seconds since the last call\n * (so `uTime` advances correctly across pauses without ever skipping\n * forward by hours).\n */\n\ninterface RunRenderLoopOptions {\n /** Element to observe with IntersectionObserver. When fully out of\n * view, the loop pauses. Pass the canvas element itself. */\n el: Element\n /** Min ms between frames. 0 = no cap (uses requestAnimationFrame).\n * Anything > 0 uses setTimeout-driven scheduling. */\n minIntervalMs?: number\n /** Frame callback. Receives the elapsed seconds since the previous\n * *executed* frame (not since the previous scheduled frame), so\n * uniforms keyed off this value will not jump after a long pause. */\n onFrame: (deltaSeconds: number) => void\n}\n\nexport function runRenderLoop({\n el,\n minIntervalMs = 0,\n onFrame\n}: RunRenderLoopOptions) {\n let running = true\n let visible = !document.hidden\n let inView = true\n let last = performance.now()\n let raf = 0\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const onVisibility = () => {\n visible = !document.hidden\n\n // When we come back from a hidden tab, reset the clock so the next\n // frame's delta is ~one frame, not \"hours since I was hidden\".\n if (visible) {\n last = performance.now()\n schedule()\n }\n }\n\n const io = new IntersectionObserver(\n entries => {\n const wasInView = inView\n inView = entries.some(e => e.isIntersecting)\n\n if (!wasInView && inView) {\n last = performance.now()\n schedule()\n }\n },\n { threshold: 0 }\n )\n\n io.observe(el)\n document.addEventListener('visibilitychange', onVisibility)\n\n const tick = () => {\n if (!running) return\n\n if (!visible || !inView) {\n // Don't reschedule — we'll be re-kicked by visibilitychange or IO.\n return\n }\n\n const now = performance.now()\n const delta = (now - last) / 1000\n last = now\n\n onFrame(delta)\n schedule()\n }\n\n function schedule() {\n if (!running || !visible || !inView) return\n\n if (minIntervalMs > 0) {\n timer = setTimeout(tick, minIntervalMs)\n } else {\n raf = requestAnimationFrame(tick)\n }\n }\n\n schedule()\n\n return () => {\n running = false\n io.disconnect()\n document.removeEventListener('visibilitychange', onVisibility)\n cancelAnimationFrame(raf)\n\n if (timer !== undefined) {\n clearTimeout(timer)\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-distortion.d.ts","sourceRoot":"","sources":["../../../src/ui/components/image-distortion.tsx"],"names":[],"mappings":"AAmJA,wBAAgB,eAAe,CAAC,EAC9B,MAAa,EACb,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACb,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"image-distortion.d.ts","sourceRoot":"","sources":["../../../src/ui/components/image-distortion.tsx"],"names":[],"mappings":"AAmJA,wBAAgB,eAAe,CAAC,EAC9B,MAAa,EACb,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,GAAG,EACH,KAAK,EACL,IAAI,EACJ,YAAY,EACb,EAAE,oBAAoB,2CAmUtB;AAED,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE/D,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CACpD"}
|
|
@@ -254,6 +254,8 @@ export function ImageDistortion({ active = true, autoPlay, className, fallbackCl
|
|
|
254
254
|
: [1, 1, 1];
|
|
255
255
|
const t0 = performance.now();
|
|
256
256
|
let raf = 0;
|
|
257
|
+
let visible = !document.hidden;
|
|
258
|
+
let inView = true;
|
|
257
259
|
const loop = () => {
|
|
258
260
|
raf = requestAnimationFrame(loop);
|
|
259
261
|
const s = state.current;
|
|
@@ -306,9 +308,32 @@ export function ImageDistortion({ active = true, autoPlay, className, fallbackCl
|
|
|
306
308
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
307
309
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
308
310
|
};
|
|
309
|
-
|
|
311
|
+
const start = () => {
|
|
312
|
+
if (visible && inView && !raf) {
|
|
313
|
+
raf = requestAnimationFrame(loop);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const stop = () => {
|
|
317
|
+
if (raf) {
|
|
318
|
+
cancelAnimationFrame(raf);
|
|
319
|
+
raf = 0;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const onVisibility = () => {
|
|
323
|
+
visible = !document.hidden;
|
|
324
|
+
visible ? start() : stop();
|
|
325
|
+
};
|
|
326
|
+
const io = new IntersectionObserver(entries => {
|
|
327
|
+
inView = entries.some(e => e.isIntersecting);
|
|
328
|
+
inView ? start() : stop();
|
|
329
|
+
}, { threshold: 0 });
|
|
330
|
+
io.observe(c);
|
|
331
|
+
document.addEventListener('visibilitychange', onVisibility);
|
|
332
|
+
start();
|
|
310
333
|
return () => {
|
|
311
|
-
|
|
334
|
+
stop();
|
|
335
|
+
io.disconnect();
|
|
336
|
+
document.removeEventListener('visibilitychange', onVisibility);
|
|
312
337
|
ro.disconnect();
|
|
313
338
|
c.removeEventListener('pointermove', onMove);
|
|
314
339
|
c.removeEventListener('pointerenter', onEnter);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-distortion.js","sourceRoot":"","sources":["../../../src/ui/components/image-distortion.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { useGpuTier } from '../../hooks/use-gpu-tier'\nimport { cn, hexToRgb } from '../../utils'\n\nconst NUM_BANDS = 12\n\nconst VERT = `attribute vec2 a;varying vec2 vUv;void main(){vUv=vec2(a.x*.5+.5,.5-a.y*.5);gl_Position=vec4(a,0,1);}`\n\nconst FRAG = `precision highp float;\nuniform float t;\nuniform vec2 r,imgSize,vel;\nuniform sampler2D tex;\nuniform float bands[${NUM_BANDS}];\nuniform vec3 tint;\nuniform float tintStrength;\nvarying vec2 vUv;\n\nfloat h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\n\n// cover-style UV: crops the image to fill the canvas, centered\nvec2 coverUV(vec2 uv){\n float canvasAspect=r.x/r.y;\n float imgAspect=imgSize.x/imgSize.y;\n vec2 scale=canvasAspect>imgAspect\n ?vec2(1.0,imgAspect/canvasAspect)\n :vec2(canvasAspect/imgAspect,1.0);\n return(uv-0.5)*scale+0.5;\n}\n\nvoid main(){\n vec2 uv=coverUV(vUv);\n float scanY=floor(vUv.y*r.y);\n\n float bandF=vUv.y*${NUM_BANDS}.0;\n int bandIdx=int(floor(bandF));\n float bandFrac=fract(bandF);\n\n float strength=0.0;\n for(int i=0;i<${NUM_BANDS};i++){\n if(i==bandIdx) strength=bands[i];\n }\n\n float neighborStr=0.0;\n int neighborIdx=bandFrac>.5?bandIdx+1:bandIdx-1;\n for(int i=0;i<${NUM_BANDS};i++){\n if(i==neighborIdx) neighborStr=bands[i];\n }\n float edgeBlend=abs(bandFrac-.5)*2.0;\n edgeBlend*=edgeBlend;\n strength=mix(strength,neighborStr,edgeBlend*.3);\n\n float speed=length(vel);\n float dirBlend=smoothstep(0.0,0.02,speed);\n vec2 dir=speed>.0001?vel/speed:vec2(0);\n dir*=dirBlend;\n\n float rowSeed=h(vec2(scanY,floor(t*3.)+float(bandIdx)*7.));\n float rowVar=mix(.4,1.0,rowSeed);\n\n float ySmooth=vUv.y*6.0+t*0.7;\n float yNoise=mix(h(vec2(floor(ySmooth),13.)),h(vec2(floor(ySmooth)+1.0,13.)),smoothstep(0.0,1.0,fract(ySmooth)));\n float colVar=mix(.4,1.0,yNoise);\n\n float tearShiftX=dir.x*strength*rowVar*0.15;\n float tearShiftY=dir.y*strength*colVar*0.10;\n\n float bandSeed=h(vec2(float(bandIdx),42.));\n tearShiftX+=strength*(.5-bandSeed)*0.05;\n\n float yJitter=mix(h(vec2(floor(ySmooth),73.)),h(vec2(floor(ySmooth)+1.0,73.)),smoothstep(0.0,1.0,fract(ySmooth)));\n tearShiftY+=strength*(.5-yJitter)*0.035;\n\n uv.x+=tearShiftX;\n uv.y+=tearShiftY;\n\n float sortGate=step(.5,strength)*step(.4,rowSeed);\n uv.x+=dir.x*sortGate*strength*0.03;\n uv.y+=dir.y*sortGate*strength*0.02;\n\n float caX=abs(tearShiftX)*2.5+sortGate*strength*0.01;\n float caY=abs(tearShiftY)*2.5+sortGate*strength*0.01;\n float cr=texture2D(tex,vec2(uv.x+caX,uv.y+caY)).r;\n float cg=texture2D(tex,uv).g;\n float cb=texture2D(tex,vec2(uv.x-caX,uv.y-caY)).b;\n\n vec3 col=vec3(cr,cg,cb);\n\n col*=.97+.03*sin(vUv.y*r.y*3.14159);\n\n float bandEdge=smoothstep(.02,.0,min(bandFrac,1.0-bandFrac));\n col+=vec3(bandEdge*strength*.1);\n\n col=mix(col,col*tint,tintStrength);\n\n gl_FragColor=vec4(col,1.0);\n}`\n\n/**\n * Choreographed motion patterns used when `autoPlay` is set. Each pattern\n * returns a synthetic pointer position in [0,1] and a hover intensity in\n * [0,1] for the current time (seconds). They drive the shader without\n * requiring a real pointer, which is what lets us record the distortion\n * as a GIF / screenshot / poster.\n */\nconst AUTOPLAY_PATTERNS: Record<\n AutoPlayPattern,\n (t: number) => { hover: number; mx: number; my: number }\n> = {\n aggressive: t => {\n const cycle = 1.4\n const phase = (t % cycle) / cycle\n const stab = Math.exp(-((phase - 0.15) ** 2) * 260)\n const angle = Math.floor(t / cycle) * 1.37\n const mx = 0.5 + Math.cos(angle) * 0.42 * (stab + 0.15)\n const my = 0.5 + Math.sin(angle) * 0.38 * (stab + 0.15)\n\n return { hover: 0.55 + stab * 0.45, mx, my }\n },\n gentle: t => ({\n hover: 0.45 + Math.sin(t * 0.9) * 0.1,\n mx: 0.5 + Math.sin(t * 0.5) * 0.28,\n my: 0.5 + Math.cos(t * 0.37) * 0.22\n }),\n slash: t => {\n // Long breath -> sword slash -> recoil twitch, repeating.\n const cycle = 3.6\n const phase = (t % cycle) / cycle\n const slash = Math.exp(-((phase - 0.28) ** 2) * 180)\n const micro = Math.exp(-((phase - 0.7) ** 2) * 340)\n\n const driftX = 0.5 + Math.sin(t * 0.7) * 0.16\n const driftY = 0.55 + Math.cos(t * 0.5) * 0.14\n\n // Slash trajectory: bottom-left up into the giant's chest (top-right).\n const slashX = -0.15 + phase * 1.55\n const slashY = 0.95 - phase * 1.35\n\n const mx = driftX * (1 - slash) + slashX * slash\n const my = driftY * (1 - slash) + slashY * slash\n\n return { hover: 0.5 + slash * 0.5 + micro * 0.35, mx, my }\n }\n}\n\nexport function ImageDistortion({\n active = true,\n autoPlay,\n className,\n fallbackClassName,\n src,\n style,\n tint,\n tintStrength\n}: ImageDistortionProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const tier = useGpuTier()\n const [loaded, setLoaded] = useState(false)\n\n const activeRef = useRef(active)\n activeRef.current = active\n const tintStrengthRef = useRef(tintStrength)\n tintStrengthRef.current = tintStrength\n const autoPlayRef = useRef(autoPlay)\n autoPlayRef.current = autoPlay\n\n const state = useRef({\n bandTargets: new Float32Array(NUM_BANDS),\n bands: new Float32Array(NUM_BANDS),\n hoverTarget: 0,\n imgH: 1,\n imgW: 1,\n mx: 0.5,\n my: 0.5,\n prevMx: 0.5,\n prevMy: 0.5,\n vx: 0,\n vy: 0\n })\n\n useEffect(() => {\n if (tier === 0) {\n return\n }\n\n const c = canvasRef.current\n\n if (!c) {\n return\n }\n\n const gl = c.getContext('webgl')\n\n if (!gl) {\n return\n }\n\n const compile = (type: number, source: string) => {\n const s = gl.createShader(type)!\n gl.shaderSource(s, source)\n gl.compileShader(s)\n\n return s\n }\n\n const prog = gl.createProgram()!\n gl.attachShader(prog, compile(gl.VERTEX_SHADER, VERT))\n gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FRAG))\n gl.linkProgram(prog)\n gl.useProgram(prog)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW\n )\n\n const a = gl.getAttribLocation(prog, 'a')\n gl.enableVertexAttribArray(a)\n gl.vertexAttribPointer(a, 2, gl.FLOAT, false, 0, 0)\n\n const uT = gl.getUniformLocation(prog, 't')\n const uR = gl.getUniformLocation(prog, 'r')\n const uImgSize = gl.getUniformLocation(prog, 'imgSize')\n const uVel = gl.getUniformLocation(prog, 'vel')\n const uTex = gl.getUniformLocation(prog, 'tex')\n const uTint = gl.getUniformLocation(prog, 'tint')\n const uTintStrength = gl.getUniformLocation(prog, 'tintStrength')\n const uBands: (null | WebGLUniformLocation)[] = []\n\n for (let i = 0; i < NUM_BANDS; i++) {\n uBands.push(gl.getUniformLocation(prog, `bands[${i}]`))\n }\n\n const texture = gl.createTexture()!\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n gl.RGBA,\n 1,\n 1,\n 0,\n gl.RGBA,\n gl.UNSIGNED_BYTE,\n new Uint8Array([0, 0, 0, 255])\n )\n\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n img.onload = () => {\n state.current.imgW = img.naturalWidth\n state.current.imgH = img.naturalHeight\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n gl.RGBA,\n gl.RGBA,\n gl.UNSIGNED_BYTE,\n img\n )\n setLoaded(true)\n }\n\n img.src = src\n\n gl.activeTexture(gl.TEXTURE0)\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.uniform1i(uTex, 0)\n\n const resize = () => {\n const rect = c.getBoundingClientRect()\n const dpr = Math.min(devicePixelRatio, 2)\n c.width = rect.width * dpr\n c.height = rect.height * dpr\n gl.viewport(0, 0, c.width, c.height)\n }\n\n resize()\n const ro = new ResizeObserver(resize)\n ro.observe(c)\n\n const onMove = (e: PointerEvent) => {\n const rect = c.getBoundingClientRect()\n state.current.mx = (e.clientX - rect.left) / rect.width\n state.current.my = (e.clientY - rect.top) / rect.height\n }\n\n const onEnter = () => {\n state.current.hoverTarget = 1\n }\n\n const onLeave = () => {\n state.current.hoverTarget = 0\n }\n\n // When autoPlay drives the distortion we want the poster to look\n // alive regardless of whether a pointer is near the canvas, so we\n // skip the real pointer listeners entirely.\n if (!autoPlayRef.current) {\n c.addEventListener('pointermove', onMove)\n c.addEventListener('pointerenter', onEnter)\n c.addEventListener('pointerleave', onLeave)\n }\n\n const bandEaseRates = new Float32Array(NUM_BANDS)\n\n for (let i = 0; i < NUM_BANDS; i++) {\n bandEaseRates[i] = 0.02 + Math.random() * 0.06\n }\n\n const tintVec: readonly [number, number, number] = tint\n ? (() => {\n const [tr, tg, tb] = hexToRgb(tint)\n\n return [tr / 255, tg / 255, tb / 255] as const\n })()\n : ([1, 1, 1] as const)\n\n const t0 = performance.now()\n let raf = 0\n\n const loop = () => {\n raf = requestAnimationFrame(loop)\n const s = state.current\n\n const pattern = autoPlayRef.current\n ? AUTOPLAY_PATTERNS[autoPlayRef.current]\n : null\n\n if (pattern) {\n const driven = pattern((performance.now() - t0) / 1e3)\n s.mx = driven.mx\n s.my = driven.my\n s.hoverTarget = driven.hover\n }\n\n const dvx = s.mx - s.prevMx\n const dvy = s.my - s.prevMy\n s.vx += (dvx * 8 - s.vx) * 0.1\n s.vy += (dvy * 8 - s.vy) * 0.1\n s.prevMx = s.mx\n s.prevMy = s.my\n\n const speed = Math.sqrt(s.vx * s.vx + s.vy * s.vy)\n\n for (let i = 0; i < NUM_BANDS; i++) {\n const bandCenter = (i + 0.5) / NUM_BANDS\n const dist = Math.abs(s.my - bandCenter)\n const proximity = Math.max(0, 1 - dist / 0.3)\n const activation =\n s.hoverTarget * proximity * (0.4 + Math.min(speed, 1) * 0.6)\n s.bandTargets[i] = activation\n }\n\n for (let i = 0; i < NUM_BANDS; i++) {\n const rate = bandEaseRates[i]!\n const current = s.bands[i] ?? 0\n const target = s.bandTargets[i] ?? 0\n s.bands[i] = current + (target - current) * rate\n\n if (s.bands[i]! < 0.001) {\n s.bands[i] = 0\n }\n }\n\n gl.uniform1f(uT, (performance.now() - t0) / 1e3)\n gl.uniform2f(uR, c.width, c.height)\n gl.uniform2f(uImgSize, s.imgW, s.imgH)\n gl.uniform2f(uVel, s.vx, s.vy)\n gl.uniform3f(uTint, tintVec[0], tintVec[1], tintVec[2])\n\n const ts = tintStrengthRef.current\n const defaultStrength = tint ? 0.35 : 0\n const defaultInactive = tint ? 0.15 : 0\n gl.uniform1f(\n uTintStrength,\n activeRef.current\n ? (ts?.active ?? defaultStrength)\n : (ts?.inactive ?? defaultInactive)\n )\n\n for (let i = 0; i < NUM_BANDS; i++) {\n gl.uniform1f(uBands[i]!, s.bands[i]!)\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n raf = requestAnimationFrame(loop)\n\n return () => {\n cancelAnimationFrame(raf)\n ro.disconnect()\n c.removeEventListener('pointermove', onMove)\n c.removeEventListener('pointerenter', onEnter)\n c.removeEventListener('pointerleave', onLeave)\n gl.deleteTexture(texture)\n gl.deleteProgram(prog)\n setLoaded(false)\n }\n // autoPlay is intentionally omitted so toggling it at runtime doesn't\n // tear down the shader pipeline. The ref-driven loop reads the live\n // value each frame, so listener attach/detach is handled once on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [src, tier, tint])\n\n if (tier === 0) {\n return (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n alt=\"\"\n className={cn(\n 'absolute inset-0 h-full w-full object-cover',\n fallbackClassName ?? className\n )}\n src={src}\n style={{ mixBlendMode: 'overlay', ...style }}\n />\n )\n }\n\n return (\n <canvas\n className={cn(\n 'absolute inset-0 h-full w-full transition-opacity duration-500',\n className\n )}\n ref={canvasRef}\n style={{\n mixBlendMode: 'overlay',\n opacity: loaded ? 1 : 0,\n ...style\n }}\n />\n )\n}\n\nexport type AutoPlayPattern = 'aggressive' | 'gentle' | 'slash'\n\ninterface ImageDistortionProps {\n active?: boolean\n /**\n * Drive the distortion with a choreographed motion pattern instead of\n * waiting for a real pointer. Useful for posters, social clips, and any\n * context where the image needs to feel alive on its own.\n */\n autoPlay?: AutoPlayPattern\n className?: string\n fallbackClassName?: string\n src: string\n style?: React.CSSProperties\n tint?: string\n tintStrength?: { active: number; inactive: number }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"image-distortion.js","sourceRoot":"","sources":["../../../src/ui/components/image-distortion.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useEffect, useRef, useState } from 'react'\n\nimport { useGpuTier } from '../../hooks/use-gpu-tier'\nimport { cn, hexToRgb } from '../../utils'\n\nconst NUM_BANDS = 12\n\nconst VERT = `attribute vec2 a;varying vec2 vUv;void main(){vUv=vec2(a.x*.5+.5,.5-a.y*.5);gl_Position=vec4(a,0,1);}`\n\nconst FRAG = `precision highp float;\nuniform float t;\nuniform vec2 r,imgSize,vel;\nuniform sampler2D tex;\nuniform float bands[${NUM_BANDS}];\nuniform vec3 tint;\nuniform float tintStrength;\nvarying vec2 vUv;\n\nfloat h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}\n\n// cover-style UV: crops the image to fill the canvas, centered\nvec2 coverUV(vec2 uv){\n float canvasAspect=r.x/r.y;\n float imgAspect=imgSize.x/imgSize.y;\n vec2 scale=canvasAspect>imgAspect\n ?vec2(1.0,imgAspect/canvasAspect)\n :vec2(canvasAspect/imgAspect,1.0);\n return(uv-0.5)*scale+0.5;\n}\n\nvoid main(){\n vec2 uv=coverUV(vUv);\n float scanY=floor(vUv.y*r.y);\n\n float bandF=vUv.y*${NUM_BANDS}.0;\n int bandIdx=int(floor(bandF));\n float bandFrac=fract(bandF);\n\n float strength=0.0;\n for(int i=0;i<${NUM_BANDS};i++){\n if(i==bandIdx) strength=bands[i];\n }\n\n float neighborStr=0.0;\n int neighborIdx=bandFrac>.5?bandIdx+1:bandIdx-1;\n for(int i=0;i<${NUM_BANDS};i++){\n if(i==neighborIdx) neighborStr=bands[i];\n }\n float edgeBlend=abs(bandFrac-.5)*2.0;\n edgeBlend*=edgeBlend;\n strength=mix(strength,neighborStr,edgeBlend*.3);\n\n float speed=length(vel);\n float dirBlend=smoothstep(0.0,0.02,speed);\n vec2 dir=speed>.0001?vel/speed:vec2(0);\n dir*=dirBlend;\n\n float rowSeed=h(vec2(scanY,floor(t*3.)+float(bandIdx)*7.));\n float rowVar=mix(.4,1.0,rowSeed);\n\n float ySmooth=vUv.y*6.0+t*0.7;\n float yNoise=mix(h(vec2(floor(ySmooth),13.)),h(vec2(floor(ySmooth)+1.0,13.)),smoothstep(0.0,1.0,fract(ySmooth)));\n float colVar=mix(.4,1.0,yNoise);\n\n float tearShiftX=dir.x*strength*rowVar*0.15;\n float tearShiftY=dir.y*strength*colVar*0.10;\n\n float bandSeed=h(vec2(float(bandIdx),42.));\n tearShiftX+=strength*(.5-bandSeed)*0.05;\n\n float yJitter=mix(h(vec2(floor(ySmooth),73.)),h(vec2(floor(ySmooth)+1.0,73.)),smoothstep(0.0,1.0,fract(ySmooth)));\n tearShiftY+=strength*(.5-yJitter)*0.035;\n\n uv.x+=tearShiftX;\n uv.y+=tearShiftY;\n\n float sortGate=step(.5,strength)*step(.4,rowSeed);\n uv.x+=dir.x*sortGate*strength*0.03;\n uv.y+=dir.y*sortGate*strength*0.02;\n\n float caX=abs(tearShiftX)*2.5+sortGate*strength*0.01;\n float caY=abs(tearShiftY)*2.5+sortGate*strength*0.01;\n float cr=texture2D(tex,vec2(uv.x+caX,uv.y+caY)).r;\n float cg=texture2D(tex,uv).g;\n float cb=texture2D(tex,vec2(uv.x-caX,uv.y-caY)).b;\n\n vec3 col=vec3(cr,cg,cb);\n\n col*=.97+.03*sin(vUv.y*r.y*3.14159);\n\n float bandEdge=smoothstep(.02,.0,min(bandFrac,1.0-bandFrac));\n col+=vec3(bandEdge*strength*.1);\n\n col=mix(col,col*tint,tintStrength);\n\n gl_FragColor=vec4(col,1.0);\n}`\n\n/**\n * Choreographed motion patterns used when `autoPlay` is set. Each pattern\n * returns a synthetic pointer position in [0,1] and a hover intensity in\n * [0,1] for the current time (seconds). They drive the shader without\n * requiring a real pointer, which is what lets us record the distortion\n * as a GIF / screenshot / poster.\n */\nconst AUTOPLAY_PATTERNS: Record<\n AutoPlayPattern,\n (t: number) => { hover: number; mx: number; my: number }\n> = {\n aggressive: t => {\n const cycle = 1.4\n const phase = (t % cycle) / cycle\n const stab = Math.exp(-((phase - 0.15) ** 2) * 260)\n const angle = Math.floor(t / cycle) * 1.37\n const mx = 0.5 + Math.cos(angle) * 0.42 * (stab + 0.15)\n const my = 0.5 + Math.sin(angle) * 0.38 * (stab + 0.15)\n\n return { hover: 0.55 + stab * 0.45, mx, my }\n },\n gentle: t => ({\n hover: 0.45 + Math.sin(t * 0.9) * 0.1,\n mx: 0.5 + Math.sin(t * 0.5) * 0.28,\n my: 0.5 + Math.cos(t * 0.37) * 0.22\n }),\n slash: t => {\n // Long breath -> sword slash -> recoil twitch, repeating.\n const cycle = 3.6\n const phase = (t % cycle) / cycle\n const slash = Math.exp(-((phase - 0.28) ** 2) * 180)\n const micro = Math.exp(-((phase - 0.7) ** 2) * 340)\n\n const driftX = 0.5 + Math.sin(t * 0.7) * 0.16\n const driftY = 0.55 + Math.cos(t * 0.5) * 0.14\n\n // Slash trajectory: bottom-left up into the giant's chest (top-right).\n const slashX = -0.15 + phase * 1.55\n const slashY = 0.95 - phase * 1.35\n\n const mx = driftX * (1 - slash) + slashX * slash\n const my = driftY * (1 - slash) + slashY * slash\n\n return { hover: 0.5 + slash * 0.5 + micro * 0.35, mx, my }\n }\n}\n\nexport function ImageDistortion({\n active = true,\n autoPlay,\n className,\n fallbackClassName,\n src,\n style,\n tint,\n tintStrength\n}: ImageDistortionProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const tier = useGpuTier()\n const [loaded, setLoaded] = useState(false)\n\n const activeRef = useRef(active)\n activeRef.current = active\n const tintStrengthRef = useRef(tintStrength)\n tintStrengthRef.current = tintStrength\n const autoPlayRef = useRef(autoPlay)\n autoPlayRef.current = autoPlay\n\n const state = useRef({\n bandTargets: new Float32Array(NUM_BANDS),\n bands: new Float32Array(NUM_BANDS),\n hoverTarget: 0,\n imgH: 1,\n imgW: 1,\n mx: 0.5,\n my: 0.5,\n prevMx: 0.5,\n prevMy: 0.5,\n vx: 0,\n vy: 0\n })\n\n useEffect(() => {\n if (tier === 0) {\n return\n }\n\n const c = canvasRef.current\n\n if (!c) {\n return\n }\n\n const gl = c.getContext('webgl')\n\n if (!gl) {\n return\n }\n\n const compile = (type: number, source: string) => {\n const s = gl.createShader(type)!\n gl.shaderSource(s, source)\n gl.compileShader(s)\n\n return s\n }\n\n const prog = gl.createProgram()!\n gl.attachShader(prog, compile(gl.VERTEX_SHADER, VERT))\n gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FRAG))\n gl.linkProgram(prog)\n gl.useProgram(prog)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n gl.STATIC_DRAW\n )\n\n const a = gl.getAttribLocation(prog, 'a')\n gl.enableVertexAttribArray(a)\n gl.vertexAttribPointer(a, 2, gl.FLOAT, false, 0, 0)\n\n const uT = gl.getUniformLocation(prog, 't')\n const uR = gl.getUniformLocation(prog, 'r')\n const uImgSize = gl.getUniformLocation(prog, 'imgSize')\n const uVel = gl.getUniformLocation(prog, 'vel')\n const uTex = gl.getUniformLocation(prog, 'tex')\n const uTint = gl.getUniformLocation(prog, 'tint')\n const uTintStrength = gl.getUniformLocation(prog, 'tintStrength')\n const uBands: (null | WebGLUniformLocation)[] = []\n\n for (let i = 0; i < NUM_BANDS; i++) {\n uBands.push(gl.getUniformLocation(prog, `bands[${i}]`))\n }\n\n const texture = gl.createTexture()!\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n gl.RGBA,\n 1,\n 1,\n 0,\n gl.RGBA,\n gl.UNSIGNED_BYTE,\n new Uint8Array([0, 0, 0, 255])\n )\n\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n img.onload = () => {\n state.current.imgW = img.naturalWidth\n state.current.imgH = img.naturalHeight\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n gl.RGBA,\n gl.RGBA,\n gl.UNSIGNED_BYTE,\n img\n )\n setLoaded(true)\n }\n\n img.src = src\n\n gl.activeTexture(gl.TEXTURE0)\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.uniform1i(uTex, 0)\n\n const resize = () => {\n const rect = c.getBoundingClientRect()\n const dpr = Math.min(devicePixelRatio, 2)\n c.width = rect.width * dpr\n c.height = rect.height * dpr\n gl.viewport(0, 0, c.width, c.height)\n }\n\n resize()\n const ro = new ResizeObserver(resize)\n ro.observe(c)\n\n const onMove = (e: PointerEvent) => {\n const rect = c.getBoundingClientRect()\n state.current.mx = (e.clientX - rect.left) / rect.width\n state.current.my = (e.clientY - rect.top) / rect.height\n }\n\n const onEnter = () => {\n state.current.hoverTarget = 1\n }\n\n const onLeave = () => {\n state.current.hoverTarget = 0\n }\n\n // When autoPlay drives the distortion we want the poster to look\n // alive regardless of whether a pointer is near the canvas, so we\n // skip the real pointer listeners entirely.\n if (!autoPlayRef.current) {\n c.addEventListener('pointermove', onMove)\n c.addEventListener('pointerenter', onEnter)\n c.addEventListener('pointerleave', onLeave)\n }\n\n const bandEaseRates = new Float32Array(NUM_BANDS)\n\n for (let i = 0; i < NUM_BANDS; i++) {\n bandEaseRates[i] = 0.02 + Math.random() * 0.06\n }\n\n const tintVec: readonly [number, number, number] = tint\n ? (() => {\n const [tr, tg, tb] = hexToRgb(tint)\n\n return [tr / 255, tg / 255, tb / 255] as const\n })()\n : ([1, 1, 1] as const)\n\n const t0 = performance.now()\n let raf = 0\n let visible = !document.hidden\n let inView = true\n\n const loop = () => {\n raf = requestAnimationFrame(loop)\n const s = state.current\n\n const pattern = autoPlayRef.current\n ? AUTOPLAY_PATTERNS[autoPlayRef.current]\n : null\n\n if (pattern) {\n const driven = pattern((performance.now() - t0) / 1e3)\n s.mx = driven.mx\n s.my = driven.my\n s.hoverTarget = driven.hover\n }\n\n const dvx = s.mx - s.prevMx\n const dvy = s.my - s.prevMy\n s.vx += (dvx * 8 - s.vx) * 0.1\n s.vy += (dvy * 8 - s.vy) * 0.1\n s.prevMx = s.mx\n s.prevMy = s.my\n\n const speed = Math.sqrt(s.vx * s.vx + s.vy * s.vy)\n\n for (let i = 0; i < NUM_BANDS; i++) {\n const bandCenter = (i + 0.5) / NUM_BANDS\n const dist = Math.abs(s.my - bandCenter)\n const proximity = Math.max(0, 1 - dist / 0.3)\n const activation =\n s.hoverTarget * proximity * (0.4 + Math.min(speed, 1) * 0.6)\n s.bandTargets[i] = activation\n }\n\n for (let i = 0; i < NUM_BANDS; i++) {\n const rate = bandEaseRates[i]!\n const current = s.bands[i] ?? 0\n const target = s.bandTargets[i] ?? 0\n s.bands[i] = current + (target - current) * rate\n\n if (s.bands[i]! < 0.001) {\n s.bands[i] = 0\n }\n }\n\n gl.uniform1f(uT, (performance.now() - t0) / 1e3)\n gl.uniform2f(uR, c.width, c.height)\n gl.uniform2f(uImgSize, s.imgW, s.imgH)\n gl.uniform2f(uVel, s.vx, s.vy)\n gl.uniform3f(uTint, tintVec[0], tintVec[1], tintVec[2])\n\n const ts = tintStrengthRef.current\n const defaultStrength = tint ? 0.35 : 0\n const defaultInactive = tint ? 0.15 : 0\n gl.uniform1f(\n uTintStrength,\n activeRef.current\n ? (ts?.active ?? defaultStrength)\n : (ts?.inactive ?? defaultInactive)\n )\n\n for (let i = 0; i < NUM_BANDS; i++) {\n gl.uniform1f(uBands[i]!, s.bands[i]!)\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture)\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)\n }\n\n const start = () => {\n if (visible && inView && !raf) {\n raf = requestAnimationFrame(loop)\n }\n }\n\n const stop = () => {\n if (raf) {\n cancelAnimationFrame(raf)\n raf = 0\n }\n }\n\n const onVisibility = () => {\n visible = !document.hidden\n visible ? start() : stop()\n }\n\n const io = new IntersectionObserver(\n entries => {\n inView = entries.some(e => e.isIntersecting)\n inView ? start() : stop()\n },\n { threshold: 0 }\n )\n\n io.observe(c)\n document.addEventListener('visibilitychange', onVisibility)\n\n start()\n\n return () => {\n stop()\n io.disconnect()\n document.removeEventListener('visibilitychange', onVisibility)\n ro.disconnect()\n c.removeEventListener('pointermove', onMove)\n c.removeEventListener('pointerenter', onEnter)\n c.removeEventListener('pointerleave', onLeave)\n gl.deleteTexture(texture)\n gl.deleteProgram(prog)\n setLoaded(false)\n }\n // autoPlay is intentionally omitted so toggling it at runtime doesn't\n // tear down the shader pipeline. The ref-driven loop reads the live\n // value each frame, so listener attach/detach is handled once on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [src, tier, tint])\n\n if (tier === 0) {\n return (\n // eslint-disable-next-line @next/next/no-img-element\n <img\n alt=\"\"\n className={cn(\n 'absolute inset-0 h-full w-full object-cover',\n fallbackClassName ?? className\n )}\n src={src}\n style={{ mixBlendMode: 'overlay', ...style }}\n />\n )\n }\n\n return (\n <canvas\n className={cn(\n 'absolute inset-0 h-full w-full transition-opacity duration-500',\n className\n )}\n ref={canvasRef}\n style={{\n mixBlendMode: 'overlay',\n opacity: loaded ? 1 : 0,\n ...style\n }}\n />\n )\n}\n\nexport type AutoPlayPattern = 'aggressive' | 'gentle' | 'slash'\n\ninterface ImageDistortionProps {\n active?: boolean\n /**\n * Drive the distortion with a choreographed motion pattern instead of\n * waiting for a real pointer. Useful for posters, social clips, and any\n * context where the image needs to feel alive on its own.\n */\n autoPlay?: AutoPlayPattern\n className?: string\n fallbackClassName?: string\n src: string\n style?: React.CSSProperties\n tint?: string\n tintStrength?: { active: number; inactive: number }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glitch.d.ts","sourceRoot":"","sources":["../../../../src/ui/components/overlays/glitch.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"glitch.d.ts","sourceRoot":"","sources":["../../../../src/ui/components/overlays/glitch.tsx"],"names":[],"mappings":"AA4GA,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,WAAW,kDAiIvD;AAED,UAAU,WAAW;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAC5B"}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
|
-
import { useGpuTier } from '../../../hooks/use-gpu-tier';
|
|
5
|
+
import { $gpuTier, useGpuTier } from '../../../hooks/use-gpu-tier';
|
|
6
|
+
import { runRenderLoop } from '../../../hooks/use-render-loop';
|
|
6
7
|
import { useSmoothControls } from '../../../hooks/use-smooth-controls';
|
|
7
8
|
import { cn } from '../../../utils';
|
|
8
9
|
import { BLEND_MODES } from './blend-modes';
|
|
@@ -120,10 +121,20 @@ export function Glitch({ className, style }) {
|
|
|
120
121
|
if (!ref.current || !enabled) {
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
let renderer;
|
|
125
|
+
try {
|
|
126
|
+
renderer = new THREE.WebGLRenderer({
|
|
127
|
+
alpha: true,
|
|
128
|
+
canvas: ref.current
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// See note in noise.tsx — eager gpu-tier detection should keep us
|
|
133
|
+
// out of here, but if the driver fails the renderer constructor
|
|
134
|
+
// anyway, downgrade so other overlays stop trying too.
|
|
135
|
+
$gpuTier.set(0);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
127
138
|
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
128
139
|
const geo = new THREE.PlaneGeometry(2, 2);
|
|
129
140
|
const scene = new THREE.Scene();
|
|
@@ -144,36 +155,37 @@ export function Glitch({ className, style }) {
|
|
|
144
155
|
scene.add(new THREE.Mesh(geo, mat));
|
|
145
156
|
const resize = () => {
|
|
146
157
|
renderer.setSize(innerWidth, innerHeight);
|
|
147
|
-
|
|
158
|
+
// Cap DPR at 1.5 — at full retina (2x) the glitch shader is one
|
|
159
|
+
// of the heaviest fillrate consumers in the app, and the visual
|
|
160
|
+
// difference is tiny because it's a chromatic-noise effect.
|
|
161
|
+
renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5));
|
|
148
162
|
};
|
|
149
163
|
resize();
|
|
150
164
|
window.addEventListener('resize', resize);
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
let time = 0;
|
|
166
|
+
// gpu-tier 1 → ~10fps (legacy), gpu-tier 2 → ~30fps (was 60fps).
|
|
167
|
+
// Glitch is a background ambient effect; users won't notice 30 vs
|
|
168
|
+
// 60 but the GPU absolutely will.
|
|
169
|
+
const minIntervalMs = gpuTier === 1 ? 100 : 33;
|
|
170
|
+
const dispose = runRenderLoop({
|
|
171
|
+
el: ref.current,
|
|
172
|
+
minIntervalMs,
|
|
173
|
+
onFrame: deltaSeconds => {
|
|
174
|
+
time += deltaSeconds;
|
|
175
|
+
const v = cRef.current;
|
|
176
|
+
mat.uniforms.uTime.value = time;
|
|
177
|
+
mat.uniforms.uAlpha.value = v.alpha;
|
|
178
|
+
mat.uniforms.uChroma.value = v.chroma;
|
|
179
|
+
mat.uniforms.uIntensity.value = v.intensity;
|
|
180
|
+
mat.uniforms.uSparsity.value = v.sparsity;
|
|
181
|
+
mat.uniforms.uSpeed.value = v.speed;
|
|
182
|
+
mat.uniforms.uColor.value.set(v.color);
|
|
183
|
+
renderer.render(scene, camera);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
169
186
|
return () => {
|
|
170
187
|
window.removeEventListener('resize', resize);
|
|
171
|
-
|
|
172
|
-
clearTimeout(raf);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
cancelAnimationFrame(raf);
|
|
176
|
-
}
|
|
188
|
+
dispose();
|
|
177
189
|
mat.dispose();
|
|
178
190
|
geo.dispose();
|
|
179
191
|
renderer.dispose();
|