fancy-ui-svelte 0.7.0 → 0.8.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/README.md +8 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte +1132 -1111
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte.d.ts +3 -5
- package/dist/fancy-ui/fluid-cursor/FluidCursor.test.js +29 -0
- package/dist/fancy-ui/fluid-cursor/README.md +57 -6
- package/dist/fancy-ui/fluid-cursor/fluid-shared.d.ts +40 -0
- package/dist/fancy-ui/fluid-cursor/fluid-shared.js +125 -0
- package/dist/fancy-ui/fluid-cursor/fluid-shared.test.d.ts +1 -0
- package/dist/fancy-ui/fluid-cursor/fluid-shared.test.js +39 -0
- package/dist/fancy-ui/fluid-cursor/webgpu-engine.d.ts +29 -0
- package/dist/fancy-ui/fluid-cursor/webgpu-engine.js +847 -0
- package/dist/fancy-ui/frosted-glass/FrostedGlass.svelte +5 -3
- package/dist/fancy-ui/frosted-glass/README.md +4 -2
- package/dist/fancy-ui/index.d.ts +1 -0
- package/dist/fancy-ui/index.js +1 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.svelte +19 -2
- package/dist/fancy-ui/liquid-glass/LiquidGlass.svelte.d.ts +4 -0
- package/dist/fancy-ui/liquid-glass/LiquidGlass.test.js +14 -0
- package/dist/fancy-ui/liquid-glass/README.md +7 -1
- package/dist/fancy-ui/liquid-text/LiquidText.svelte +1151 -0
- package/dist/fancy-ui/liquid-text/LiquidText.svelte.d.ts +37 -0
- package/dist/fancy-ui/liquid-text/LiquidText.test.d.ts +1 -0
- package/dist/fancy-ui/liquid-text/LiquidText.test.js +219 -0
- package/dist/fancy-ui/liquid-text/README.md +139 -0
- package/dist/fancy-ui/liquid-text/index.d.ts +2 -0
- package/dist/fancy-ui/liquid-text/index.js +1 -0
- package/dist/fancy-ui/registry.js +124 -0
- package/package.json +4 -3
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
r: number;
|
|
3
|
-
g: number;
|
|
4
|
-
b: number;
|
|
5
|
-
}
|
|
1
|
+
import { type ColorRGB } from "./fluid-shared.js";
|
|
6
2
|
interface Props {
|
|
7
3
|
simResolution?: number;
|
|
8
4
|
dyeResolution?: number;
|
|
@@ -29,6 +25,8 @@ interface Props {
|
|
|
29
25
|
splatOnMount?: boolean;
|
|
30
26
|
allowMultiple?: boolean;
|
|
31
27
|
contained?: boolean;
|
|
28
|
+
hdr?: boolean;
|
|
29
|
+
hdrBoost?: number;
|
|
32
30
|
}
|
|
33
31
|
declare const FluidCursor: import("svelte").Component<Props, {}, "">;
|
|
34
32
|
type FluidCursor = ReturnType<typeof FluidCursor>;
|
|
@@ -66,6 +66,35 @@ describe("FluidCursor", () => {
|
|
|
66
66
|
vi.unstubAllGlobals();
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
|
+
describe("hdr props", () => {
|
|
70
|
+
it("renders without error with hdr={true} when WebGPU is unavailable", () => {
|
|
71
|
+
const { container } = render(FluidCursor, { props: { hdr: true } });
|
|
72
|
+
expect(container.querySelector("canvas")).toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
it("renders without error when hdrBoost is out of [1,4] range (clamped internally)", () => {
|
|
75
|
+
const { container } = render(FluidCursor, { props: { hdr: true, hdrBoost: 100 } });
|
|
76
|
+
expect(container.querySelector("canvas")).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
it("falls back cleanly when WebGPU is present but no adapter is returned", async () => {
|
|
79
|
+
const requestAdapter = vi.fn(async () => null);
|
|
80
|
+
// Define `gpu` on the real navigator instead of replacing it, so the
|
|
81
|
+
// Navigator prototype (userAgent, etc.) stays intact.
|
|
82
|
+
Object.defineProperty(navigator, "gpu", {
|
|
83
|
+
value: { requestAdapter },
|
|
84
|
+
configurable: true,
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const { container } = render(FluidCursor, { props: { hdr: true } });
|
|
88
|
+
// Let the async WebGPU attempt resolve and fall back.
|
|
89
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
90
|
+
expect(requestAdapter).toHaveBeenCalled();
|
|
91
|
+
expect(container.querySelector("canvas")).toBeInTheDocument();
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
delete navigator.gpu;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
69
98
|
describe("color props", () => {
|
|
70
99
|
it("renders without error when fluidColor is provided", () => {
|
|
71
100
|
const { container } = render(FluidCursor, { props: { fluidColor: "#00ffcc" } });
|
|
@@ -5,6 +5,7 @@ A WebGL-based fluid simulation that follows the cursor, creating beautiful flowi
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- WebGL2 fluid simulation with WebGL1 fallback
|
|
8
|
+
- Optional HDR mode rendered by a WebGPU engine (wide gamut + extended tone mapping)
|
|
8
9
|
- Mouse and touch support
|
|
9
10
|
- Configurable simulation parameters
|
|
10
11
|
- Transparent background support
|
|
@@ -23,6 +24,7 @@ A WebGL-based fluid simulation that follows the cursor, creating beautiful flowi
|
|
|
23
24
|
### WebGL Implementation
|
|
24
25
|
|
|
25
26
|
The component implements a full Navier-Stokes fluid simulation with:
|
|
27
|
+
|
|
26
28
|
- Curl/vorticity computation
|
|
27
29
|
- Pressure solving with iterative Jacobi method
|
|
28
30
|
- Advection for both velocity and dye
|
|
@@ -30,12 +32,12 @@ The component implements a full Navier-Stokes fluid simulation with:
|
|
|
30
32
|
|
|
31
33
|
## Color Props
|
|
32
34
|
|
|
33
|
-
| Prop
|
|
34
|
-
|
|
35
|
-
| `fluidColor`
|
|
36
|
-
| `fluidColors`
|
|
37
|
-
| `colorIntensity` | `number`
|
|
38
|
-
| `backColor`
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
| ---------------- | ------------------------------- | ------------------------ | ---------------------------------------------------- |
|
|
37
|
+
| `fluidColor` | `string` (hex) | — | Fixed fluid color. Disables random cycling. |
|
|
38
|
+
| `fluidColors` | `string[]` (hex) | — | Palette to cycle through on each splat. |
|
|
39
|
+
| `colorIntensity` | `number` | `0.15` | Intensity multiplier applied to fluid colors (0–1). |
|
|
40
|
+
| `backColor` | `{ r, g, b }` or `string` (hex) | `{ r: 0.5, g: 0, b: 0 }` | Background color — accepts RGB object or hex string. |
|
|
39
41
|
|
|
40
42
|
**Priority**: `fluidColor` > `fluidColors` > random HSV
|
|
41
43
|
|
|
@@ -52,8 +54,57 @@ The component implements a full Navier-Stokes fluid simulation with:
|
|
|
52
54
|
<FluidCursor backColor="#1a1a2e" />
|
|
53
55
|
```
|
|
54
56
|
|
|
57
|
+
### Contained-mode splat scaling
|
|
58
|
+
|
|
59
|
+
`splatRadius` is defined as a fraction of the canvas, which would make the
|
|
60
|
+
effect look tiny inside small containers (e.g. docs demos). In `contained`
|
|
61
|
+
mode both engines grow the aspect-corrected radius with the
|
|
62
|
+
viewport/container height ratio — sub-linearly in apparent size (×√k), so a
|
|
63
|
+
small demo reads bigger without mimicking fullscreen 1:1 — capped at ~30% of
|
|
64
|
+
the container height (`scaleRadiusForContainer` in `fluid-shared.ts`).
|
|
65
|
+
Fullscreen and viewport-sized containers are unaffected.
|
|
66
|
+
|
|
55
67
|
### Event Handling
|
|
56
68
|
|
|
57
69
|
- Window-level mouse/touch events for full-screen tracking
|
|
58
70
|
- First-move handlers to start animation only when user interacts
|
|
59
71
|
- Proper cleanup of all event listeners on component unmount
|
|
72
|
+
|
|
73
|
+
## HDR Mode
|
|
74
|
+
|
|
75
|
+
```svelte
|
|
76
|
+
<FluidCursor hdr hdrBoost={2} fluidColors={["#a142ff", "#42cfff"]} />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
With `hdr` enabled the component tries a WebGPU engine (`webgpu-engine.ts`, a
|
|
80
|
+
WGSL port of the same simulation) that renders into an `rgba16float` canvas
|
|
81
|
+
configured with `colorSpace: "display-p3"` and `toneMapping: { mode: "extended" }`.
|
|
82
|
+
On an HDR display, dye values pushed above `1.0` by `hdrBoost` render brighter
|
|
83
|
+
than SDR white; on a wide-gamut (P3) display colors are noticeably more
|
|
84
|
+
saturated even without HDR headroom.
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
| ---------- | --------- | ------- | ------------------------------------------------------------------------------------ |
|
|
88
|
+
| `hdr` | `boolean` | `false` | Opt into the WebGPU HDR engine with automatic fallback. |
|
|
89
|
+
| `hdrBoost` | `number` | `1.5` | Display exposure multiplier, clamped to `[1, 4]`. Only applied by the WebGPU engine. |
|
|
90
|
+
|
|
91
|
+
### Support cascade
|
|
92
|
+
|
|
93
|
+
The component degrades silently (the level is logged to the console in dev):
|
|
94
|
+
|
|
95
|
+
1. **`webgpu-hdr`** — WebGPU available: float16 backbuffer, P3, extended tone
|
|
96
|
+
mapping (Chrome/Edge 129+, Safari 26+; browsers that ignore `toneMapping`
|
|
97
|
+
still get the float16 + P3 rendering, clamped to SDR).
|
|
98
|
+
2. **`webgl-p3`** — no WebGPU: the regular WebGL engine with
|
|
99
|
+
`drawingBufferColorSpace = "display-p3"` (Chrome/Edge 104+, Safari 16.4+,
|
|
100
|
+
Firefox 132+). Wide gamut only, no extra brightness; `hdrBoost` is ignored.
|
|
101
|
+
3. **`sdr`** — everything else: the unchanged default rendering.
|
|
102
|
+
|
|
103
|
+
Notes:
|
|
104
|
+
|
|
105
|
+
- Colors are intentionally reinterpreted in P3 space rather than converted
|
|
106
|
+
from sRGB — the resulting saturation push is the point of the effect.
|
|
107
|
+
- The glow requires an HDR-capable display (`matchMedia("(dynamic-range: high)")`).
|
|
108
|
+
On SDR displays HDR mode is still safe, just clamped.
|
|
109
|
+
- With `hdr={false}` (the default) the WebGPU code path is never taken and
|
|
110
|
+
rendering is identical to previous versions.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface ColorRGB {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Pointer {
|
|
7
|
+
id: number;
|
|
8
|
+
texcoordX: number;
|
|
9
|
+
texcoordY: number;
|
|
10
|
+
prevTexcoordX: number;
|
|
11
|
+
prevTexcoordY: number;
|
|
12
|
+
deltaX: number;
|
|
13
|
+
deltaY: number;
|
|
14
|
+
down: boolean;
|
|
15
|
+
moved: boolean;
|
|
16
|
+
color: ColorRGB;
|
|
17
|
+
}
|
|
18
|
+
export declare function pointerPrototype(): Pointer;
|
|
19
|
+
export declare function hexToRgb(hex: string): ColorRGB;
|
|
20
|
+
export declare function HSVtoRGB(h: number, s: number, v: number): ColorRGB;
|
|
21
|
+
export declare function wrap(value: number, min: number, max: number): number;
|
|
22
|
+
/** Sim/dye grid size for a given base resolution and drawing buffer dimensions. */
|
|
23
|
+
export declare function getSimResolution(resolution: number, bufferWidth: number, bufferHeight: number): {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
};
|
|
27
|
+
export declare function correctRadius(radius: number, width: number, height: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* In contained mode the splat radius is a fraction of the canvas, so the same
|
|
30
|
+
* settings that look right fullscreen produce a tiny effect inside a small
|
|
31
|
+
* demo container. Grow the radius with the viewport/container ratio —
|
|
32
|
+
* sub-linearly in apparent size (the gaussian's characteristic length is
|
|
33
|
+
* sqrt(radius), so `radius * k` scales the length by sqrt(k)): a small demo
|
|
34
|
+
* reads bigger without mimicking fullscreen 1:1. Must be applied to the
|
|
35
|
+
* final, aspect-corrected radius; the 0.09 cap keeps the characteristic
|
|
36
|
+
* length under ~30% of the container height.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scaleRadiusForContainer(radius: number, containerHeight: number, viewportHeight: number): number;
|
|
39
|
+
export declare function correctDeltaX(delta: number, width: number, height: number): number;
|
|
40
|
+
export declare function correctDeltaY(delta: number, width: number, height: number): number;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Pure helpers shared by the WebGL and WebGPU fluid engines.
|
|
2
|
+
export function pointerPrototype() {
|
|
3
|
+
return {
|
|
4
|
+
id: -1,
|
|
5
|
+
texcoordX: 0,
|
|
6
|
+
texcoordY: 0,
|
|
7
|
+
prevTexcoordX: 0,
|
|
8
|
+
prevTexcoordY: 0,
|
|
9
|
+
deltaX: 0,
|
|
10
|
+
deltaY: 0,
|
|
11
|
+
down: false,
|
|
12
|
+
moved: false,
|
|
13
|
+
color: { r: 0, g: 0, b: 0 },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function hexToRgb(hex) {
|
|
17
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
18
|
+
if (!result) {
|
|
19
|
+
console.warn(`[FluidCursor] Invalid hex color: "${hex}". Expected format: "#rrggbb". Falling back to white.`);
|
|
20
|
+
return { r: 1, g: 1, b: 1 };
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
r: parseInt(result[1], 16) / 255,
|
|
24
|
+
g: parseInt(result[2], 16) / 255,
|
|
25
|
+
b: parseInt(result[3], 16) / 255,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function HSVtoRGB(h, s, v) {
|
|
29
|
+
let r = 0;
|
|
30
|
+
let g = 0;
|
|
31
|
+
let b = 0;
|
|
32
|
+
const i = Math.floor(h * 6);
|
|
33
|
+
const f = h * 6 - i;
|
|
34
|
+
const p = v * (1 - s);
|
|
35
|
+
const q = v * (1 - f * s);
|
|
36
|
+
const t = v * (1 - (1 - f) * s);
|
|
37
|
+
switch (i % 6) {
|
|
38
|
+
case 0:
|
|
39
|
+
r = v;
|
|
40
|
+
g = t;
|
|
41
|
+
b = p;
|
|
42
|
+
break;
|
|
43
|
+
case 1:
|
|
44
|
+
r = q;
|
|
45
|
+
g = v;
|
|
46
|
+
b = p;
|
|
47
|
+
break;
|
|
48
|
+
case 2:
|
|
49
|
+
r = p;
|
|
50
|
+
g = v;
|
|
51
|
+
b = t;
|
|
52
|
+
break;
|
|
53
|
+
case 3:
|
|
54
|
+
r = p;
|
|
55
|
+
g = q;
|
|
56
|
+
b = v;
|
|
57
|
+
break;
|
|
58
|
+
case 4:
|
|
59
|
+
r = t;
|
|
60
|
+
g = p;
|
|
61
|
+
b = v;
|
|
62
|
+
break;
|
|
63
|
+
case 5:
|
|
64
|
+
r = v;
|
|
65
|
+
g = p;
|
|
66
|
+
b = q;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
return { r, g, b };
|
|
70
|
+
}
|
|
71
|
+
export function wrap(value, min, max) {
|
|
72
|
+
const range = max - min;
|
|
73
|
+
if (range === 0)
|
|
74
|
+
return min;
|
|
75
|
+
return ((value - min) % range) + min;
|
|
76
|
+
}
|
|
77
|
+
/** Sim/dye grid size for a given base resolution and drawing buffer dimensions. */
|
|
78
|
+
export function getSimResolution(resolution, bufferWidth, bufferHeight) {
|
|
79
|
+
const aspectRatio = bufferWidth / bufferHeight;
|
|
80
|
+
const aspect = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;
|
|
81
|
+
const min = Math.round(resolution);
|
|
82
|
+
const max = Math.round(resolution * aspect);
|
|
83
|
+
if (bufferWidth > bufferHeight) {
|
|
84
|
+
return { width: max, height: min };
|
|
85
|
+
}
|
|
86
|
+
return { width: min, height: max };
|
|
87
|
+
}
|
|
88
|
+
export function correctRadius(radius, width, height) {
|
|
89
|
+
const aspectRatio = width / height;
|
|
90
|
+
if (aspectRatio > 1)
|
|
91
|
+
return radius * aspectRatio;
|
|
92
|
+
return radius;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* In contained mode the splat radius is a fraction of the canvas, so the same
|
|
96
|
+
* settings that look right fullscreen produce a tiny effect inside a small
|
|
97
|
+
* demo container. Grow the radius with the viewport/container ratio —
|
|
98
|
+
* sub-linearly in apparent size (the gaussian's characteristic length is
|
|
99
|
+
* sqrt(radius), so `radius * k` scales the length by sqrt(k)): a small demo
|
|
100
|
+
* reads bigger without mimicking fullscreen 1:1. Must be applied to the
|
|
101
|
+
* final, aspect-corrected radius; the 0.09 cap keeps the characteristic
|
|
102
|
+
* length under ~30% of the container height.
|
|
103
|
+
*/
|
|
104
|
+
export function scaleRadiusForContainer(radius, containerHeight, viewportHeight) {
|
|
105
|
+
if (containerHeight <= 0 || viewportHeight <= 0)
|
|
106
|
+
return radius;
|
|
107
|
+
const k = viewportHeight / containerHeight;
|
|
108
|
+
if (k <= 1)
|
|
109
|
+
return radius;
|
|
110
|
+
// The cap only limits the auto-scaling — a caller-provided radius that is
|
|
111
|
+
// already larger must never be shrunk by entering a small container.
|
|
112
|
+
return Math.max(radius, Math.min(radius * k, 0.09));
|
|
113
|
+
}
|
|
114
|
+
export function correctDeltaX(delta, width, height) {
|
|
115
|
+
const aspectRatio = width / height;
|
|
116
|
+
if (aspectRatio < 1)
|
|
117
|
+
return delta * aspectRatio;
|
|
118
|
+
return delta;
|
|
119
|
+
}
|
|
120
|
+
export function correctDeltaY(delta, width, height) {
|
|
121
|
+
const aspectRatio = width / height;
|
|
122
|
+
if (aspectRatio > 1)
|
|
123
|
+
return delta / aspectRatio;
|
|
124
|
+
return delta;
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { scaleRadiusForContainer, correctRadius, getSimResolution } from "./fluid-shared.js";
|
|
3
|
+
describe("scaleRadiusForContainer", () => {
|
|
4
|
+
it("keeps the radius unchanged at viewport size (fullscreen)", () => {
|
|
5
|
+
expect(scaleRadiusForContainer(0.002, 900, 900)).toBe(0.002);
|
|
6
|
+
});
|
|
7
|
+
it("keeps the radius unchanged when the container is larger than the viewport", () => {
|
|
8
|
+
expect(scaleRadiusForContainer(0.002, 1800, 900)).toBe(0.002);
|
|
9
|
+
});
|
|
10
|
+
it("grows the radius linearly with the viewport/container ratio (sqrt(k) in apparent size)", () => {
|
|
11
|
+
// Container 4x smaller than viewport: radius x4, characteristic
|
|
12
|
+
// length x2 in container fractions.
|
|
13
|
+
expect(scaleRadiusForContainer(0.002, 250, 1000)).toBeCloseTo(0.008, 10);
|
|
14
|
+
});
|
|
15
|
+
it("caps the radius so a splat stays under ~30% of a tiny container", () => {
|
|
16
|
+
expect(scaleRadiusForContainer(0.01, 50, 1000)).toBe(0.09);
|
|
17
|
+
});
|
|
18
|
+
it("never shrinks a caller-provided radius already above the cap", () => {
|
|
19
|
+
expect(scaleRadiusForContainer(0.2, 250, 1000)).toBe(0.2);
|
|
20
|
+
});
|
|
21
|
+
it("is defensive about degenerate dimensions", () => {
|
|
22
|
+
expect(scaleRadiusForContainer(0.002, 0, 1000)).toBe(0.002);
|
|
23
|
+
expect(scaleRadiusForContainer(0.002, 250, 0)).toBe(0.002);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe("correctRadius", () => {
|
|
27
|
+
it("stretches the radius by aspect ratio on landscape canvases", () => {
|
|
28
|
+
expect(correctRadius(0.01, 200, 100)).toBe(0.02);
|
|
29
|
+
});
|
|
30
|
+
it("leaves portrait canvases unchanged", () => {
|
|
31
|
+
expect(correctRadius(0.01, 100, 200)).toBe(0.01);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("getSimResolution", () => {
|
|
35
|
+
it("puts the larger grid dimension along the larger canvas axis", () => {
|
|
36
|
+
expect(getSimResolution(128, 1000, 500)).toEqual({ width: 256, height: 128 });
|
|
37
|
+
expect(getSimResolution(128, 500, 1000)).toEqual({ width: 128, height: 256 });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ColorRGB } from "./fluid-shared.js";
|
|
2
|
+
export interface WebGpuFluidOptions {
|
|
3
|
+
simResolution: number;
|
|
4
|
+
dyeResolution: number;
|
|
5
|
+
densityDissipation: number;
|
|
6
|
+
velocityDissipation: number;
|
|
7
|
+
pressure: number;
|
|
8
|
+
pressureIterations: number;
|
|
9
|
+
curl: number;
|
|
10
|
+
splatRadius: number;
|
|
11
|
+
splatForce: number;
|
|
12
|
+
shading: boolean;
|
|
13
|
+
colorUpdateSpeed: number;
|
|
14
|
+
/** Display exposure multiplier — values above 1 create the HDR glow. */
|
|
15
|
+
exposure: number;
|
|
16
|
+
contained: boolean;
|
|
17
|
+
interactive: boolean;
|
|
18
|
+
autoSplat: boolean;
|
|
19
|
+
autoSplatInterval: number;
|
|
20
|
+
pauseWhenHidden: boolean;
|
|
21
|
+
splatOnMount: boolean;
|
|
22
|
+
generateColor: () => ColorRGB;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Full WebGPU setup for FluidCursor: engine + pointer wiring + rAF loop +
|
|
26
|
+
* visibility pause + auto splats. Resolves with a cleanup function, or with
|
|
27
|
+
* `null` when WebGPU is unavailable (caller falls back to the WebGL path).
|
|
28
|
+
*/
|
|
29
|
+
export declare function startWebGpuFluid(canvas: HTMLCanvasElement, opts: WebGpuFluidOptions): Promise<(() => void) | null>;
|