@wave3d/core 0.1.0 → 0.2.1
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 +30 -18
- package/dist/index.d.ts +2 -2
- package/dist/renderer/WaveRenderer.d.ts +3 -3
- package/dist/renderer/WaveRenderer.js +21 -10
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/shell/createWave.d.ts +16 -1
- package/dist/shell/createWave.js +5 -0
- package/dist/shell/createWave.js.map +1 -1
- package/dist/standalone/wave3d.standalone.js +618 -633
- package/dist/standalone.d.ts +2 -2
- package/dist/standalone.js.map +1 -1
- package/package.json +1 -1
- package/skills/wave3d/SKILL.md +7 -4
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# @wave3d/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The framework-agnostic renderer and config model behind [Wave Studio](https://wave-studio.pages.dev): glossy, twisting 3D gradient waves driven by one JSON config.
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A wave is a strip swept along a curve, with a gradient running down its length and a satin sheen. The renderer and every export read from the same `WaveConfig`, so what you design is exactly what ships.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> 💡 Fastest way in: design a wave in **[Wave Studio](https://wave-studio.pages.dev)**, hit **⟨⟩ Export code**, and copy the snippet. This package is what that snippet imports.
|
|
8
|
+
|
|
9
|
+
## 📦 Install
|
|
8
10
|
|
|
9
11
|
```sh
|
|
10
12
|
pnpm add @wave3d/core three # three is a peer dependency
|
|
@@ -12,9 +14,9 @@ pnpm add @wave3d/core three # three is a peer dependency
|
|
|
12
14
|
|
|
13
15
|
`three` is a peer dependency (`>=0.180 <1`); add `@types/three` for TypeScript.
|
|
14
16
|
|
|
15
|
-
## Drop-in wave
|
|
17
|
+
## Drop-in wave
|
|
16
18
|
|
|
17
|
-
The
|
|
19
|
+
The default entry is poster-first: it shows a poster immediately, then upgrades to live WebGL only when the browser can run it. It falls back to the poster on no-WebGL, Save-Data, reduced motion, or a lost context, and `three.js` is code-split out of the initial load.
|
|
18
20
|
|
|
19
21
|
```ts
|
|
20
22
|
import { createWave } from "@wave3d/core";
|
|
@@ -22,38 +24,48 @@ import { createWave } from "@wave3d/core";
|
|
|
22
24
|
const handle = createWave(document.getElementById("wave"), config, {
|
|
23
25
|
poster: "/wave.png",
|
|
24
26
|
});
|
|
25
|
-
// handle.set(nextConfig) · handle.pause() · handle.play() · handle.destroy()
|
|
27
|
+
// handle.set(nextConfig) · handle.pause() · handle.play() · handle.snapshot() · handle.destroy()
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
Capture the live frame as an image you can host and reuse as the poster for reduced-motion, no-WebGL, and Save-Data visitors:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const blob = await handle.snapshot(); // resolves null until running. options: { type?, quality?, transparent? }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Single `<script>` from a CDN
|
|
37
|
+
|
|
38
|
+
`three` is bundled into the standalone build, so this needs nothing else:
|
|
29
39
|
|
|
30
40
|
```html
|
|
31
41
|
<script type="module">
|
|
32
42
|
import { mountWave } from "https://esm.sh/@wave3d/core/standalone";
|
|
33
43
|
mountWave(document.getElementById("wave"), {
|
|
34
|
-
/* config */
|
|
44
|
+
/* your exported config */
|
|
35
45
|
});
|
|
36
46
|
</script>
|
|
37
47
|
```
|
|
38
48
|
|
|
39
49
|
## Entry points
|
|
40
50
|
|
|
41
|
-
| Import | What
|
|
42
|
-
| ------------------------- |
|
|
43
|
-
| `@wave3d/core` | poster-first shell (`createWave
|
|
44
|
-
| `@wave3d/core/renderer` | the raw `WaveRenderer`
|
|
45
|
-
| `@wave3d/core/presets` | the built-in `PRESETS`
|
|
46
|
-
| `@wave3d/core/studio` | `StudioWaveRenderer` + randomizers
|
|
47
|
-
| `@wave3d/core/standalone` | single-file build with three bundled (the CDN entry)
|
|
51
|
+
| Import | What |
|
|
52
|
+
| ------------------------- | -------------------------------------------------------------- |
|
|
53
|
+
| `@wave3d/core` | poster-first shell (`createWave` / `mountWave`) + config model |
|
|
54
|
+
| `@wave3d/core/renderer` | the raw `WaveRenderer` and `WaveGeometry` (static three) |
|
|
55
|
+
| `@wave3d/core/presets` | the built-in `PRESETS` |
|
|
56
|
+
| `@wave3d/core/studio` | `StudioWaveRenderer` + randomizers |
|
|
57
|
+
| `@wave3d/core/standalone` | single-file build with three bundled (the CDN entry) |
|
|
48
58
|
|
|
49
59
|
## Framework wrappers
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
Prefer a component? Reach for the wrapper for your framework:
|
|
62
|
+
|
|
63
|
+
- **React** → [`@wave3d/react`](https://www.npmjs.com/package/@wave3d/react): the `<Wave3D>` component.
|
|
64
|
+
- **Vue, Svelte, or plain HTML** → [`@wave3d/element`](https://www.npmjs.com/package/@wave3d/element): the `<wave-3d>` custom element.
|
|
53
65
|
|
|
54
66
|
## Credits
|
|
55
67
|
|
|
56
|
-
|
|
68
|
+
Built by [Amir Abushanab](https://github.com/Amir-Abushanab).
|
|
57
69
|
|
|
58
70
|
## License
|
|
59
71
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, normalizeBackground, normalizeWave, resizeWaves } from "./config/model.js";
|
|
2
2
|
import { WaveRenderer, WaveRendererOptions } from "./renderer/WaveRenderer.js";
|
|
3
|
-
import { FallbackReason, WaveHandle, WaveOptions, WaveState, createWave, mountWave } from "./shell/createWave.js";
|
|
4
|
-
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, type WaveOptions, type WaveRenderer, type WaveRendererOptions, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
|
|
3
|
+
import { FallbackReason, SnapshotOptions, WaveHandle, WaveOptions, WaveState, createWave, mountWave } from "./shell/createWave.js";
|
|
4
|
+
export { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, ColorStop, DEFAULT_LIGHT_POSITION, type FallbackReason, GradientType, LightConfig, MAX_COLORS, MAX_LIGHTS, MAX_MESH_POINTS, MAX_NOISE_BANDS, MAX_WAVES, MeshGradientPoint, NoiseBand, PaletteSource, SceneConfig, type SnapshotOptions, StudioConfig, Vec2, Vec3, WaveConfig, type WaveHandle, type WaveOptions, type WaveRenderer, type WaveRendererOptions, type WaveState, createDefaultConfig, createDefaultMeshPoints, createLight, createNoiseBand, createWave, ensureCamera, ensureSceneDefaults, ensureStudioConfig, makeStops, makeWave, makeWaveSpread, mountWave, normalizeBackground, normalizeWave, resizeWaves };
|
|
@@ -91,7 +91,7 @@ declare class WaveRenderer {
|
|
|
91
91
|
private readonly clipSphere;
|
|
92
92
|
private readonly clipTmpA;
|
|
93
93
|
private readonly clipTmpB;
|
|
94
|
-
private readonly
|
|
94
|
+
private readonly timer;
|
|
95
95
|
private time;
|
|
96
96
|
private rafId;
|
|
97
97
|
protected running: boolean;
|
|
@@ -174,7 +174,7 @@ declare class WaveRenderer {
|
|
|
174
174
|
private onVisibilityChange;
|
|
175
175
|
private updateRunning;
|
|
176
176
|
private loop;
|
|
177
|
-
/** Advance the per-frame
|
|
177
|
+
/** Advance the per-frame time uniforms (geometry itself is static). Time model:
|
|
178
178
|
* time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */
|
|
179
179
|
private updateTime;
|
|
180
180
|
/** Render exactly one frame at the current time. */
|
|
@@ -220,7 +220,7 @@ declare class WaveRenderer {
|
|
|
220
220
|
* of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed
|
|
221
221
|
* off-frame. Camera-relative: it's "left on screen" no matter how the view is rotated/zoomed,
|
|
222
222
|
* because it's built from the camera's right/up axes and the frame's visible world size. */
|
|
223
|
-
captureImage(mime: string, transparent?: boolean, quality?: number): Promise<Blob>;
|
|
223
|
+
captureImage(mime: string, transparent?: boolean, quality?: number, time?: number): Promise<Blob>;
|
|
224
224
|
captureStream(fps?: number): MediaStream;
|
|
225
225
|
get canvas(): HTMLCanvasElement;
|
|
226
226
|
getConfig(): StudioConfig;
|
|
@@ -169,7 +169,7 @@ var WaveRenderer = class {
|
|
|
169
169
|
clipSphere = new THREE.Sphere();
|
|
170
170
|
clipTmpA = new THREE.Vector3();
|
|
171
171
|
clipTmpB = new THREE.Vector3();
|
|
172
|
-
|
|
172
|
+
timer = new THREE.Timer();
|
|
173
173
|
time = 0;
|
|
174
174
|
rafId = 0;
|
|
175
175
|
running = false;
|
|
@@ -925,8 +925,7 @@ var WaveRenderer = class {
|
|
|
925
925
|
const shouldAnimate = this.started && this.visible && this.pageVisible && !this.config.paused && !this.reducedMotion;
|
|
926
926
|
if (shouldAnimate && !this.running) {
|
|
927
927
|
this.running = true;
|
|
928
|
-
this.
|
|
929
|
-
this.clock.getDelta();
|
|
928
|
+
this.timer.update();
|
|
930
929
|
this.rafId = requestAnimationFrame(this.loop);
|
|
931
930
|
} else if (!shouldAnimate && this.running) {
|
|
932
931
|
this.running = false;
|
|
@@ -940,12 +939,13 @@ var WaveRenderer = class {
|
|
|
940
939
|
}
|
|
941
940
|
loop = () => {
|
|
942
941
|
if (!this.running) return;
|
|
943
|
-
this.
|
|
942
|
+
this.timer.update();
|
|
943
|
+
this.time += this.timer.getDelta();
|
|
944
944
|
if (this.introTimeRamp < 1) this.introTimeRamp = Math.min(1, this.introTimeRamp + .016);
|
|
945
945
|
this.renderOnce();
|
|
946
946
|
this.rafId = requestAnimationFrame(this.loop);
|
|
947
947
|
};
|
|
948
|
-
/** Advance the per-frame
|
|
948
|
+
/** Advance the per-frame time uniforms (geometry itself is static). Time model:
|
|
949
949
|
* time = elapsed·introTimeRamp + timeOffset — the ramp eases the animation in on load. */
|
|
950
950
|
updateTime() {
|
|
951
951
|
const ramp = this.config.introRamp === false || this.skipIntroRamp ? 1 : this.introTimeRamp;
|
|
@@ -1051,12 +1051,19 @@ var WaveRenderer = class {
|
|
|
1051
1051
|
* of hidden exactly on top of it or (for the hero, which fills the right of the frame) pushed
|
|
1052
1052
|
* off-frame. Camera-relative: it's "left on screen" no matter how the view is rotated/zoomed,
|
|
1053
1053
|
* because it's built from the camera's right/up axes and the frame's visible world size. */
|
|
1054
|
-
async captureImage(mime, transparent = true, quality) {
|
|
1055
|
-
const
|
|
1056
|
-
if (transparent !==
|
|
1054
|
+
async captureImage(mime, transparent = true, quality, time) {
|
|
1055
|
+
const prevBg = this.config.transparentBackground;
|
|
1056
|
+
if (transparent !== prevBg) {
|
|
1057
1057
|
this.config.transparentBackground = transparent;
|
|
1058
1058
|
this.applyBackground();
|
|
1059
1059
|
}
|
|
1060
|
+
const fixTime = time !== void 0;
|
|
1061
|
+
const prevTime = this.time;
|
|
1062
|
+
const prevRamp = this.introTimeRamp;
|
|
1063
|
+
if (fixTime) {
|
|
1064
|
+
this.time = time;
|
|
1065
|
+
this.introTimeRamp = 1;
|
|
1066
|
+
}
|
|
1060
1067
|
let blob = null;
|
|
1061
1068
|
try {
|
|
1062
1069
|
this.capturing = true;
|
|
@@ -1064,10 +1071,14 @@ var WaveRenderer = class {
|
|
|
1064
1071
|
blob = await new Promise((resolve) => this.canvas.toBlob(resolve, mime, quality));
|
|
1065
1072
|
} finally {
|
|
1066
1073
|
this.capturing = false;
|
|
1067
|
-
if (transparent !==
|
|
1068
|
-
this.config.transparentBackground =
|
|
1074
|
+
if (transparent !== prevBg) {
|
|
1075
|
+
this.config.transparentBackground = prevBg;
|
|
1069
1076
|
this.applyBackground();
|
|
1070
1077
|
}
|
|
1078
|
+
if (fixTime) {
|
|
1079
|
+
this.time = prevTime;
|
|
1080
|
+
this.introTimeRamp = prevRamp;
|
|
1081
|
+
}
|
|
1071
1082
|
this.renderOnce();
|
|
1072
1083
|
}
|
|
1073
1084
|
if (!blob || blob.type !== mime) throw new Error(`Failed to capture ${mime}`);
|