react-smokey-fluid-cursor 1.0.4 → 2.1.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/LICENSE +21 -0
- package/README.md +305 -115
- package/dist/index.d.mts +409 -0
- package/dist/index.d.ts +357 -3
- package/dist/index.js +195 -292
- package/dist/index.mjs +194 -292
- package/package.json +68 -20
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,127 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* This file defines the foundational TypeScript interfaces
|
|
5
|
+
* and type aliases used throughout the Smokey Fluid simulation engine.
|
|
6
|
+
*
|
|
7
|
+
* It abstracts WebGL object types (FBOs, textures, uniforms, etc.)
|
|
8
|
+
* to provide clear, type-safe contracts for interacting with GPU resources.
|
|
9
|
+
*
|
|
10
|
+
* Developers integrating or extending this package should primarily
|
|
11
|
+
* adjust configuration options defined in `ISmokeyFluidConfig`.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Generic alias for either WebGL1 or WebGL2 rendering contexts.
|
|
15
|
+
*
|
|
16
|
+
* - WebGLRenderingContext → legacy WebGL1 API
|
|
17
|
+
* - WebGL2RenderingContext → modern API with extended texture formats and MRT support
|
|
18
|
+
*
|
|
19
|
+
* This alias allows code to remain compatible across both WebGL versions.
|
|
20
|
+
*/
|
|
21
|
+
type GL = WebGLRenderingContext | WebGL2RenderingContext;
|
|
22
|
+
/**
|
|
23
|
+
* Describes a format pair used for texture or renderbuffer configuration.
|
|
24
|
+
*
|
|
25
|
+
* - `internalFormat`: how GPU stores the texture internally (e.g., RGBA16F)
|
|
26
|
+
* - `format`: how the data is laid out when uploading pixels (e.g., RGBA)
|
|
27
|
+
*
|
|
28
|
+
* Used for compatibility mapping between WebGL1 and WebGL2.
|
|
29
|
+
*/
|
|
30
|
+
interface GLFormat {
|
|
31
|
+
internalFormat: number;
|
|
32
|
+
format: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Represents detected WebGL extension and format capabilities
|
|
36
|
+
* for the current context. This allows the engine to dynamically
|
|
37
|
+
* choose the optimal texture formats and filtering modes.
|
|
38
|
+
*
|
|
39
|
+
* Usually populated at initialization after checking for extensions like:
|
|
40
|
+
* - OES_texture_half_float
|
|
41
|
+
* - OES_texture_float_linear
|
|
42
|
+
* - EXT_color_buffer_float
|
|
43
|
+
*/
|
|
44
|
+
interface GLExtInfo {
|
|
45
|
+
/** Supported RGBA floating-point texture format */
|
|
46
|
+
formatRGBA: GLFormat | null;
|
|
47
|
+
/** Supported RG (two-channel) texture format (WebGL2 only) */
|
|
48
|
+
formatRG: GLFormat | null;
|
|
49
|
+
/** Supported R (single-channel) texture format (WebGL2 only) */
|
|
50
|
+
formatR: GLFormat | null;
|
|
51
|
+
/** GL constant for HALF_FLOAT type (e.g., gl.HALF_FLOAT_OES) */
|
|
52
|
+
halfFloatTexType: number;
|
|
53
|
+
/** Whether linear filtering (smooth interpolation) for float textures is available */
|
|
54
|
+
supportLinearFiltering: boolean;
|
|
55
|
+
/** True if running on WebGL2 (vs WebGL1 + extensions) */
|
|
56
|
+
isWebGL2: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Framebuffer Object (FBO) descriptor — wraps a texture and its framebuffer target.
|
|
60
|
+
*
|
|
61
|
+
* Each FBO represents a GPU render target that can store color or data fields
|
|
62
|
+
* (such as velocity, pressure, or density in fluid simulation).
|
|
63
|
+
*/
|
|
64
|
+
interface FBO {
|
|
65
|
+
/** GPU texture attached to the framebuffer */
|
|
66
|
+
texture: WebGLTexture | null;
|
|
67
|
+
/** The framebuffer object itself */
|
|
68
|
+
fbo: WebGLFramebuffer | null;
|
|
69
|
+
/** Width of the texture in pixels */
|
|
70
|
+
width: number;
|
|
71
|
+
/** Height of the texture in pixels */
|
|
72
|
+
height: number;
|
|
73
|
+
/** Normalized texel size (1 / width) */
|
|
74
|
+
texelSizeX: number;
|
|
75
|
+
/** Normalized texel size (1 / height) */
|
|
76
|
+
texelSizeY: number;
|
|
77
|
+
/**
|
|
78
|
+
* Binds this FBO's texture to a given texture unit (slot).
|
|
79
|
+
* Returns the bound texture unit index for convenience.
|
|
80
|
+
*
|
|
81
|
+
* Example:
|
|
82
|
+
* ```ts
|
|
83
|
+
* const unit = velocityFBO.attach(0); // binds to TEXTURE0
|
|
84
|
+
* gl.uniform1i(uniforms.uVelocity, unit);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
attach(id: number): number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Represents a double-buffered framebuffer system.
|
|
91
|
+
*
|
|
92
|
+
* Used heavily in fluid simulations to "ping-pong" between two framebuffers:
|
|
93
|
+
* - One for reading previous state
|
|
94
|
+
* - One for writing the next frame
|
|
95
|
+
*
|
|
96
|
+
* After each simulation step, `swap()` is called to exchange them.
|
|
97
|
+
*/
|
|
98
|
+
interface DoubleFBO {
|
|
99
|
+
/** Buffer width in pixels */
|
|
100
|
+
width: number;
|
|
101
|
+
/** Buffer height in pixels */
|
|
102
|
+
height: number;
|
|
103
|
+
/** Normalized texel size (1 / width) */
|
|
104
|
+
texelSizeX: number;
|
|
105
|
+
/** Normalized texel size (1 / height) */
|
|
106
|
+
texelSizeY: number;
|
|
107
|
+
/** Framebuffer currently used for reading */
|
|
108
|
+
read: FBO;
|
|
109
|
+
/** Framebuffer currently used for writing */
|
|
110
|
+
write: FBO;
|
|
111
|
+
/** Swaps read/write buffers for next iteration */
|
|
112
|
+
swap(): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* ---------------------------------------------------------
|
|
116
|
+
* 🔧 Smokey Fluid Configuration
|
|
117
|
+
* ---------------------------------------------------------
|
|
118
|
+
*
|
|
119
|
+
* This interface defines **all configurable simulation parameters**
|
|
120
|
+
* that can be customized externally (e.g., by UI, app settings, or user code).
|
|
121
|
+
*
|
|
122
|
+
* Each property controls a different aspect of the fluid’s physics,
|
|
123
|
+
* rendering behavior, or visual appearance.
|
|
124
|
+
*/
|
|
3
125
|
interface ISmokeyFluidConfig {
|
|
4
126
|
/** Simulation grid resolution for velocity/pressure fields (lower = faster but coarser) */
|
|
5
127
|
simResolution: number;
|
|
@@ -46,10 +168,242 @@ interface ISmokeyFluidConfig {
|
|
|
46
168
|
* Default is "smokey-fluid-canvas".
|
|
47
169
|
*/
|
|
48
170
|
id: string;
|
|
171
|
+
/**
|
|
172
|
+
* An existing canvas to render into — an element, or a CSS selector.
|
|
173
|
+
*
|
|
174
|
+
* Takes precedence over `id` and `container`. Use this when the canvas is
|
|
175
|
+
* already part of your markup or managed by a framework.
|
|
176
|
+
*/
|
|
177
|
+
canvas?: HTMLCanvasElement | string | null;
|
|
178
|
+
/**
|
|
179
|
+
* Element (or CSS selector) to create the canvas inside, when no canvas
|
|
180
|
+
* matches `canvas`/`id` yet.
|
|
181
|
+
*
|
|
182
|
+
* Defaults to `document.body`, which with `position: "fixed"` gives the
|
|
183
|
+
* classic full-viewport effect. Point it at a section to scope the effect
|
|
184
|
+
* to just that area.
|
|
185
|
+
*/
|
|
186
|
+
container?: HTMLElement | string | null;
|
|
187
|
+
/**
|
|
188
|
+
* CSS position for the canvas. Use `"absolute"` to confine the effect to a
|
|
189
|
+
* positioned container; `"fixed"` covers the viewport. Default `"fixed"`.
|
|
190
|
+
*/
|
|
191
|
+
position: "fixed" | "absolute" | "static" | "relative";
|
|
192
|
+
/** Stacking order of the canvas. Default `-9999` (behind page content). */
|
|
193
|
+
zIndex: number;
|
|
194
|
+
/**
|
|
195
|
+
* Whether the canvas receives pointer events. Default `false`, so clicks
|
|
196
|
+
* pass straight through to your UI.
|
|
197
|
+
*/
|
|
198
|
+
pointerEvents: boolean;
|
|
199
|
+
/** Extra class name applied to the canvas element. */
|
|
200
|
+
className?: string;
|
|
201
|
+
/**
|
|
202
|
+
* Upper bound on the device pixel ratio used for the render buffers.
|
|
203
|
+
*
|
|
204
|
+
* Default `2`. Uncapped, a 3x phone renders nine times the pixels of a 1x
|
|
205
|
+
* display for a purely decorative effect, which drains battery and drops
|
|
206
|
+
* frame rate.
|
|
207
|
+
*/
|
|
208
|
+
maxDpr: number;
|
|
209
|
+
/**
|
|
210
|
+
* Pause the simulation while the page is hidden (background tab). Default
|
|
211
|
+
* `true` — an invisible animation should not burn CPU or battery.
|
|
212
|
+
*/
|
|
213
|
+
pauseOnHidden: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Honour the `prefers-reduced-motion` media query. When the visitor has
|
|
216
|
+
* asked for reduced motion the simulation starts paused. Default `true`.
|
|
217
|
+
*/
|
|
218
|
+
respectReducedMotion: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Colour palette for the fluid, as CSS hex strings (e.g. `["#ff4ecd"]`).
|
|
221
|
+
*
|
|
222
|
+
* When omitted, colours are generated randomly across the full hue range,
|
|
223
|
+
* which is the original behaviour.
|
|
224
|
+
*/
|
|
225
|
+
palette?: string[] | null;
|
|
226
|
+
/**
|
|
227
|
+
* Brightness multiplier applied to generated colours. Default `0.15`;
|
|
228
|
+
* raise it for a more saturated, higher-contrast trail.
|
|
229
|
+
*/
|
|
230
|
+
colorIntensity: number;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Controls returned by {@link initFluid}.
|
|
234
|
+
*/
|
|
235
|
+
interface FluidHandle {
|
|
236
|
+
/** Stop the render loop, detach listeners and release the GL context. */
|
|
237
|
+
dispose(): void;
|
|
238
|
+
/** Pause the simulation, leaving the canvas in place. */
|
|
239
|
+
pause(): void;
|
|
240
|
+
/** Resume after {@link FluidHandle.pause}. */
|
|
241
|
+
resume(): void;
|
|
242
|
+
/** Whether the simulation is currently paused. */
|
|
243
|
+
isPaused(): boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Update tunable options in place, without tearing the simulation down.
|
|
246
|
+
*
|
|
247
|
+
* Resolution options (`simResolution`, `dyeResolution`) reallocate the
|
|
248
|
+
* framebuffers; everything else applies on the next frame.
|
|
249
|
+
*/
|
|
250
|
+
setConfig(partial: Partial<ISmokeyFluidConfig>): void;
|
|
251
|
+
/**
|
|
252
|
+
* Inject a splash at a point, in CSS pixels relative to the canvas.
|
|
253
|
+
* Useful for driving the effect from something other than the pointer.
|
|
254
|
+
*/
|
|
255
|
+
splat(x: number, y: number, color?: {
|
|
256
|
+
r: number;
|
|
257
|
+
g: number;
|
|
258
|
+
b: number;
|
|
259
|
+
}): void;
|
|
260
|
+
/** The canvas being rendered into. */
|
|
261
|
+
readonly canvas: HTMLCanvasElement | null;
|
|
49
262
|
}
|
|
50
263
|
|
|
51
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Initializes and starts the fluid simulation
|
|
266
|
+
* @param incomingConfig - Partial configuration object to override default settings
|
|
267
|
+
*/
|
|
268
|
+
declare const initFluid: (incomingConfig?: Partial<ISmokeyFluidConfig>) => FluidHandle;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* A named, ready-made configuration.
|
|
272
|
+
*
|
|
273
|
+
* Presets only set appearance and physics — never mounting or placement — so
|
|
274
|
+
* they compose with whatever `canvas`, `container` or `zIndex` you pass.
|
|
275
|
+
*/
|
|
276
|
+
type Preset = Pick<Partial<ISmokeyFluidConfig>, "palette" | "colorIntensity" | "colorUpdateSpeed" | "curl" | "splatForce" | "splatRadius" | "densityDissipation" | "velocityDissipation" | "pressure" | "pressureIteration" | "shading">;
|
|
277
|
+
/**
|
|
278
|
+
* The colour side of a preset.
|
|
279
|
+
*
|
|
280
|
+
* `null` means "no palette": hues are generated across the full spectrum,
|
|
281
|
+
* which is the library's default behaviour.
|
|
282
|
+
*/
|
|
283
|
+
declare const palettes: {
|
|
284
|
+
readonly Spectrum: null;
|
|
285
|
+
readonly Sunset: readonly ["#ff4ecd", "#ff8a4e", "#ffd24e"];
|
|
286
|
+
readonly Ocean: readonly ["#4ea8ff", "#4effd2", "#7c4dff"];
|
|
287
|
+
readonly Mono: readonly ["#ffffff"];
|
|
288
|
+
readonly Aurora: readonly ["#3affa3", "#38d9ff", "#8f7bff"];
|
|
289
|
+
readonly Ember: readonly ["#ff5722", "#ff9100", "#ffc400"];
|
|
290
|
+
readonly Lagoon: readonly ["#00c2a8", "#00a3ff", "#0057d9"];
|
|
291
|
+
readonly Candy: readonly ["#ff8fd0", "#ffa9f0", "#c79bff"];
|
|
292
|
+
readonly Toxic: readonly ["#b6ff00", "#4dff88", "#00ffc8"];
|
|
293
|
+
readonly Royal: readonly ["#5b2bff", "#8f4dff", "#c44dff"];
|
|
294
|
+
readonly Sakura: readonly ["#ffc2dd", "#ff8fb1", "#ff6f91"];
|
|
295
|
+
readonly Mint: readonly ["#9cffd6", "#5ef2c0", "#2fd6a5"];
|
|
296
|
+
readonly Copper: readonly ["#ff9a5a", "#e2703a", "#b34700"];
|
|
297
|
+
readonly Ultraviolet: readonly ["#7b2cff", "#b429ff", "#ff29f0"];
|
|
298
|
+
readonly Ice: readonly ["#c9f0ff", "#8ad4ff", "#4fb3ff"];
|
|
299
|
+
readonly Magma: readonly ["#ff2d2d", "#ff6a00", "#ffb300"];
|
|
300
|
+
readonly Forest: readonly ["#2f9e44", "#69db7c", "#a9e34b"];
|
|
301
|
+
readonly Dusk: readonly ["#3b3b98", "#7158e2", "#cd84f1"];
|
|
302
|
+
readonly Cyber: readonly ["#00fff0", "#ff00e0", "#fffb00"];
|
|
303
|
+
readonly Pastel: readonly ["#ffd6e0", "#c7ceea", "#b5ead7"];
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* The motion side of a preset — how the fluid moves, independent of colour.
|
|
307
|
+
*/
|
|
308
|
+
declare const characters: {
|
|
309
|
+
readonly Calm: {
|
|
310
|
+
readonly curl: 3;
|
|
311
|
+
readonly splatForce: 4200;
|
|
312
|
+
readonly splatRadius: 0.45;
|
|
313
|
+
readonly densityDissipation: 4.6;
|
|
314
|
+
readonly velocityDissipation: 2.6;
|
|
315
|
+
readonly pressureIteration: 16;
|
|
316
|
+
readonly colorUpdateSpeed: 6;
|
|
317
|
+
};
|
|
318
|
+
readonly Flow: {
|
|
319
|
+
readonly curl: 10;
|
|
320
|
+
readonly splatForce: 6000;
|
|
321
|
+
readonly splatRadius: 0.5;
|
|
322
|
+
readonly densityDissipation: 3.5;
|
|
323
|
+
readonly velocityDissipation: 2;
|
|
324
|
+
readonly pressureIteration: 20;
|
|
325
|
+
readonly colorUpdateSpeed: 10;
|
|
326
|
+
};
|
|
327
|
+
readonly Swirl: {
|
|
328
|
+
readonly curl: 24;
|
|
329
|
+
readonly splatForce: 7200;
|
|
330
|
+
readonly splatRadius: 0.55;
|
|
331
|
+
readonly densityDissipation: 3;
|
|
332
|
+
readonly velocityDissipation: 1.6;
|
|
333
|
+
readonly pressureIteration: 24;
|
|
334
|
+
readonly colorUpdateSpeed: 12;
|
|
335
|
+
};
|
|
336
|
+
readonly Storm: {
|
|
337
|
+
readonly curl: 40;
|
|
338
|
+
readonly splatForce: 9500;
|
|
339
|
+
readonly splatRadius: 0.65;
|
|
340
|
+
readonly densityDissipation: 2.2;
|
|
341
|
+
readonly velocityDissipation: 1.2;
|
|
342
|
+
readonly pressureIteration: 28;
|
|
343
|
+
readonly colorUpdateSpeed: 16;
|
|
344
|
+
};
|
|
345
|
+
readonly Wisp: {
|
|
346
|
+
readonly curl: 6;
|
|
347
|
+
readonly splatForce: 3200;
|
|
348
|
+
readonly splatRadius: 0.32;
|
|
349
|
+
readonly densityDissipation: 6.5;
|
|
350
|
+
readonly velocityDissipation: 3.4;
|
|
351
|
+
readonly pressureIteration: 12;
|
|
352
|
+
readonly colorUpdateSpeed: 8;
|
|
353
|
+
};
|
|
354
|
+
};
|
|
355
|
+
type PaletteName = keyof typeof palettes;
|
|
356
|
+
type CharacterName = keyof typeof characters;
|
|
357
|
+
type PresetName = `${PaletteName} ${CharacterName}`;
|
|
358
|
+
/**
|
|
359
|
+
* 100 ready-made looks: every colour palette crossed with every motion
|
|
360
|
+
* character, named `"<Palette> <Character>"` — e.g. `"Ocean Swirl"`.
|
|
361
|
+
*
|
|
362
|
+
* ```ts
|
|
363
|
+
* import { initFluid, presets } from "smokey-fluid-cursor";
|
|
364
|
+
*
|
|
365
|
+
* initFluid(presets["Ocean Swirl"]);
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
declare const presets: Record<PresetName, Preset>;
|
|
369
|
+
/** Every preset name, in definition order. */
|
|
370
|
+
declare const presetNames: PresetName[];
|
|
371
|
+
/** The palette names presets are built from. */
|
|
372
|
+
declare const paletteNames: PaletteName[];
|
|
373
|
+
/** The motion characters presets are built from. */
|
|
374
|
+
declare const characterNames: CharacterName[];
|
|
375
|
+
/** Looks up a preset by name, returning `undefined` when it does not exist. */
|
|
376
|
+
declare const getPreset: (name: string) => Preset | undefined;
|
|
377
|
+
|
|
378
|
+
interface SmokeyFluidCursorProps {
|
|
379
|
+
/** Simulation options. See ISmokeyFluidConfig. */
|
|
52
380
|
config?: Partial<ISmokeyFluidConfig>;
|
|
53
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Render the effect inside this component's own wrapper element rather than
|
|
383
|
+
* over the whole viewport. Combine with `config.position: "absolute"` to
|
|
384
|
+
* scope the fluid to a section of the page.
|
|
385
|
+
*/
|
|
386
|
+
scoped?: boolean;
|
|
387
|
+
/** Class name for the wrapper element (only rendered when `scoped`). */
|
|
388
|
+
className?: string;
|
|
389
|
+
/** Inline styles for the wrapper element (only rendered when `scoped`). */
|
|
390
|
+
style?: React.CSSProperties;
|
|
391
|
+
/** Content to render inside the scoped wrapper. */
|
|
392
|
+
children?: React.ReactNode;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Runs the fluid simulation for the lifetime of the calling component.
|
|
396
|
+
*
|
|
397
|
+
* Returns a ref to the live {@link FluidHandle}, so callers can pause, resume
|
|
398
|
+
* or retune without remounting.
|
|
399
|
+
*/
|
|
400
|
+
declare function useSmokeyFluidCursor(config?: Partial<ISmokeyFluidConfig>, containerRef?: React.RefObject<HTMLElement | null>): React.RefObject<FluidHandle | null>;
|
|
401
|
+
/**
|
|
402
|
+
* Drop-in fluid cursor effect.
|
|
403
|
+
*
|
|
404
|
+
* By default it covers the viewport and sits behind your content. Pass
|
|
405
|
+
* `scoped` with `config.position: "absolute"` to confine it to one section.
|
|
406
|
+
*/
|
|
407
|
+
declare const SmokeyFluidCursor: React.ForwardRefExoticComponent<SmokeyFluidCursorProps & React.RefAttributes<FluidHandle>>;
|
|
54
408
|
|
|
55
|
-
export { SmokeyFluidCursor };
|
|
409
|
+
export { type CharacterName, type DoubleFBO, type FBO, type FluidHandle, type GL, type GLExtInfo, type ISmokeyFluidConfig, type PaletteName, type Preset, type PresetName, SmokeyFluidCursor, type SmokeyFluidCursorProps, characterNames, getPreset, initFluid, paletteNames, presetNames, presets, useSmokeyFluidCursor };
|