@rippleflow/water-distortion 0.1.2 → 0.1.3
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 +173 -173
- package/dist/WaterDistortion.d.ts.map +1 -1
- package/dist/core/waterRenderer.d.ts +2 -1
- package/dist/core/waterRenderer.d.ts.map +1 -1
- package/dist/water-distortion.cjs +2 -2
- package/dist/water-distortion.cjs.map +1 -1
- package/dist/water-distortion.js +151 -148
- package/dist/water-distortion.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
# RippleFlow Water Distortion
|
|
2
|
-
|
|
3
|
-
A React + Vite + TypeScript package for cursor-driven water on portfolio-style interfaces. RippleFlow now uses a low-resolution heightfield simulation: pointer movement stamps local trough/ridge wakes, the wave equation propagates them outward, and a WebGL material turns the heightfield into refraction, specular highlights, crests, and experimental ripple-driven caustic light.
|
|
4
|
-
|
|
5
|
-
Foreground content stays sharp and interactive. Background content can be sampled directly by WebGL in `texture` / `canvas` mode for the strongest refraction, or rendered as live DOM in `dom` mode.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @rippleflow/water-distortion
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
React and React DOM are peer dependencies:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install react react-dom
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
```tsx
|
|
22
|
-
import { WaterDistortion } from "@rippleflow/water-distortion";
|
|
23
|
-
|
|
24
|
-
export function Hero() {
|
|
25
|
-
return (
|
|
26
|
-
<WaterDistortion
|
|
27
|
-
mode="texture"
|
|
28
|
-
wakeStrength={0.54}
|
|
29
|
-
interactionRadius={25}
|
|
30
|
-
troughStrength={0.92}
|
|
31
|
-
ridgeStrength={0.46}
|
|
32
|
-
ridgeOffset={10}
|
|
33
|
-
wakeLength={84}
|
|
34
|
-
velocityScale={1}
|
|
35
|
-
velocityClamp={1.35}
|
|
36
|
-
cursorSmoothing={0.2}
|
|
37
|
-
segmentSpacing={18}
|
|
38
|
-
damping={0.98}
|
|
39
|
-
waveSpeed={0.4}
|
|
40
|
-
normalScale={20}
|
|
41
|
-
refractionStrength={0.7}
|
|
42
|
-
specularIntensity={1}
|
|
43
|
-
crestIntensity={0.2}
|
|
44
|
-
causticIntensity={0}
|
|
45
|
-
simulationResolution={192}
|
|
46
|
-
texture="/work-collage.jpg"
|
|
47
|
-
underlay={
|
|
48
|
-
<img
|
|
49
|
-
src="/work-collage.jpg"
|
|
50
|
-
alt=""
|
|
51
|
-
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
|
52
|
-
/>
|
|
53
|
-
}
|
|
54
|
-
style={{ minHeight: 620 }}
|
|
55
|
-
>
|
|
56
|
-
<section>
|
|
57
|
-
<h1>Portfolio title stays crisp</h1>
|
|
58
|
-
<a href="/work">View work</a>
|
|
59
|
-
</section>
|
|
60
|
-
</WaterDistortion>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
For true shader refraction, provide a texture source:
|
|
66
|
-
|
|
67
|
-
```tsx
|
|
68
|
-
<WaterDistortion
|
|
69
|
-
mode="texture"
|
|
70
|
-
texture="/hero-background.jpg"
|
|
71
|
-
wakeStrength={0.8}
|
|
72
|
-
underlay={<img src="/hero-background.jpg" alt="" />}
|
|
73
|
-
>
|
|
74
|
-
<HeroCopy />
|
|
75
|
-
</WaterDistortion>
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
`WaterLayer` is exported as an alias of `WaterDistortion`.
|
|
79
|
-
|
|
80
|
-
## React API
|
|
81
|
-
|
|
82
|
-
| Prop | Type | Default | Notes |
|
|
83
|
-
| --- | --- | --- | --- |
|
|
84
|
-
| `underlay` | `ReactNode` | `undefined` | Live DOM/background content behind the water. Recommended in `dom` mode and useful as a fallback in texture modes. |
|
|
85
|
-
| `children` | `ReactNode` | `undefined` | Foreground content rendered above the water. |
|
|
86
|
-
| `foreground` | `ReactNode` | `undefined` | Explicit foreground prop; takes precedence over `children`. |
|
|
1
|
+
# RippleFlow Water Distortion
|
|
2
|
+
|
|
3
|
+
A React + Vite + TypeScript package for cursor-driven water on portfolio-style interfaces. RippleFlow now uses a low-resolution heightfield simulation: pointer movement stamps local trough/ridge wakes, the wave equation propagates them outward, and a WebGL material turns the heightfield into refraction, specular highlights, crests, and experimental ripple-driven caustic light.
|
|
4
|
+
|
|
5
|
+
Foreground content stays sharp and interactive. Background content can be sampled directly by WebGL in `texture` / `canvas` mode for the strongest refraction, or rendered as live DOM in `dom` mode.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @rippleflow/water-distortion
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
React and React DOM are peer dependencies:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react react-dom
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { WaterDistortion } from "@rippleflow/water-distortion";
|
|
23
|
+
|
|
24
|
+
export function Hero() {
|
|
25
|
+
return (
|
|
26
|
+
<WaterDistortion
|
|
27
|
+
mode="texture"
|
|
28
|
+
wakeStrength={0.54}
|
|
29
|
+
interactionRadius={25}
|
|
30
|
+
troughStrength={0.92}
|
|
31
|
+
ridgeStrength={0.46}
|
|
32
|
+
ridgeOffset={10}
|
|
33
|
+
wakeLength={84}
|
|
34
|
+
velocityScale={1}
|
|
35
|
+
velocityClamp={1.35}
|
|
36
|
+
cursorSmoothing={0.2}
|
|
37
|
+
segmentSpacing={18}
|
|
38
|
+
damping={0.98}
|
|
39
|
+
waveSpeed={0.4}
|
|
40
|
+
normalScale={20}
|
|
41
|
+
refractionStrength={0.7}
|
|
42
|
+
specularIntensity={1}
|
|
43
|
+
crestIntensity={0.2}
|
|
44
|
+
causticIntensity={0}
|
|
45
|
+
simulationResolution={192}
|
|
46
|
+
texture="/work-collage.jpg"
|
|
47
|
+
underlay={
|
|
48
|
+
<img
|
|
49
|
+
src="/work-collage.jpg"
|
|
50
|
+
alt=""
|
|
51
|
+
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
|
52
|
+
/>
|
|
53
|
+
}
|
|
54
|
+
style={{ minHeight: 620 }}
|
|
55
|
+
>
|
|
56
|
+
<section>
|
|
57
|
+
<h1>Portfolio title stays crisp</h1>
|
|
58
|
+
<a href="/work">View work</a>
|
|
59
|
+
</section>
|
|
60
|
+
</WaterDistortion>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For true shader refraction, provide a texture source:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<WaterDistortion
|
|
69
|
+
mode="texture"
|
|
70
|
+
texture="/hero-background.jpg"
|
|
71
|
+
wakeStrength={0.8}
|
|
72
|
+
underlay={<img src="/hero-background.jpg" alt="" />}
|
|
73
|
+
>
|
|
74
|
+
<HeroCopy />
|
|
75
|
+
</WaterDistortion>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`WaterLayer` is exported as an alias of `WaterDistortion`.
|
|
79
|
+
|
|
80
|
+
## React API
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Default | Notes |
|
|
83
|
+
| --- | --- | --- | --- |
|
|
84
|
+
| `underlay` | `ReactNode` | `undefined` | Live DOM/background content behind the water. Recommended in `dom` mode and useful as a fallback in texture modes. |
|
|
85
|
+
| `children` | `ReactNode` | `undefined` | Foreground content rendered above the water. |
|
|
86
|
+
| `foreground` | `ReactNode` | `undefined` | Explicit foreground prop; takes precedence over `children`. |
|
|
87
87
|
| `mode` | `"dom" \| "texture" \| "canvas"` | `"texture"` | Chooses live-DOM overlay mode or true WebGL background sampling. |
|
|
88
88
|
| `texture` | `TexImageSource \| string` | `undefined` | Image/canvas/video source sampled by WebGL in `texture` or `canvas` mode. URL strings are loaded with `crossOrigin="anonymous"`. |
|
|
89
89
|
| `interactionMode` | `"move" \| "click" \| "drag" \| "event"` | `"move"` | Chooses pointer-movement wakes, one ripple per pointer down, press ripples plus held-drag wakes, or programmatic ripples only. |
|
|
90
90
|
| `runInBackground` | `boolean` | `false` | Continues stepping while hidden or unfocused and catches up elapsed time when the browser throttles background callbacks. |
|
|
91
91
|
| `pauseKey` | `string` | `undefined` | Toggles simulation pausing when this [`KeyboardEvent.code`](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/code) is pressed, such as `Space` or `KeyP`. Keyboard input in interactive controls is ignored. |
|
|
92
92
|
| `wakeStrength` | `number` | `0.54` | Overall pointer wake strength. Slow drags stay subtle; fast movement is clamped before it reaches the shader. |
|
|
93
|
-
| `rippleStrength` | `number` | `undefined` | Compatibility alias for `wakeStrength`. |
|
|
94
|
-
| `strength` | `number` | `undefined` | Legacy alias mapped to `wakeStrength` as `strength / 48`. |
|
|
95
|
-
| `interactionRadius` | `number` | `25` | Width in CSS pixels of the moving virtual finger/body that parts the water. |
|
|
96
|
-
| `troughStrength` | `number` | `0.92` | Strength of the narrow negative trough along the pointer path. |
|
|
97
|
-
| `ridgeStrength` | `number` | `0.46` | Strength of the paired side ridges beside the trough. |
|
|
98
|
-
| `ridgeOffset` | `number` | `10` | Side-ridge offset from the path centerline in CSS pixels. |
|
|
99
|
-
| `wakeLength` | `number` | `84` | Base trailing wake length in CSS pixels. Fast movement scales it longer. |
|
|
100
|
-
| `velocityScale` | `number` | `1` | Multiplier applied before pointer speed is clamped. |
|
|
101
|
-
| `velocityClamp` | `number` | `1.35` | Pointer speed in CSS pixels per millisecond that maps to full wake strength. |
|
|
102
|
-
| `cursorSmoothing` | `number` | `0.2` | Low-pass smoothing for the virtual finger position and speed. |
|
|
103
|
-
| `segmentSpacing` | `number` | `18` | Maximum CSS-pixel spacing before long pointer moves are subdivided into connected wake segments. |
|
|
104
|
-
| `damping` | `number` | `0.98` | Per-step velocity damping. Higher values let waves travel longer. |
|
|
105
|
-
| `dissipation` | `number` | `undefined` | Legacy alias for `damping`. |
|
|
106
|
-
| `waveSpeed` | `number` | `0.4` | Stiffness of the damped wave equation. |
|
|
107
|
-
| `normalScale` | `number` | `20` | Height-gradient scale used to compute visible water normals. |
|
|
108
|
-
| `refractionStrength` | `number` | `0.7` | Amount of normal-driven texture displacement in texture/canvas modes. |
|
|
109
|
-
| `specularIntensity` | `number` | `1` | Strength of fixed-light specular highlights. |
|
|
110
|
-
| `crestIntensity` | `number` | `0.2` | Visibility of height-gradient crests and troughs. |
|
|
111
|
-
| `causticIntensity` | `number` | `0` | Experimental ripple-driven caustic light that fades when the simulated surface is calm. |
|
|
112
|
-
| `simulationResolution` | `number` | `192` | Longest side of the simulation texture. Clamped to `64...384`. |
|
|
113
|
-
| `idleTimeout` | `number` | `900` | Minimum active time after input before the renderer begins checking whether ripples have faded enough to sleep. |
|
|
114
|
-
| `className` | `string` | `undefined` | Applied to the container. |
|
|
115
|
-
| `style` | `CSSProperties` | `undefined` | Merged onto the container. The component enforces relative positioning, hidden overflow, and isolation. |
|
|
93
|
+
| `rippleStrength` | `number` | `undefined` | Compatibility alias for `wakeStrength`. |
|
|
94
|
+
| `strength` | `number` | `undefined` | Legacy alias mapped to `wakeStrength` as `strength / 48`. |
|
|
95
|
+
| `interactionRadius` | `number` | `25` | Width in CSS pixels of the moving virtual finger/body that parts the water. |
|
|
96
|
+
| `troughStrength` | `number` | `0.92` | Strength of the narrow negative trough along the pointer path. |
|
|
97
|
+
| `ridgeStrength` | `number` | `0.46` | Strength of the paired side ridges beside the trough. |
|
|
98
|
+
| `ridgeOffset` | `number` | `10` | Side-ridge offset from the path centerline in CSS pixels. |
|
|
99
|
+
| `wakeLength` | `number` | `84` | Base trailing wake length in CSS pixels. Fast movement scales it longer. |
|
|
100
|
+
| `velocityScale` | `number` | `1` | Multiplier applied before pointer speed is clamped. |
|
|
101
|
+
| `velocityClamp` | `number` | `1.35` | Pointer speed in CSS pixels per millisecond that maps to full wake strength. |
|
|
102
|
+
| `cursorSmoothing` | `number` | `0.2` | Low-pass smoothing for the virtual finger position and speed. |
|
|
103
|
+
| `segmentSpacing` | `number` | `18` | Maximum CSS-pixel spacing before long pointer moves are subdivided into connected wake segments. |
|
|
104
|
+
| `damping` | `number` | `0.98` | Per-step velocity damping. Higher values let waves travel longer. |
|
|
105
|
+
| `dissipation` | `number` | `undefined` | Legacy alias for `damping`. |
|
|
106
|
+
| `waveSpeed` | `number` | `0.4` | Stiffness of the damped wave equation. |
|
|
107
|
+
| `normalScale` | `number` | `20` | Height-gradient scale used to compute visible water normals. |
|
|
108
|
+
| `refractionStrength` | `number` | `0.7` | Amount of normal-driven texture displacement in texture/canvas modes. |
|
|
109
|
+
| `specularIntensity` | `number` | `1` | Strength of fixed-light specular highlights. |
|
|
110
|
+
| `crestIntensity` | `number` | `0.2` | Visibility of height-gradient crests and troughs. |
|
|
111
|
+
| `causticIntensity` | `number` | `0` | Experimental ripple-driven caustic light that fades when the simulated surface is calm. |
|
|
112
|
+
| `simulationResolution` | `number` | `192` | Longest side of the simulation texture. Clamped to `64...384`. |
|
|
113
|
+
| `idleTimeout` | `number` | `900` | Minimum active time after input before the renderer begins checking whether ripples have faded enough to sleep. |
|
|
114
|
+
| `className` | `string` | `undefined` | Applied to the container. |
|
|
115
|
+
| `style` | `CSSProperties` | `undefined` | Merged onto the container. The component enforces relative positioning, hidden overflow, and isolation. |
|
|
116
116
|
| `reducedMotion` | `"respect" \| "disable" \| "ignore"` | `"respect"` | `respect` disables wave input when `prefers-reduced-motion: reduce` matches. |
|
|
117
117
|
|
|
118
118
|
Programmatic ripples use normalized container coordinates through the component ref:
|
|
@@ -136,67 +136,67 @@ function EventDrivenWater() {
|
|
|
136
136
|
~~~
|
|
137
137
|
|
|
138
138
|
The older `blur`, `maxTrailPoints`, `maxStamps`, and `trailSpacing` props remain accepted for source compatibility, but the heightfield renderer does not use the old trail-stamp engine.
|
|
139
|
-
|
|
140
|
-
## Modes
|
|
141
|
-
|
|
142
|
-
### `dom`
|
|
143
|
-
|
|
144
|
-
Use `dom` when the background is arbitrary live React/DOM content. The DOM underlay is rendered normally, foreground content stays sharp, and the WebGL canvas draws transparent water highlights derived from the heightfield.
|
|
145
|
-
|
|
146
|
-
Browsers do not provide an efficient, general API for sampling arbitrary live DOM into WebGL every frame. RippleFlow therefore does not pretend to perform perfect DOM refraction. The visible water material comes from heightfield normals, specular, crest, and caustic lighting, so ripples remain readable even when the DOM itself is not displaced.
|
|
147
|
-
|
|
148
|
-
### `texture` / `canvas`
|
|
149
|
-
|
|
150
|
-
Use `texture` or `canvas` when you can provide an image, video, canvas, or URL that WebGL can sample. This path gives the best visual quality because the shader offsets the sampled background with `normal.xy` from the water heightfield.
|
|
151
|
-
|
|
152
|
-
If no `texture` is supplied in these modes, RippleFlow renders a procedural gradient background so the playground and demos still show true refraction.
|
|
153
|
-
|
|
154
|
-
## How It Works
|
|
155
|
-
|
|
156
|
-
- A WebGL renderer maintains two ping-pong floating-point simulation textures.
|
|
157
|
-
- Each texel stores height and velocity.
|
|
158
|
-
- Every fixed step samples neighboring heights, accelerates velocity toward the neighbor average, applies damping, and integrates height.
|
|
159
|
-
- Pointer input uses `pointermove` plus `getCoalescedEvents()` where available, then low-pass filters samples into a virtual cursor.
|
|
160
|
-
- Pointer movement is stamped as continuous SDF/capsule wake segments, not repeated timed circles.
|
|
161
|
-
- Each segment uses a directional profile: center trough, paired side ridges, a trailing wake mask, and subtle behind-only wavelets.
|
|
162
|
-
- The material pass computes normals from height gradients, then combines refraction, specular highlights, crest/trough visibility, and projected caustic light that fades with ripple activity.
|
|
163
|
-
- The animation loop runs while input is active, then sleeps after ripples measure as visually quiet.
|
|
164
|
-
|
|
165
|
-
## Performance Notes
|
|
166
|
-
|
|
167
|
-
- The simulation resolution defaults to `192`, while the visible canvas still matches the container size.
|
|
168
|
-
- Keep `simulationResolution` around `128...256` for most portfolio hero sections.
|
|
169
|
-
- The renderer uses refs and an imperative animation loop; React state is not updated per frame.
|
|
170
|
-
- `ResizeObserver` keeps the canvas responsive.
|
|
171
|
-
- `IntersectionObserver` and `document.visibilityState` pause offscreen or hidden work when available.
|
|
172
|
-
- `toDataURL()` is not used in the primary render path.
|
|
173
|
-
- Dynamic canvas/video texture sources may be uploaded each rendered frame in `canvas`/`texture` mode.
|
|
174
|
-
- `prefers-reduced-motion` is respected by default.
|
|
175
|
-
|
|
176
|
-
## Browser Notes
|
|
177
|
-
|
|
178
|
-
The primary path uses WebGL2 with floating-point render targets. A WebGL1 path is attempted when float textures and float color buffers are available. Current Chrome, Safari, and Firefox support the WebGL2 path on modern desktop systems; older browsers or restricted GPU environments may fall back to showing only the underlay and foreground.
|
|
179
|
-
|
|
180
|
-
Useful APIs:
|
|
181
|
-
|
|
182
|
-
- WebGL2: https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext
|
|
183
|
-
- PointerEvent.getCoalescedEvents(): https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/getCoalescedEvents
|
|
184
|
-
- ResizeObserver: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
|
185
|
-
- IntersectionObserver: https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
|
186
|
-
|
|
187
|
-
## Demo
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
npm install
|
|
191
|
-
npm run dev
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Open the local Vite URL and move across the hero. The `/playground` controls adjust wake shape, smoothing, segment spacing, damping, wave speed, normal scale, refraction, specular, crest/caustic intensity, and simulation resolution.
|
|
195
|
-
|
|
196
|
-
## Quality
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
npm test
|
|
200
|
-
npm run typecheck
|
|
201
|
-
npm run build
|
|
202
|
-
```
|
|
139
|
+
|
|
140
|
+
## Modes
|
|
141
|
+
|
|
142
|
+
### `dom`
|
|
143
|
+
|
|
144
|
+
Use `dom` when the background is arbitrary live React/DOM content. The DOM underlay is rendered normally, foreground content stays sharp, and the WebGL canvas draws transparent water highlights derived from the heightfield.
|
|
145
|
+
|
|
146
|
+
Browsers do not provide an efficient, general API for sampling arbitrary live DOM into WebGL every frame. RippleFlow therefore does not pretend to perform perfect DOM refraction. The visible water material comes from heightfield normals, specular, crest, and caustic lighting, so ripples remain readable even when the DOM itself is not displaced.
|
|
147
|
+
|
|
148
|
+
### `texture` / `canvas`
|
|
149
|
+
|
|
150
|
+
Use `texture` or `canvas` when you can provide an image, video, canvas, or URL that WebGL can sample. This path gives the best visual quality because the shader offsets the sampled background with `normal.xy` from the water heightfield.
|
|
151
|
+
|
|
152
|
+
If no `texture` is supplied in these modes, RippleFlow renders a procedural gradient background so the playground and demos still show true refraction. When `texture` is a URL, the canvas remains transparent while the image loads and if loading or WebGL upload fails. A successful load automatically requests a fresh render.
|
|
153
|
+
|
|
154
|
+
## How It Works
|
|
155
|
+
|
|
156
|
+
- A WebGL renderer maintains two ping-pong floating-point simulation textures.
|
|
157
|
+
- Each texel stores height and velocity.
|
|
158
|
+
- Every fixed step samples neighboring heights, accelerates velocity toward the neighbor average, applies damping, and integrates height.
|
|
159
|
+
- Pointer input uses `pointermove` plus `getCoalescedEvents()` where available, then low-pass filters samples into a virtual cursor.
|
|
160
|
+
- Pointer movement is stamped as continuous SDF/capsule wake segments, not repeated timed circles.
|
|
161
|
+
- Each segment uses a directional profile: center trough, paired side ridges, a trailing wake mask, and subtle behind-only wavelets.
|
|
162
|
+
- The material pass computes normals from height gradients, then combines refraction, specular highlights, crest/trough visibility, and projected caustic light that fades with ripple activity.
|
|
163
|
+
- The animation loop runs while input is active, then sleeps after ripples measure as visually quiet.
|
|
164
|
+
|
|
165
|
+
## Performance Notes
|
|
166
|
+
|
|
167
|
+
- The simulation resolution defaults to `192`, while the visible canvas still matches the container size.
|
|
168
|
+
- Keep `simulationResolution` around `128...256` for most portfolio hero sections.
|
|
169
|
+
- The renderer uses refs and an imperative animation loop; React state is not updated per frame.
|
|
170
|
+
- `ResizeObserver` keeps the canvas responsive.
|
|
171
|
+
- `IntersectionObserver` and `document.visibilityState` pause offscreen or hidden work when available.
|
|
172
|
+
- `toDataURL()` is not used in the primary render path.
|
|
173
|
+
- Dynamic canvas/video texture sources may be uploaded each rendered frame in `canvas`/`texture` mode.
|
|
174
|
+
- `prefers-reduced-motion` is respected by default.
|
|
175
|
+
|
|
176
|
+
## Browser Notes
|
|
177
|
+
|
|
178
|
+
The primary path uses WebGL2 with floating-point render targets. A WebGL1 path is attempted when float textures and float color buffers are available. Current Chrome, Safari, and Firefox support the WebGL2 path on modern desktop systems; older browsers or restricted GPU environments may fall back to showing only the underlay and foreground.
|
|
179
|
+
|
|
180
|
+
Useful APIs:
|
|
181
|
+
|
|
182
|
+
- WebGL2: https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext
|
|
183
|
+
- PointerEvent.getCoalescedEvents(): https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/getCoalescedEvents
|
|
184
|
+
- ResizeObserver: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
|
|
185
|
+
- IntersectionObserver: https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
|
186
|
+
|
|
187
|
+
## Demo
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm install
|
|
191
|
+
npm run dev
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Open the local Vite URL and move across the hero. The `/playground` controls adjust wake shape, smoothing, segment spacing, damping, wave speed, normal scale, refraction, specular, crest/caustic intensity, and simulation resolution.
|
|
195
|
+
|
|
196
|
+
## Quality
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm test
|
|
200
|
+
npm run typecheck
|
|
201
|
+
npm run build
|
|
202
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaterDistortion.d.ts","sourceRoot":"","sources":["../src/WaterDistortion.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAStD,OAAO,EAEL,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAS9B,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AACrE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,CAAC,EAAE,MAAM,CAAC;IACV,+DAA+D;IAC/D,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkOD,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"WaterDistortion.d.ts","sourceRoot":"","sources":["../src/WaterDistortion.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAStD,OAAO,EAEL,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAS9B,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AACrE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,CAAC,EAAE,MAAM,CAAC;IACV,+DAA+D;IAC/D,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkOD,eAAO,MAAM,eAAe,wHA0qB1B,CAAC;AAEH,eAAO,MAAM,UAAU,wHAAkB,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -21,6 +21,7 @@ export interface WaterActivityThresholds {
|
|
|
21
21
|
}
|
|
22
22
|
export declare class HeightfieldWaterRenderer {
|
|
23
23
|
private readonly canvas;
|
|
24
|
+
private readonly requestRender;
|
|
24
25
|
private readonly gl;
|
|
25
26
|
private readonly isWebGL2;
|
|
26
27
|
private readonly canLinearFloat;
|
|
@@ -44,7 +45,7 @@ export declare class HeightfieldWaterRenderer {
|
|
|
44
45
|
private readonly wakeAUniforms;
|
|
45
46
|
private readonly wakeBUniforms;
|
|
46
47
|
private readonly wakeShapeUniforms;
|
|
47
|
-
constructor(canvas: HTMLCanvasElement);
|
|
48
|
+
constructor(canvas: HTMLCanvasElement, requestRender?: () => void);
|
|
48
49
|
resize(cssWidth: number, cssHeight: number, pixelRatio: number, simulationResolution: number): void;
|
|
49
50
|
setTextureSource(source: WaterTextureSource | undefined): void;
|
|
50
51
|
step(disturbances: readonly WaterDisturbance[], settings: WaterRendererSettings, delta: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waterRenderer.d.ts","sourceRoot":"","sources":["../../src/core/waterRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqSD,qBAAa,wBAAwB;
|
|
1
|
+
{"version":3,"file":"waterRenderer.d.ts","sourceRoot":"","sources":["../../src/core/waterRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAC3D,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqSD,qBAAa,wBAAwB;IA0BjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa;IA1BhC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAe;IAC9C,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,YAAY,CAAmD;IACvE,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2C;gBAG1D,MAAM,EAAE,iBAAiB,EACzB,aAAa,GAAE,MAAM,IAAsB;IA2B9D,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,oBAAoB,EAAE,MAAM,GAC3B,IAAI;IA4BP,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,IAAI;IAwC9D,IAAI,CACF,YAAY,EAAE,SAAS,gBAAgB,EAAE,EACzC,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,MAAM,GACZ,IAAI;IAoBP,MAAM,CAAC,QAAQ,EAAE,qBAAqB,EAAE,WAAW,SAAI,GAAG,IAAI;IAoC9D,kBAAkB,IAAI,OAAO;IAoC7B,KAAK,IAAI,IAAI;IAmBb,OAAO,IAAI,IAAI;IAYf,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,kBAAkB;IAiD1B,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,2BAA2B;IAqBnC,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,qBAAqB;IA+B7B,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,qBAAqB;IA8C7B,OAAO,CAAC,mBAAmB;CAwB5B;AA4OD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,EACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,uBAAqD,GAChE,OAAO,CAyCT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var Ye=Object.defineProperty;var
|
|
1
|
+
"use strict";var Ye=Object.defineProperty;var qe=(r,e,n)=>e in r?Ye(r,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[e]=n;var m=(r,e,n)=>qe(r,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ae=require("react/jsx-runtime"),f=require("react");function h(r,e,n){return Math.min(n,Math.max(e,r))}function Ve(r,e){return Math.hypot(e.x-r.x,e.y-r.y)}function O(r,e,n){return r+(e-r)*n}const Z=24,C=128,oe=8,se=Math.PI*2,de={causticGain:.1,screenCompensation:1.35,maxAlpha:.9},je={gradient:8e-4,curvature:8e-4,trough:.006,velocity:8e-4},be=`
|
|
2
2
|
attribute vec2 a_position;
|
|
3
3
|
varying vec2 v_uv;
|
|
4
4
|
|
|
@@ -247,5 +247,5 @@ void main() {
|
|
|
247
247
|
vec3 color = base + highlight - vec3(0.015, 0.055, 0.070) * visibleTrough * 0.45;
|
|
248
248
|
gl_FragColor = vec4(color, 1.0);
|
|
249
249
|
}
|
|
250
|
-
`;class Ze{constructor(e){m(this,"gl");m(this,"isWebGL2");m(this,"canLinearFloat");m(this,"quadBuffer");m(this,"simulationProgram");m(this,"materialProgram");m(this,"backgroundTexture");m(this,"causticTexture");m(this,"stateTextures");m(this,"framebuffers");m(this,"readIndex",0);m(this,"simWidth",1);m(this,"simHeight",1);m(this,"cssWidth",1);m(this,"cssHeight",1);m(this,"textureSource");m(this,"textureImage");m(this,"textureReady",!1);m(this,"textureNeedsUpload",!1);m(this,"activityPixels");m(this,"wakeAUniforms",new Float32Array(Z*4));m(this,"wakeBUniforms",new Float32Array(Z*4));m(this,"wakeShapeUniforms",new Float32Array(Z*4));this.canvas=e;const{gl:t,isWebGL2:n,canLinearFloat:i}=et(e);this.gl=t,this.isWebGL2=n,this.canLinearFloat=i,this.quadBuffer=V(t.createBuffer(),"WebGL buffer"),this.simulationProgram=Fe(t,be,$e),this.materialProgram=Fe(t,be,Qe),this.backgroundTexture=V(t.createTexture(),"WebGL texture"),this.causticTexture=V(t.createTexture(),"WebGL texture"),t.bindBuffer(t.ARRAY_BUFFER,this.quadBuffer),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),t.STATIC_DRAW),t.disable(t.DEPTH_TEST),t.disable(t.STENCIL_TEST),t.disable(t.BLEND),this.initializeBackgroundTexture(),this.initializeCausticTexture(),this.resize(1,1,1,192)}resize(e,t,n,i){this.cssWidth=Math.max(1,e),this.cssHeight=Math.max(1,t);const a=Math.min(2,Math.max(1,n||1)),l=Math.max(1,Math.round(this.cssWidth*a)),c=Math.max(1,Math.round(this.cssHeight*a));(this.canvas.width!==l||this.canvas.height!==c)&&(this.canvas.width=l,this.canvas.height=c);const s=Math.max(32,Math.round(i)),d=this.cssWidth/this.cssHeight,_=d>=1?s:Math.max(1,Math.round(s*d)),S=d>=1?Math.max(1,Math.round(s/d)):s;_===this.simWidth&&S===this.simHeight||(this.simWidth=_,this.simHeight=S,this.recreateState())}setTextureSource(e){if(e===this.textureSource||(this.textureSource=e,this.textureImage=void 0,this.textureReady=!1,this.textureNeedsUpload=!1,!e))return;if(typeof e!="string"){this.textureImage=e,this.textureNeedsUpload=!0;return}const t=new Image;t.crossOrigin="anonymous",t.onload=()=>{this.textureSource===e&&(this.textureImage=t,this.textureNeedsUpload=!0)},t.onerror=()=>{this.textureSource===e&&(this.textureImage=void 0,this.textureReady=!1,this.textureNeedsUpload=!1)},t.src=e}step(e,t,n){if(!this.stateTextures||!this.framebuffers)return;const i=this.gl,a=1-this.readIndex;i.useProgram(this.simulationProgram.program),i.bindFramebuffer(i.FRAMEBUFFER,this.framebuffers[a]),i.viewport(0,0,this.simWidth,this.simHeight),i.activeTexture(i.TEXTURE0),i.bindTexture(i.TEXTURE_2D,this.stateTextures[this.readIndex]),this.prepareQuad(this.simulationProgram),this.setSimulationUniforms(e,t,n),i.drawArrays(i.TRIANGLE_STRIP,0,4),i.bindFramebuffer(i.FRAMEBUFFER,null),this.readIndex=a}render(e,t=0){const n=this.gl;this.stateTextures&&(e.texture?this.setTextureSource(e.texture):this.setTextureSource(void 0),this.uploadTextureIfNeeded(e),n.useProgram(this.materialProgram.program),n.bindFramebuffer(n.FRAMEBUFFER,null),n.viewport(0,0,this.canvas.width,this.canvas.height),n.clearColor(0,0,0,0),n.clear(n.COLOR_BUFFER_BIT),n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,this.stateTextures[this.readIndex]),n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,this.backgroundTexture),n.activeTexture(n.TEXTURE2),n.bindTexture(n.TEXTURE_2D,this.causticTexture),this.prepareQuad(this.materialProgram),this.setMaterialUniforms(e,t),n.drawArrays(n.TRIANGLE_STRIP,0,4))}hasVisibleActivity(){if(!this.framebuffers)return!1;const e=this.gl,t=this.framebuffers[this.readIndex],n=this.simWidth*this.simHeight*4;(!this.activityPixels||this.activityPixels.length!==n)&&(this.activityPixels=new Float32Array(n)),e.bindFramebuffer(e.FRAMEBUFFER,t);try{e.readPixels(0,0,this.simWidth,this.simHeight,e.RGBA,e.FLOAT,this.activityPixels)}finally{e.bindFramebuffer(e.FRAMEBUFFER,null)}return rt(this.activityPixels,this.simWidth,this.simHeight)}clear(){const e=this.gl;if(this.framebuffers){e.viewport(0,0,this.simWidth,this.simHeight);for(const t of this.framebuffers)e.bindFramebuffer(e.FRAMEBUFFER,t),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT)}e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,this.canvas.width,this.canvas.height),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)}dispose(){var t,n;const e=this.gl;(t=this.stateTextures)==null||t.forEach(i=>e.deleteTexture(i)),(n=this.framebuffers)==null||n.forEach(i=>e.deleteFramebuffer(i)),e.deleteTexture(this.backgroundTexture),e.deleteTexture(this.causticTexture),e.deleteBuffer(this.quadBuffer),e.deleteProgram(this.simulationProgram.program),e.deleteProgram(this.materialProgram.program)}recreateState(){var l,c;const e=this.gl;(l=this.stateTextures)==null||l.forEach(s=>e.deleteTexture(s)),(c=this.framebuffers)==null||c.forEach(s=>e.deleteFramebuffer(s));const t=this.createStateTexture(),n=this.createStateTexture(),i=this.createFramebuffer(t),a=this.createFramebuffer(n);this.stateTextures=[t,n],this.framebuffers=[i,a],this.readIndex=0,this.clear()}createStateTexture(){const e=this.gl,t=V(e.createTexture(),"WebGL texture");if(e.bindTexture(e.TEXTURE_2D,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,this.canLinearFloat?e.LINEAR:e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,this.canLinearFloat?e.LINEAR:e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),this.isWebGL2){const n=e;n.texImage2D(n.TEXTURE_2D,0,n.RGBA32F,this.simWidth,this.simHeight,0,n.RGBA,n.FLOAT,null)}else e.texImage2D(e.TEXTURE_2D,0,e.RGBA,this.simWidth,this.simHeight,0,e.RGBA,e.FLOAT,null);return t}createFramebuffer(e){const t=this.gl,n=V(t.createFramebuffer(),"WebGL framebuffer");if(t.bindFramebuffer(t.FRAMEBUFFER,n),t.framebufferTexture2D(t.FRAMEBUFFER,t.COLOR_ATTACHMENT0,t.TEXTURE_2D,e,0),t.checkFramebufferStatus(t.FRAMEBUFFER)!==t.FRAMEBUFFER_COMPLETE)throw new Error("WaterDistortion could not create a float simulation target.");return t.bindFramebuffer(t.FRAMEBUFFER,null),n}initializeBackgroundTexture(){const e=this.gl;e.bindTexture(e.TEXTURE_2D,this.backgroundTexture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,new Uint8Array([18,28,26,255]))}initializeCausticTexture(){const e=this.gl;e.bindTexture(e.TEXTURE_2D,this.causticTexture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.REPEAT),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.REPEAT),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,C,C,0,e.RGBA,e.UNSIGNED_BYTE,Je()),e.generateMipmap(e.TEXTURE_2D)}uploadTextureIfNeeded(e){if(e.mode==="dom"||!this.textureImage||!this.textureNeedsUpload&&!tt(this.textureImage))return;const t=this.gl;try{t.bindTexture(t.TEXTURE_2D,this.backgroundTexture),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,1),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,this.textureImage),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,0),this.textureReady=!0,this.textureNeedsUpload=!1}catch{this.textureReady=!1,this.textureNeedsUpload=!1}}prepareQuad(e){const t=this.gl;t.bindBuffer(t.ARRAY_BUFFER,this.quadBuffer),t.enableVertexAttribArray(e.position),t.vertexAttribPointer(e.position,2,t.FLOAT,!1,0,0)}setSimulationUniforms(e,t,n){const i=this.gl,a=this.simulationProgram.program,l=Math.min(e.length,Z),c=this.wakeAUniforms,s=this.wakeBUniforms,d=this.wakeShapeUniforms,_=Math.max(0,e.length-Z);c.fill(0),s.fill(0),d.fill(0);for(let S=0;S<l;S+=1){const g=e[_+S],x=S*4;c[x]=g.ax,c[x+1]=g.ay,c[x+2]=g.radius,c[x+3]=g.wakeStrength,s[x]=g.bx,s[x+1]=g.by,s[x+2]=g.wakeLength,s[x+3]=g.velocity,d[x]=g.troughStrength,d[x+1]=g.ridgeStrength,d[x+2]=g.ridgeOffset}i.uniform1i(p(i,a,"u_state"),0),i.uniform2f(p(i,a,"u_texel"),1/this.simWidth,1/this.simHeight),i.uniform1f(p(i,a,"u_delta"),n),i.uniform1f(p(i,a,"u_damping"),t.damping),i.uniform1f(p(i,a,"u_stiffness"),t.waveSpeed),i.uniform1f(p(i,a,"u_aspect"),this.cssWidth/this.cssHeight),i.uniform1i(p(i,a,"u_wakeCount"),l),i.uniform4fv(p(i,a,"u_wakeA[0]"),c),i.uniform4fv(p(i,a,"u_wakeB[0]"),s),i.uniform4fv(p(i,a,"u_wakeShape[0]"),d)}setMaterialUniforms(e,t){const n=this.gl,i=this.materialProgram.program,a=e.mode!=="dom";n.uniform1i(p(n,i,"u_state"),0),n.uniform1i(p(n,i,"u_background"),1),n.uniform1i(p(n,i,"u_causticMap"),2),n.uniform2f(p(n,i,"u_texel"),1/this.simWidth,1/this.simHeight),n.uniform1f(p(n,i,"u_domMode"),e.mode==="dom"?1:0),n.uniform1f(p(n,i,"u_textureEnabled"),a&&this.textureReady?1:0),n.uniform1f(p(n,i,"u_normalScale"),e.normalScale),n.uniform1f(p(n,i,"u_refractionStrength"),e.refractionStrength),n.uniform1f(p(n,i,"u_specularIntensity"),e.specularIntensity),n.uniform1f(p(n,i,"u_crestIntensity"),e.crestIntensity),n.uniform1f(p(n,i,"u_causticIntensity"),e.causticIntensity),n.uniform1f(p(n,i,"u_time"),t%1e3)}}function Je(){const r=new Uint8Array(C*C*4);for(let e=0;e<C;e+=1)for(let t=0;t<C;t+=1){const n=(t+.5)/C,i=(e+.5)/C,a=Math.round(ce(n,i,0)*255),l=Math.round(ce(n,i,11)*255),c=Math.round(ce(n,i,23)*255),s=Math.round(ce(n,i,37)*255),d=(e*C+t)*4;r[d]=a,r[d+1]=l,r[d+2]=c,r[d+3]=s}return r}function ce(r,e,t){const n=Math.sin(se*(e*2+t*.013))*.12+Math.sin(se*(r*3+e+t*.021))*.06,i=Math.cos(se*(r*2+t*.017))*.12+Math.sin(se*(e*3-r+t*.019))*.06,a=r*oe+n,l=e*oe+i,c=Math.floor(a),s=Math.floor(l);let d=Number.POSITIVE_INFINITY,_=Number.POSITIVE_INFINITY,S=0,g=0;for(let P=-1;P<=1;P+=1)for(let D=-1;D<=1;D+=1){const k=c+D,B=s+P,M=Ae(k,oe),I=Ae(B,oe),z=k+.16+fe(M,I,t)*.68,N=B+.16+fe(M,I,t+1)*.68,U=Math.hypot(a-z,l-N);U<d?(_=d,d=U,S=M,g=I):U<_&&(_=U)}const x=_-d,A=1-Ie(.025,.13,x),w=1-Ie(.08,.34,x),W=.74+fe(S,g,t+2)*.26,H=A*A*.58+w*.34;return Ce(Math.pow(H*W,1.12))}function fe(r,e,t){const n=Math.sin(r*127.1+e*311.7+t*74.7)*43758.5453123;return n-Math.floor(n)}function Ae(r,e){return(r%e+e)%e}function Ie(r,e,t){const n=Ce((t-r)/(e-r));return n*n*(3-n*2)}function Ce(r){return Math.min(1,Math.max(0,r))}function et(r){const e={alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!1,preserveDrawingBuffer:!1},t=r.getContext("webgl2",e);if(t){if(!t.getExtension("EXT_color_buffer_float"))throw new Error("WaterDistortion requires float render targets.");return{gl:t,isWebGL2:!0,canLinearFloat:!!t.getExtension("OES_texture_float_linear")}}const n=r.getContext("webgl",e)||r.getContext("experimental-webgl",e);if(!n)throw new Error("WaterDistortion requires WebGL.");if(!n.getExtension("OES_texture_float")||!n.getExtension("WEBGL_color_buffer_float")&&!n.getExtension("EXT_color_buffer_float"))throw new Error("WaterDistortion requires float texture support.");return{gl:n,isWebGL2:!1,canLinearFloat:!!n.getExtension("OES_texture_float_linear")}}function Fe(r,e,t){const n=Le(r,r.VERTEX_SHADER,e),i=Le(r,r.FRAGMENT_SHADER,t),a=V(r.createProgram(),"WebGL program");if(r.attachShader(a,n),r.attachShader(a,i),r.linkProgram(a),r.deleteShader(n),r.deleteShader(i),!r.getProgramParameter(a,r.LINK_STATUS)){const c=r.getProgramInfoLog(a)||"Unknown shader link error.";throw r.deleteProgram(a),new Error(c)}const l=r.getAttribLocation(a,"a_position");if(l<0)throw new Error("WaterDistortion could not bind its quad attribute.");return{program:a,position:l}}function Le(r,e,t){const n=V(r.createShader(e),"WebGL shader");if(r.shaderSource(n,t),r.compileShader(n),!r.getShaderParameter(n,r.COMPILE_STATUS)){const i=r.getShaderInfoLog(n)||"Unknown shader compile error.";throw r.deleteShader(n),new Error(i)}return n}function p(r,e,t){const n=r.getUniformLocation(e,t);if(!n)throw new Error(`WaterDistortion could not bind uniform ${t}.`);return n}function V(r,e){if(!r)throw new Error(`WaterDistortion could not create ${e}.`);return r}function tt(r){return typeof HTMLCanvasElement<"u"&&r instanceof HTMLCanvasElement||typeof HTMLVideoElement<"u"&&r instanceof HTMLVideoElement||typeof OffscreenCanvas<"u"&&r instanceof OffscreenCanvas}function rt(r,e,t,n=je){const i=Math.max(0,Math.floor(e)),a=Math.max(0,Math.floor(t));if(i===0||a===0)return!1;const l=(c,s)=>r[(s*i+c)*4]??0;for(let c=0;c<a;c+=1)for(let s=0;s<i;s+=1){const d=(c*i+s)*4,_=r[d]??0,S=r[d+1]??0;if(_<-n.trough||Math.abs(S)>n.velocity)return!0;const g=s>0?l(s-1,c):_,x=s<i-1?l(s+1,c):_,A=c>0?l(s,c-1):_,w=c<a-1?l(s,c+1):_,W=Math.hypot(x-g,w-A),H=Math.abs(g+x+A+w-_*4);if(W>n.gradient||H>n.curvature)return!0}return!1}const L={width:1,height:1,interactionRadius:25,wakeStrength:.54,troughStrength:.92,ridgeStrength:.46,ridgeOffset:10,wakeLength:84,velocityScale:1,velocityClamp:1.35,cursorSmoothing:.2,segmentSpacing:18},nt=24;function me(r){return{width:Math.max(1,r.width??L.width),height:Math.max(1,r.height??L.height),interactionRadius:Math.max(1,r.interactionRadius??L.interactionRadius),wakeStrength:Math.max(0,r.wakeStrength??L.wakeStrength),troughStrength:Math.max(0,r.troughStrength??L.troughStrength),ridgeStrength:Math.max(0,r.ridgeStrength??L.ridgeStrength),ridgeOffset:Math.max(0,r.ridgeOffset??L.ridgeOffset),wakeLength:Math.max(1,r.wakeLength??L.wakeLength),velocityScale:Math.max(.01,r.velocityScale??L.velocityScale),velocityClamp:Math.max(.05,r.velocityClamp??L.velocityClamp),cursorSmoothing:h(r.cursorSmoothing??L.cursorSmoothing,0,.9),segmentSpacing:Math.max(1,r.segmentSpacing??L.segmentSpacing)}}function ke(r,e,t=r.timeStamp){const n=typeof r.getCoalescedEvents=="function"?r.getCoalescedEvents():[],i=n.length>0?n:[r],a=t-r.timeStamp;return i.map(l=>({x:l.clientX-e.left,y:l.clientY-e.top,time:l.timeStamp+a}))}class it{constructor(e={}){m(this,"options");m(this,"previous");m(this,"smoothedVelocity",0);m(this,"disturbances",[]);this.options=me(e)}configure(e){this.options=me({...this.options,...e})}reset(){this.previous=void 0,this.smoothedVelocity=0,this.disturbances.length=0}addSample(e){this.disturbances.length=0;const t=this.clampSample(e),n=this.previous;if(!n)return this.previous=t,this.disturbances;t.time<=n.time&&(t.time=n.time+.01);const i=this.smoothSample(n,t),a=qe(n,i);if(a<.35)return this.previous=i,this.disturbances;const l=a/Math.max(.01,i.time-n.time),c=h(1-this.options.cursorSmoothing,.12,1);this.smoothedVelocity=O(this.smoothedVelocity,l,c),this.previous=i;const s=Math.min(nt,Math.max(1,Math.ceil(a/this.options.segmentSpacing)));for(let d=0;d<s;d+=1){const _=d/s,S=(d+1)/s;this.disturbances.push(ge(De(n,i,_),De(n,i,S),this.smoothedVelocity,this.options))}return this.disturbances}clampSample(e){return{x:h(e.x,0,this.options.width),y:h(e.y,0,this.options.height),time:e.time}}smoothSample(e,t){const n=1-this.options.cursorSmoothing;return{x:O(e.x,t.x,n),y:O(e.y,t.y,n),time:t.time}}}function ge(r,e,t,n){const i=me(n),a=h(t*i.velocityScale/i.velocityClamp,0,1),l=h(r.y/i.height,0,1),c=h(e.y/i.height,0,1),s=i.wakeLength*O(.55,1.35,a);return{ax:h(r.x/i.width,0,1),ay:1-l,bx:h(e.x/i.width,0,1),by:1-c,radius:i.interactionRadius/i.height,wakeStrength:i.wakeStrength*O(.18,1,a),troughStrength:i.troughStrength,ridgeStrength:i.ridgeStrength,ridgeOffset:i.ridgeOffset/i.height,wakeLength:s/i.height,velocity:a}}function De(r,e,t){return{x:O(r.x,e.x,t),y:O(r.y,e.y,t),time:O(r.time,e.time,t)}}function at(r){const e=h(r,.85,.998),t=Math.ceil(Math.log(.012)/Math.log(e));return h(t*(1e3/60),900,5200)}const E={mode:"texture",damping:.98,waveSpeed:.4,normalScale:20,refractionStrength:.7,specularIntensity:1,crestIntensity:.2,causticIntensity:0,simulationResolution:192,wakeStrength:.54,interactionRadius:25,troughStrength:.92,ridgeStrength:.46,ridgeOffset:10,wakeLength:84,velocityScale:1,velocityClamp:1.35,cursorSmoothing:.2,segmentSpacing:18,idleTimeout:900,interactionMode:"move",runInBackground:!1,pauseKey:void 0},G=1e3/60,ot=4,Ue=360,Pe=24,st=200;function ct(r){return r instanceof Element&&r.closest("a, button, input, select, textarea, [contenteditable]:not([contenteditable=false])")!==null}function ut(r){const e=r.strength===void 0?void 0:r.strength/48;return{mode:r.mode??E.mode,texture:r.texture,damping:h(r.damping??r.dissipation??E.damping,.85,.998),waveSpeed:h(r.waveSpeed??E.waveSpeed,.05,1),normalScale:h(r.normalScale??E.normalScale,1,80),refractionStrength:h(r.refractionStrength??E.refractionStrength,0,2.5),specularIntensity:h(r.specularIntensity??E.specularIntensity,0,3),crestIntensity:h(r.crestIntensity??E.crestIntensity,0,3),causticIntensity:h(r.causticIntensity??E.causticIntensity,0,3),simulationResolution:h(Math.round(r.simulationResolution??E.simulationResolution),64,384),wakeStrength:h(r.wakeStrength??r.rippleStrength??e??E.wakeStrength,0,3),interactionRadius:h(r.interactionRadius??E.interactionRadius,2,80),troughStrength:h(r.troughStrength??E.troughStrength,0,3),ridgeStrength:h(r.ridgeStrength??E.ridgeStrength,0,3),ridgeOffset:h(r.ridgeOffset??E.ridgeOffset,0,80),wakeLength:h(r.wakeLength??E.wakeLength,1,240),velocityScale:h(r.velocityScale??E.velocityScale,.01,4),velocityClamp:h(r.velocityClamp??E.velocityClamp,.05,8),cursorSmoothing:h(r.cursorSmoothing??E.cursorSmoothing,0,.9),segmentSpacing:h(r.segmentSpacing??E.segmentSpacing,1,96),idleTimeout:Math.max(120,r.idleTimeout??E.idleTimeout),interactionMode:r.interactionMode??E.interactionMode,runInBackground:r.runInBackground??E.runInBackground,pauseKey:r.pauseKey||E.pauseKey}}function Be(r,e,t){const n=h(r.x,0,1)*t.width,i=h(r.y,0,1)*t.height,a=Math.max(.5,e.interactionRadius*.08),l=e.velocityClamp/e.velocityScale,c={...e,width:t.width,height:t.height,wakeLength:e.interactionRadius*1.5};return[ge({x:n-a,y:i},{x:n+a,y:i},l,c),ge({x:n,y:i-a},{x:n,y:i+a},l,c)]}function lt(r){const[e,t]=f.useState(!1);return f.useEffect(()=>{if(!r||typeof window>"u"||!window.matchMedia){t(!1);return}const n=window.matchMedia("(prefers-reduced-motion: reduce)");t(n.matches);const i=a=>{t(a.matches)};return n.addEventListener("change",i),()=>{n.removeEventListener("change",i)}},[r]),e}const We=f.forwardRef(function(e,t){const{underlay:n,children:i,foreground:a,className:l,style:c,reducedMotion:s="respect"}=e,d=f.useRef(null),_=f.useRef(null),S=f.useRef(null),g=f.useRef(new it),x=f.useRef(null),A=f.useRef(null),w=f.useRef(()=>{}),W=f.useRef(()=>{}),H=f.useRef(()=>{}),P=f.useRef([]),D=f.useRef(!0),k=f.useRef(!0),B=f.useRef(!1),M=f.useRef(!1),I=f.useRef(null),z=f.useRef(0),N=f.useRef(0),U=f.useRef(0),y=ut(e),v=f.useRef(y),q=f.useRef({width:1,height:1}),Ne=lt(s==="respect"),ue=s==="disable"||s==="respect"&&Ne,Xe=a??i;v.current=y,f.useImperativeHandle(t,()=>({triggerRipple(T){B.current||M.current||H.current(Be(T,v.current,q.current))}}),[]),f.useEffect(()=>{var T;B.current=ue,ue&&(g.current.reset(),I.current=null,P.current=[],z.current=0,N.current=0,U.current=0,(T=S.current)==null||T.clear()),w.current()},[ue]),f.useEffect(()=>{const T=S.current;if(!T)return;const{width:K,height:b}=q.current;T.resize(K,b,window.devicePixelRatio,y.simulationResolution),T.setTextureSource(y.texture),w.current()},[y.mode,y.texture,y.damping,y.waveSpeed,y.normalScale,y.refractionStrength,y.specularIntensity,y.crestIntensity,y.causticIntensity,y.simulationResolution]),f.useEffect(()=>{const T=d.current,K=_.current;if(!T||!K)return;k.current=typeof document.hasFocus=="function"?document.hasFocus():!0;let b=null,ne,J=G,ve=!1,ee;const Y=()=>{x.current!==null&&(window.cancelAnimationFrame(x.current),x.current=null),A.current!==null&&(window.clearTimeout(A.current),A.current=null)},Oe=(o,u)=>{g.current.configure({width:o,height:u,interactionRadius:v.current.interactionRadius,wakeStrength:v.current.wakeStrength,troughStrength:v.current.troughStrength,ridgeStrength:v.current.ridgeStrength,ridgeOffset:v.current.ridgeOffset,wakeLength:v.current.wakeLength,velocityScale:v.current.velocityScale,velocityClamp:v.current.velocityClamp,cursorSmoothing:v.current.cursorSmoothing,segmentSpacing:v.current.segmentSpacing})},He=(o,u)=>{const F=Math.max(1,o),R=Math.max(1,u);q.current={width:F,height:R},Oe(F,R),b==null||b.resize(F,R,window.devicePixelRatio,v.current.simulationResolution)},xe=()=>{b==null||b.render(v.current,performance.now()*.001)},X=()=>{ne=void 0,J=G},te=()=>{Y(),g.current.reset(),I.current=null,X(),ee===void 0&&(ee=performance.now())},j=()=>{const o=v.current;let u=!1;if(!(!B.current&&!M.current&&D.current&&(o.runInBackground||!document.hidden&&k.current))){X();return}if(ee!==void 0){const R=performance.now()-ee;z.current+=R,N.current+=R,U.current+=R,ee=void 0,u=!0}(!o.runInBackground||u)&&X(),xe(),w.current()},Ee=o=>{x.current=null;const u=v.current;if(!b||M.current||!D.current||!u.runInBackground&&(document.hidden||!k.current)){X();return}if(B.current){P.current=[],b.render(u,o*.001),X();return}const F=u.runInBackground?Ue:ot,R=ne===void 0?G:h(o-ne,0,u.runInBackground?G*Ue:64);ne=o,J=Math.min(J+R,G*F);let Q=P.current.splice(0),Me=0;for(;J>=G&&Me<F;)b.step(Q,u,1),Q=[],J-=G,Me+=1;if(b.render(u,o*.001),P.current.length>0){w.current();return}if(o<z.current){w.current();return}if(ve){o<N.current?w.current():X();return}if(o<U.current){w.current();return}try{b.hasVisibleActivity()?(U.current=o+st,w.current()):X()}catch{ve=!0,o<N.current?w.current():X()}},le=()=>{M.current||x.current!==null||A.current!==null||(v.current.runInBackground&&document.hidden?A.current=window.setTimeout(()=>{A.current=null,Ee(performance.now())},G):x.current=window.requestAnimationFrame(Ee))};w.current=le;try{b=new Ze(K),S.current=b,b.setTextureSource(v.current.texture)}catch{return K.style.display="none",S.current=null,w.current=()=>{},()=>{}}const ie=()=>{const o=K.getBoundingClientRect();return He(o.width,o.height),o},ze=()=>{ie(),xe()},he=o=>{if(M.current||o.length===0)return;const u=P.current;for(const Q of o)u.push(Q);u.length>Pe&&u.splice(0,u.length-Pe);const R=performance.now()+v.current.idleTimeout;z.current=Math.max(z.current,R),N.current=Math.max(N.current,R+at(v.current.damping)),U.current=0,le()};H.current=he;const pe=o=>{const u=v.current;if(B.current||M.current||!D.current||u.interactionMode==="event"||!u.runInBackground&&(document.hidden||!k.current)||o.pointerType==="touch"&&!o.isPrimary)return;g.current.reset();const F=ie(),[R]=ke(o,F,performance.now());R&&((u.interactionMode==="click"||u.interactionMode==="drag")&&he(Be({x:R.x/q.current.width,y:R.y/q.current.height},u,q.current)),u.interactionMode==="drag"?(I.current=o.pointerId,g.current.addSample(R)):u.interactionMode==="move"&&g.current.addSample(R))},_e=o=>{const u=v.current;if(B.current||M.current||!D.current||u.interactionMode!=="move"&&(u.interactionMode!=="drag"||I.current!==o.pointerId)||!u.runInBackground&&(document.hidden||!k.current)||o.pointerType==="touch"&&!o.isPrimary)return;const F=ie(),R=ke(o,F,performance.now());for(const Q of R)he(g.current.addSample(Q))},$=o=>{I.current!==null&&I.current!==o.pointerId||(I.current=null,g.current.reset())},Se=o=>{const u=v.current.pauseKey;!u||o.code!==u||o.repeat||o.altKey||o.ctrlKey||o.metaKey||ct(o.target)||(o.preventDefault(),M.current=!M.current,M.current?te():(Y(),j()))},Te=()=>{Y(),document.hidden&&!v.current.runInBackground?te():j()},Re=()=>{k.current=!1,v.current.runInBackground?(Y(),j()):te()},ye=()=>{k.current=!0,Y(),j()},Ke=()=>{Y(),!v.current.runInBackground&&(document.hidden||!k.current)?te():j()};W.current=Ke,ze();const we=new ResizeObserver(()=>{ie(),le()});we.observe(K);let re;return"IntersectionObserver"in window&&(re=new IntersectionObserver(o=>{const u=o[0];D.current=u?u.isIntersecting:!0,D.current?j():te()}),re.observe(T)),T.addEventListener("pointerdown",pe,{passive:!0}),T.addEventListener("pointermove",_e,{passive:!0}),T.addEventListener("pointerup",$,{passive:!0}),T.addEventListener("pointerleave",$,{passive:!0}),T.addEventListener("pointercancel",$,{passive:!0}),window.addEventListener("blur",Re),window.addEventListener("focus",ye),document.addEventListener("keydown",Se),document.addEventListener("visibilitychange",Te),()=>{var o;Y(),we.disconnect(),re==null||re.disconnect(),T.removeEventListener("pointerdown",pe),T.removeEventListener("pointermove",_e),T.removeEventListener("pointerup",$),T.removeEventListener("pointerleave",$),T.removeEventListener("pointercancel",$),window.removeEventListener("blur",Re),window.removeEventListener("focus",ye),document.removeEventListener("keydown",Se),document.removeEventListener("visibilitychange",Te),(o=S.current)==null||o.dispose(),S.current=null,w.current=()=>{},W.current=()=>{},H.current=()=>{}}},[]),f.useEffect(()=>{g.current.reset(),I.current=null},[y.interactionMode]),f.useEffect(()=>{W.current()},[y.runInBackground]),f.useEffect(()=>{!y.pauseKey&&M.current&&(M.current=!1,W.current())},[y.pauseKey]);const Ge={...c,position:"relative",overflow:"hidden",isolation:"isolate"};return ae.jsxs("div",{ref:d,className:l,style:Ge,children:[n?ae.jsx("div",{style:{position:"absolute",inset:0,zIndex:0,pointerEvents:"none",transform:"translateZ(0)"},children:n}):null,ae.jsx("canvas",{ref:_,"aria-hidden":"true",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",pointerEvents:"none",mixBlendMode:y.mode==="dom"?"screen":"normal"}}),ae.jsx("div",{style:{position:"relative",zIndex:2},children:Xe})]})}),ht=We;exports.WaterDistortion=We;exports.WaterLayer=ht;
|
|
250
|
+
`;class Ze{constructor(e,n=()=>{}){m(this,"gl");m(this,"isWebGL2");m(this,"canLinearFloat");m(this,"quadBuffer");m(this,"simulationProgram");m(this,"materialProgram");m(this,"backgroundTexture");m(this,"causticTexture");m(this,"stateTextures");m(this,"framebuffers");m(this,"readIndex",0);m(this,"simWidth",1);m(this,"simHeight",1);m(this,"cssWidth",1);m(this,"cssHeight",1);m(this,"textureSource");m(this,"textureImage");m(this,"textureReady",!1);m(this,"textureNeedsUpload",!1);m(this,"activityPixels");m(this,"wakeAUniforms",new Float32Array(Z*4));m(this,"wakeBUniforms",new Float32Array(Z*4));m(this,"wakeShapeUniforms",new Float32Array(Z*4));this.canvas=e,this.requestRender=n;const{gl:t,isWebGL2:i,canLinearFloat:a}=et(e);this.gl=t,this.isWebGL2=i,this.canLinearFloat=a,this.quadBuffer=q(t.createBuffer(),"WebGL buffer"),this.simulationProgram=Fe(t,be,$e),this.materialProgram=Fe(t,be,Qe),this.backgroundTexture=q(t.createTexture(),"WebGL texture"),this.causticTexture=q(t.createTexture(),"WebGL texture"),t.bindBuffer(t.ARRAY_BUFFER,this.quadBuffer),t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),t.STATIC_DRAW),t.disable(t.DEPTH_TEST),t.disable(t.STENCIL_TEST),t.disable(t.BLEND),this.initializeBackgroundTexture(),this.initializeCausticTexture(),this.resize(1,1,1,192)}resize(e,n,t,i){this.cssWidth=Math.max(1,e),this.cssHeight=Math.max(1,n);const a=Math.min(2,Math.max(1,t||1)),l=Math.max(1,Math.round(this.cssWidth*a)),c=Math.max(1,Math.round(this.cssHeight*a));(this.canvas.width!==l||this.canvas.height!==c)&&(this.canvas.width=l,this.canvas.height=c);const s=Math.max(32,Math.round(i)),d=this.cssWidth/this.cssHeight,_=d>=1?s:Math.max(1,Math.round(s*d)),S=d>=1?Math.max(1,Math.round(s/d)):s;_===this.simWidth&&S===this.simHeight||(this.simWidth=_,this.simHeight=S,this.recreateState())}setTextureSource(e){if(e===this.textureSource||(this.textureSource=e,this.textureImage=void 0,this.textureReady=!1,this.textureNeedsUpload=!1,!e))return;if(typeof e!="string"){this.textureImage=e,this.textureNeedsUpload=!0;return}const n=new Image;n.crossOrigin="anonymous",n.onload=()=>{this.textureSource===e&&(this.textureImage=n,this.textureNeedsUpload=!0,this.requestRender())},n.onerror=()=>{this.textureSource===e&&(this.textureImage=void 0,this.textureReady=!1,this.textureNeedsUpload=!1)},n.src=e}step(e,n,t){if(!this.stateTextures||!this.framebuffers)return;const i=this.gl,a=1-this.readIndex;i.useProgram(this.simulationProgram.program),i.bindFramebuffer(i.FRAMEBUFFER,this.framebuffers[a]),i.viewport(0,0,this.simWidth,this.simHeight),i.activeTexture(i.TEXTURE0),i.bindTexture(i.TEXTURE_2D,this.stateTextures[this.readIndex]),this.prepareQuad(this.simulationProgram),this.setSimulationUniforms(e,n,t),i.drawArrays(i.TRIANGLE_STRIP,0,4),i.bindFramebuffer(i.FRAMEBUFFER,null),this.readIndex=a}render(e,n=0){const t=this.gl;this.stateTextures&&(e.texture?this.setTextureSource(e.texture):this.setTextureSource(void 0),this.uploadTextureIfNeeded(e),t.useProgram(this.materialProgram.program),t.bindFramebuffer(t.FRAMEBUFFER,null),t.viewport(0,0,this.canvas.width,this.canvas.height),t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT),!(e.mode!=="dom"&&e.texture&&!this.textureReady)&&(t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.stateTextures[this.readIndex]),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this.backgroundTexture),t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,this.causticTexture),this.prepareQuad(this.materialProgram),this.setMaterialUniforms(e,n),t.drawArrays(t.TRIANGLE_STRIP,0,4)))}hasVisibleActivity(){if(!this.framebuffers)return!1;const e=this.gl,n=this.framebuffers[this.readIndex],t=this.simWidth*this.simHeight*4;(!this.activityPixels||this.activityPixels.length!==t)&&(this.activityPixels=new Float32Array(t)),e.bindFramebuffer(e.FRAMEBUFFER,n);try{e.readPixels(0,0,this.simWidth,this.simHeight,e.RGBA,e.FLOAT,this.activityPixels)}finally{e.bindFramebuffer(e.FRAMEBUFFER,null)}return rt(this.activityPixels,this.simWidth,this.simHeight)}clear(){const e=this.gl;if(this.framebuffers){e.viewport(0,0,this.simWidth,this.simHeight);for(const n of this.framebuffers)e.bindFramebuffer(e.FRAMEBUFFER,n),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT)}e.bindFramebuffer(e.FRAMEBUFFER,null),e.viewport(0,0,this.canvas.width,this.canvas.height),e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT)}dispose(){var n,t;const e=this.gl;(n=this.stateTextures)==null||n.forEach(i=>e.deleteTexture(i)),(t=this.framebuffers)==null||t.forEach(i=>e.deleteFramebuffer(i)),e.deleteTexture(this.backgroundTexture),e.deleteTexture(this.causticTexture),e.deleteBuffer(this.quadBuffer),e.deleteProgram(this.simulationProgram.program),e.deleteProgram(this.materialProgram.program)}recreateState(){var l,c;const e=this.gl;(l=this.stateTextures)==null||l.forEach(s=>e.deleteTexture(s)),(c=this.framebuffers)==null||c.forEach(s=>e.deleteFramebuffer(s));const n=this.createStateTexture(),t=this.createStateTexture(),i=this.createFramebuffer(n),a=this.createFramebuffer(t);this.stateTextures=[n,t],this.framebuffers=[i,a],this.readIndex=0,this.clear()}createStateTexture(){const e=this.gl,n=q(e.createTexture(),"WebGL texture");if(e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,this.canLinearFloat?e.LINEAR:e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,this.canLinearFloat?e.LINEAR:e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),this.isWebGL2){const t=e;t.texImage2D(t.TEXTURE_2D,0,t.RGBA32F,this.simWidth,this.simHeight,0,t.RGBA,t.FLOAT,null)}else e.texImage2D(e.TEXTURE_2D,0,e.RGBA,this.simWidth,this.simHeight,0,e.RGBA,e.FLOAT,null);return n}createFramebuffer(e){const n=this.gl,t=q(n.createFramebuffer(),"WebGL framebuffer");if(n.bindFramebuffer(n.FRAMEBUFFER,t),n.framebufferTexture2D(n.FRAMEBUFFER,n.COLOR_ATTACHMENT0,n.TEXTURE_2D,e,0),n.checkFramebufferStatus(n.FRAMEBUFFER)!==n.FRAMEBUFFER_COMPLETE)throw new Error("WaterDistortion could not create a float simulation target.");return n.bindFramebuffer(n.FRAMEBUFFER,null),t}initializeBackgroundTexture(){const e=this.gl;e.bindTexture(e.TEXTURE_2D,this.backgroundTexture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,new Uint8Array([18,28,26,255]))}initializeCausticTexture(){const e=this.gl;e.bindTexture(e.TEXTURE_2D,this.causticTexture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.REPEAT),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.REPEAT),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,C,C,0,e.RGBA,e.UNSIGNED_BYTE,Je()),e.generateMipmap(e.TEXTURE_2D)}uploadTextureIfNeeded(e){if(e.mode==="dom"||!this.textureImage||!this.textureNeedsUpload&&!tt(this.textureImage))return;const n=this.gl;try{n.bindTexture(n.TEXTURE_2D,this.backgroundTexture),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,1),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,this.textureImage),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,0),this.textureReady=!0,this.textureNeedsUpload=!1}catch{this.textureReady=!1,this.textureNeedsUpload=!1}}prepareQuad(e){const n=this.gl;n.bindBuffer(n.ARRAY_BUFFER,this.quadBuffer),n.enableVertexAttribArray(e.position),n.vertexAttribPointer(e.position,2,n.FLOAT,!1,0,0)}setSimulationUniforms(e,n,t){const i=this.gl,a=this.simulationProgram.program,l=Math.min(e.length,Z),c=this.wakeAUniforms,s=this.wakeBUniforms,d=this.wakeShapeUniforms,_=Math.max(0,e.length-Z);c.fill(0),s.fill(0),d.fill(0);for(let S=0;S<l;S+=1){const g=e[_+S],x=S*4;c[x]=g.ax,c[x+1]=g.ay,c[x+2]=g.radius,c[x+3]=g.wakeStrength,s[x]=g.bx,s[x+1]=g.by,s[x+2]=g.wakeLength,s[x+3]=g.velocity,d[x]=g.troughStrength,d[x+1]=g.ridgeStrength,d[x+2]=g.ridgeOffset}i.uniform1i(p(i,a,"u_state"),0),i.uniform2f(p(i,a,"u_texel"),1/this.simWidth,1/this.simHeight),i.uniform1f(p(i,a,"u_delta"),t),i.uniform1f(p(i,a,"u_damping"),n.damping),i.uniform1f(p(i,a,"u_stiffness"),n.waveSpeed),i.uniform1f(p(i,a,"u_aspect"),this.cssWidth/this.cssHeight),i.uniform1i(p(i,a,"u_wakeCount"),l),i.uniform4fv(p(i,a,"u_wakeA[0]"),c),i.uniform4fv(p(i,a,"u_wakeB[0]"),s),i.uniform4fv(p(i,a,"u_wakeShape[0]"),d)}setMaterialUniforms(e,n){const t=this.gl,i=this.materialProgram.program,a=e.mode!=="dom";t.uniform1i(p(t,i,"u_state"),0),t.uniform1i(p(t,i,"u_background"),1),t.uniform1i(p(t,i,"u_causticMap"),2),t.uniform2f(p(t,i,"u_texel"),1/this.simWidth,1/this.simHeight),t.uniform1f(p(t,i,"u_domMode"),e.mode==="dom"?1:0),t.uniform1f(p(t,i,"u_textureEnabled"),a&&this.textureReady?1:0),t.uniform1f(p(t,i,"u_normalScale"),e.normalScale),t.uniform1f(p(t,i,"u_refractionStrength"),e.refractionStrength),t.uniform1f(p(t,i,"u_specularIntensity"),e.specularIntensity),t.uniform1f(p(t,i,"u_crestIntensity"),e.crestIntensity),t.uniform1f(p(t,i,"u_causticIntensity"),e.causticIntensity),t.uniform1f(p(t,i,"u_time"),n%1e3)}}function Je(){const r=new Uint8Array(C*C*4);for(let e=0;e<C;e+=1)for(let n=0;n<C;n+=1){const t=(n+.5)/C,i=(e+.5)/C,a=Math.round(ce(t,i,0)*255),l=Math.round(ce(t,i,11)*255),c=Math.round(ce(t,i,23)*255),s=Math.round(ce(t,i,37)*255),d=(e*C+n)*4;r[d]=a,r[d+1]=l,r[d+2]=c,r[d+3]=s}return r}function ce(r,e,n){const t=Math.sin(se*(e*2+n*.013))*.12+Math.sin(se*(r*3+e+n*.021))*.06,i=Math.cos(se*(r*2+n*.017))*.12+Math.sin(se*(e*3-r+n*.019))*.06,a=r*oe+t,l=e*oe+i,c=Math.floor(a),s=Math.floor(l);let d=Number.POSITIVE_INFINITY,_=Number.POSITIVE_INFINITY,S=0,g=0;for(let P=-1;P<=1;P+=1)for(let D=-1;D<=1;D+=1){const k=c+D,B=s+P,M=Ae(k,oe),I=Ae(B,oe),z=k+.16+fe(M,I,n)*.68,N=B+.16+fe(M,I,n+1)*.68,U=Math.hypot(a-z,l-N);U<d?(_=d,d=U,S=M,g=I):U<_&&(_=U)}const x=_-d,A=1-Ie(.025,.13,x),w=1-Ie(.08,.34,x),W=.74+fe(S,g,n+2)*.26,H=A*A*.58+w*.34;return Ce(Math.pow(H*W,1.12))}function fe(r,e,n){const t=Math.sin(r*127.1+e*311.7+n*74.7)*43758.5453123;return t-Math.floor(t)}function Ae(r,e){return(r%e+e)%e}function Ie(r,e,n){const t=Ce((n-r)/(e-r));return t*t*(3-t*2)}function Ce(r){return Math.min(1,Math.max(0,r))}function et(r){const e={alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!1,preserveDrawingBuffer:!1},n=r.getContext("webgl2",e);if(n){if(!n.getExtension("EXT_color_buffer_float"))throw new Error("WaterDistortion requires float render targets.");return{gl:n,isWebGL2:!0,canLinearFloat:!!n.getExtension("OES_texture_float_linear")}}const t=r.getContext("webgl",e)||r.getContext("experimental-webgl",e);if(!t)throw new Error("WaterDistortion requires WebGL.");if(!t.getExtension("OES_texture_float")||!t.getExtension("WEBGL_color_buffer_float")&&!t.getExtension("EXT_color_buffer_float"))throw new Error("WaterDistortion requires float texture support.");return{gl:t,isWebGL2:!1,canLinearFloat:!!t.getExtension("OES_texture_float_linear")}}function Fe(r,e,n){const t=Le(r,r.VERTEX_SHADER,e),i=Le(r,r.FRAGMENT_SHADER,n),a=q(r.createProgram(),"WebGL program");if(r.attachShader(a,t),r.attachShader(a,i),r.linkProgram(a),r.deleteShader(t),r.deleteShader(i),!r.getProgramParameter(a,r.LINK_STATUS)){const c=r.getProgramInfoLog(a)||"Unknown shader link error.";throw r.deleteProgram(a),new Error(c)}const l=r.getAttribLocation(a,"a_position");if(l<0)throw new Error("WaterDistortion could not bind its quad attribute.");return{program:a,position:l}}function Le(r,e,n){const t=q(r.createShader(e),"WebGL shader");if(r.shaderSource(t,n),r.compileShader(t),!r.getShaderParameter(t,r.COMPILE_STATUS)){const i=r.getShaderInfoLog(t)||"Unknown shader compile error.";throw r.deleteShader(t),new Error(i)}return t}function p(r,e,n){const t=r.getUniformLocation(e,n);if(!t)throw new Error(`WaterDistortion could not bind uniform ${n}.`);return t}function q(r,e){if(!r)throw new Error(`WaterDistortion could not create ${e}.`);return r}function tt(r){return typeof HTMLCanvasElement<"u"&&r instanceof HTMLCanvasElement||typeof HTMLVideoElement<"u"&&r instanceof HTMLVideoElement||typeof OffscreenCanvas<"u"&&r instanceof OffscreenCanvas}function rt(r,e,n,t=je){const i=Math.max(0,Math.floor(e)),a=Math.max(0,Math.floor(n));if(i===0||a===0)return!1;const l=(c,s)=>r[(s*i+c)*4]??0;for(let c=0;c<a;c+=1)for(let s=0;s<i;s+=1){const d=(c*i+s)*4,_=r[d]??0,S=r[d+1]??0;if(_<-t.trough||Math.abs(S)>t.velocity)return!0;const g=s>0?l(s-1,c):_,x=s<i-1?l(s+1,c):_,A=c>0?l(s,c-1):_,w=c<a-1?l(s,c+1):_,W=Math.hypot(x-g,w-A),H=Math.abs(g+x+A+w-_*4);if(W>t.gradient||H>t.curvature)return!0}return!1}const L={width:1,height:1,interactionRadius:25,wakeStrength:.54,troughStrength:.92,ridgeStrength:.46,ridgeOffset:10,wakeLength:84,velocityScale:1,velocityClamp:1.35,cursorSmoothing:.2,segmentSpacing:18},nt=24;function me(r){return{width:Math.max(1,r.width??L.width),height:Math.max(1,r.height??L.height),interactionRadius:Math.max(1,r.interactionRadius??L.interactionRadius),wakeStrength:Math.max(0,r.wakeStrength??L.wakeStrength),troughStrength:Math.max(0,r.troughStrength??L.troughStrength),ridgeStrength:Math.max(0,r.ridgeStrength??L.ridgeStrength),ridgeOffset:Math.max(0,r.ridgeOffset??L.ridgeOffset),wakeLength:Math.max(1,r.wakeLength??L.wakeLength),velocityScale:Math.max(.01,r.velocityScale??L.velocityScale),velocityClamp:Math.max(.05,r.velocityClamp??L.velocityClamp),cursorSmoothing:h(r.cursorSmoothing??L.cursorSmoothing,0,.9),segmentSpacing:Math.max(1,r.segmentSpacing??L.segmentSpacing)}}function ke(r,e,n=r.timeStamp){const t=typeof r.getCoalescedEvents=="function"?r.getCoalescedEvents():[],i=t.length>0?t:[r],a=n-r.timeStamp;return i.map(l=>({x:l.clientX-e.left,y:l.clientY-e.top,time:l.timeStamp+a}))}class it{constructor(e={}){m(this,"options");m(this,"previous");m(this,"smoothedVelocity",0);m(this,"disturbances",[]);this.options=me(e)}configure(e){this.options=me({...this.options,...e})}reset(){this.previous=void 0,this.smoothedVelocity=0,this.disturbances.length=0}addSample(e){this.disturbances.length=0;const n=this.clampSample(e),t=this.previous;if(!t)return this.previous=n,this.disturbances;n.time<=t.time&&(n.time=t.time+.01);const i=this.smoothSample(t,n),a=Ve(t,i);if(a<.35)return this.previous=i,this.disturbances;const l=a/Math.max(.01,i.time-t.time),c=h(1-this.options.cursorSmoothing,.12,1);this.smoothedVelocity=O(this.smoothedVelocity,l,c),this.previous=i;const s=Math.min(nt,Math.max(1,Math.ceil(a/this.options.segmentSpacing)));for(let d=0;d<s;d+=1){const _=d/s,S=(d+1)/s;this.disturbances.push(ge(De(t,i,_),De(t,i,S),this.smoothedVelocity,this.options))}return this.disturbances}clampSample(e){return{x:h(e.x,0,this.options.width),y:h(e.y,0,this.options.height),time:e.time}}smoothSample(e,n){const t=1-this.options.cursorSmoothing;return{x:O(e.x,n.x,t),y:O(e.y,n.y,t),time:n.time}}}function ge(r,e,n,t){const i=me(t),a=h(n*i.velocityScale/i.velocityClamp,0,1),l=h(r.y/i.height,0,1),c=h(e.y/i.height,0,1),s=i.wakeLength*O(.55,1.35,a);return{ax:h(r.x/i.width,0,1),ay:1-l,bx:h(e.x/i.width,0,1),by:1-c,radius:i.interactionRadius/i.height,wakeStrength:i.wakeStrength*O(.18,1,a),troughStrength:i.troughStrength,ridgeStrength:i.ridgeStrength,ridgeOffset:i.ridgeOffset/i.height,wakeLength:s/i.height,velocity:a}}function De(r,e,n){return{x:O(r.x,e.x,n),y:O(r.y,e.y,n),time:O(r.time,e.time,n)}}function at(r){const e=h(r,.85,.998),n=Math.ceil(Math.log(.012)/Math.log(e));return h(n*(1e3/60),900,5200)}const E={mode:"texture",damping:.98,waveSpeed:.4,normalScale:20,refractionStrength:.7,specularIntensity:1,crestIntensity:.2,causticIntensity:0,simulationResolution:192,wakeStrength:.54,interactionRadius:25,troughStrength:.92,ridgeStrength:.46,ridgeOffset:10,wakeLength:84,velocityScale:1,velocityClamp:1.35,cursorSmoothing:.2,segmentSpacing:18,idleTimeout:900,interactionMode:"move",runInBackground:!1,pauseKey:void 0},G=1e3/60,ot=4,Ue=360,Pe=24,st=200;function ct(r){return r instanceof Element&&r.closest("a, button, input, select, textarea, [contenteditable]:not([contenteditable=false])")!==null}function ut(r){const e=r.strength===void 0?void 0:r.strength/48;return{mode:r.mode??E.mode,texture:r.texture,damping:h(r.damping??r.dissipation??E.damping,.85,.998),waveSpeed:h(r.waveSpeed??E.waveSpeed,.05,1),normalScale:h(r.normalScale??E.normalScale,1,80),refractionStrength:h(r.refractionStrength??E.refractionStrength,0,2.5),specularIntensity:h(r.specularIntensity??E.specularIntensity,0,3),crestIntensity:h(r.crestIntensity??E.crestIntensity,0,3),causticIntensity:h(r.causticIntensity??E.causticIntensity,0,3),simulationResolution:h(Math.round(r.simulationResolution??E.simulationResolution),64,384),wakeStrength:h(r.wakeStrength??r.rippleStrength??e??E.wakeStrength,0,3),interactionRadius:h(r.interactionRadius??E.interactionRadius,2,80),troughStrength:h(r.troughStrength??E.troughStrength,0,3),ridgeStrength:h(r.ridgeStrength??E.ridgeStrength,0,3),ridgeOffset:h(r.ridgeOffset??E.ridgeOffset,0,80),wakeLength:h(r.wakeLength??E.wakeLength,1,240),velocityScale:h(r.velocityScale??E.velocityScale,.01,4),velocityClamp:h(r.velocityClamp??E.velocityClamp,.05,8),cursorSmoothing:h(r.cursorSmoothing??E.cursorSmoothing,0,.9),segmentSpacing:h(r.segmentSpacing??E.segmentSpacing,1,96),idleTimeout:Math.max(120,r.idleTimeout??E.idleTimeout),interactionMode:r.interactionMode??E.interactionMode,runInBackground:r.runInBackground??E.runInBackground,pauseKey:r.pauseKey||E.pauseKey}}function Be(r,e,n){const t=h(r.x,0,1)*n.width,i=h(r.y,0,1)*n.height,a=Math.max(.5,e.interactionRadius*.08),l=e.velocityClamp/e.velocityScale,c={...e,width:n.width,height:n.height,wakeLength:e.interactionRadius*1.5};return[ge({x:t-a,y:i},{x:t+a,y:i},l,c),ge({x:t,y:i-a},{x:t,y:i+a},l,c)]}function lt(r){const[e,n]=f.useState(!1);return f.useEffect(()=>{if(!r||typeof window>"u"||!window.matchMedia){n(!1);return}const t=window.matchMedia("(prefers-reduced-motion: reduce)");n(t.matches);const i=a=>{n(a.matches)};return t.addEventListener("change",i),()=>{t.removeEventListener("change",i)}},[r]),e}const We=f.forwardRef(function(e,n){const{underlay:t,children:i,foreground:a,className:l,style:c,reducedMotion:s="respect"}=e,d=f.useRef(null),_=f.useRef(null),S=f.useRef(null),g=f.useRef(new it),x=f.useRef(null),A=f.useRef(null),w=f.useRef(()=>{}),W=f.useRef(()=>{}),H=f.useRef(()=>{}),P=f.useRef([]),D=f.useRef(!0),k=f.useRef(!0),B=f.useRef(!1),M=f.useRef(!1),I=f.useRef(null),z=f.useRef(0),N=f.useRef(0),U=f.useRef(0),y=ut(e),v=f.useRef(y),V=f.useRef({width:1,height:1}),Ne=lt(s==="respect"),ue=s==="disable"||s==="respect"&&Ne,Xe=a??i;v.current=y,f.useImperativeHandle(n,()=>({triggerRipple(T){B.current||M.current||H.current(Be(T,v.current,V.current))}}),[]),f.useEffect(()=>{var T;B.current=ue,ue&&(g.current.reset(),I.current=null,P.current=[],z.current=0,N.current=0,U.current=0,(T=S.current)==null||T.clear()),w.current()},[ue]),f.useEffect(()=>{const T=S.current;if(!T)return;const{width:K,height:b}=V.current;T.resize(K,b,window.devicePixelRatio,y.simulationResolution),T.setTextureSource(y.texture),w.current()},[y.mode,y.texture,y.damping,y.waveSpeed,y.normalScale,y.refractionStrength,y.specularIntensity,y.crestIntensity,y.causticIntensity,y.simulationResolution]),f.useEffect(()=>{const T=d.current,K=_.current;if(!T||!K)return;k.current=typeof document.hasFocus=="function"?document.hasFocus():!0;let b=null,ne,J=G,ve=!1,ee;const Y=()=>{x.current!==null&&(window.cancelAnimationFrame(x.current),x.current=null),A.current!==null&&(window.clearTimeout(A.current),A.current=null)},Oe=(o,u)=>{g.current.configure({width:o,height:u,interactionRadius:v.current.interactionRadius,wakeStrength:v.current.wakeStrength,troughStrength:v.current.troughStrength,ridgeStrength:v.current.ridgeStrength,ridgeOffset:v.current.ridgeOffset,wakeLength:v.current.wakeLength,velocityScale:v.current.velocityScale,velocityClamp:v.current.velocityClamp,cursorSmoothing:v.current.cursorSmoothing,segmentSpacing:v.current.segmentSpacing})},He=(o,u)=>{const F=Math.max(1,o),R=Math.max(1,u);V.current={width:F,height:R},Oe(F,R),b==null||b.resize(F,R,window.devicePixelRatio,v.current.simulationResolution)},xe=()=>{b==null||b.render(v.current,performance.now()*.001)},X=()=>{ne=void 0,J=G},te=()=>{Y(),g.current.reset(),I.current=null,X(),ee===void 0&&(ee=performance.now())},j=()=>{const o=v.current;let u=!1;if(!(!B.current&&!M.current&&D.current&&(o.runInBackground||!document.hidden&&k.current))){X();return}if(ee!==void 0){const R=performance.now()-ee;z.current+=R,N.current+=R,U.current+=R,ee=void 0,u=!0}(!o.runInBackground||u)&&X(),xe(),w.current()},Ee=o=>{x.current=null;const u=v.current;if(!b||M.current||!D.current||!u.runInBackground&&(document.hidden||!k.current)){X();return}if(B.current){P.current=[],b.render(u,o*.001),X();return}const F=u.runInBackground?Ue:ot,R=ne===void 0?G:h(o-ne,0,u.runInBackground?G*Ue:64);ne=o,J=Math.min(J+R,G*F);let Q=P.current.splice(0),Me=0;for(;J>=G&&Me<F;)b.step(Q,u,1),Q=[],J-=G,Me+=1;if(b.render(u,o*.001),P.current.length>0){w.current();return}if(o<z.current){w.current();return}if(ve){o<N.current?w.current():X();return}if(o<U.current){w.current();return}try{b.hasVisibleActivity()?(U.current=o+st,w.current()):X()}catch{ve=!0,o<N.current?w.current():X()}},le=()=>{M.current||x.current!==null||A.current!==null||(v.current.runInBackground&&document.hidden?A.current=window.setTimeout(()=>{A.current=null,Ee(performance.now())},G):x.current=window.requestAnimationFrame(Ee))};w.current=le;try{b=new Ze(K,()=>{w.current()}),S.current=b,b.setTextureSource(v.current.texture)}catch{return K.style.display="none",S.current=null,w.current=()=>{},()=>{}}const ie=()=>{const o=K.getBoundingClientRect();return He(o.width,o.height),o},ze=()=>{ie(),xe()},he=o=>{if(M.current||o.length===0)return;const u=P.current;for(const Q of o)u.push(Q);u.length>Pe&&u.splice(0,u.length-Pe);const R=performance.now()+v.current.idleTimeout;z.current=Math.max(z.current,R),N.current=Math.max(N.current,R+at(v.current.damping)),U.current=0,le()};H.current=he;const pe=o=>{const u=v.current;if(B.current||M.current||!D.current||u.interactionMode==="event"||!u.runInBackground&&(document.hidden||!k.current)||o.pointerType==="touch"&&!o.isPrimary)return;g.current.reset();const F=ie(),[R]=ke(o,F,performance.now());R&&((u.interactionMode==="click"||u.interactionMode==="drag")&&he(Be({x:R.x/V.current.width,y:R.y/V.current.height},u,V.current)),u.interactionMode==="drag"?(I.current=o.pointerId,g.current.addSample(R)):u.interactionMode==="move"&&g.current.addSample(R))},_e=o=>{const u=v.current;if(B.current||M.current||!D.current||u.interactionMode!=="move"&&(u.interactionMode!=="drag"||I.current!==o.pointerId)||!u.runInBackground&&(document.hidden||!k.current)||o.pointerType==="touch"&&!o.isPrimary)return;const F=ie(),R=ke(o,F,performance.now());for(const Q of R)he(g.current.addSample(Q))},$=o=>{I.current!==null&&I.current!==o.pointerId||(I.current=null,g.current.reset())},Se=o=>{const u=v.current.pauseKey;!u||o.code!==u||o.repeat||o.altKey||o.ctrlKey||o.metaKey||ct(o.target)||(o.preventDefault(),M.current=!M.current,M.current?te():(Y(),j()))},Te=()=>{Y(),document.hidden&&!v.current.runInBackground?te():j()},Re=()=>{k.current=!1,v.current.runInBackground?(Y(),j()):te()},ye=()=>{k.current=!0,Y(),j()},Ke=()=>{Y(),!v.current.runInBackground&&(document.hidden||!k.current)?te():j()};W.current=Ke,ze();const we=new ResizeObserver(()=>{ie(),le()});we.observe(K);let re;return"IntersectionObserver"in window&&(re=new IntersectionObserver(o=>{const u=o[0];D.current=u?u.isIntersecting:!0,D.current?j():te()}),re.observe(T)),T.addEventListener("pointerdown",pe,{passive:!0}),T.addEventListener("pointermove",_e,{passive:!0}),T.addEventListener("pointerup",$,{passive:!0}),T.addEventListener("pointerleave",$,{passive:!0}),T.addEventListener("pointercancel",$,{passive:!0}),window.addEventListener("blur",Re),window.addEventListener("focus",ye),document.addEventListener("keydown",Se),document.addEventListener("visibilitychange",Te),()=>{var o;Y(),we.disconnect(),re==null||re.disconnect(),T.removeEventListener("pointerdown",pe),T.removeEventListener("pointermove",_e),T.removeEventListener("pointerup",$),T.removeEventListener("pointerleave",$),T.removeEventListener("pointercancel",$),window.removeEventListener("blur",Re),window.removeEventListener("focus",ye),document.removeEventListener("keydown",Se),document.removeEventListener("visibilitychange",Te),(o=S.current)==null||o.dispose(),S.current=null,w.current=()=>{},W.current=()=>{},H.current=()=>{}}},[]),f.useEffect(()=>{g.current.reset(),I.current=null},[y.interactionMode]),f.useEffect(()=>{W.current()},[y.runInBackground]),f.useEffect(()=>{!y.pauseKey&&M.current&&(M.current=!1,W.current())},[y.pauseKey]);const Ge={...c,position:"relative",overflow:"hidden",isolation:"isolate"};return ae.jsxs("div",{ref:d,className:l,style:Ge,children:[t?ae.jsx("div",{style:{position:"absolute",inset:0,zIndex:0,pointerEvents:"none",transform:"translateZ(0)"},children:t}):null,ae.jsx("canvas",{ref:_,"aria-hidden":"true",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",pointerEvents:"none",mixBlendMode:y.mode==="dom"?"screen":"normal"}}),ae.jsx("div",{style:{position:"relative",zIndex:2},children:Xe})]})}),ht=We;exports.WaterDistortion=We;exports.WaterLayer=ht;
|
|
251
251
|
//# sourceMappingURL=water-distortion.cjs.map
|