@wave3d/core 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/config/model.d.ts +25 -1
- package/dist/config/model.js +121 -60
- package/dist/config/model.js.map +1 -1
- package/dist/core-loader.d.ts +0 -2
- package/dist/presets.d.ts +0 -1
- package/dist/presets.js +105 -0
- package/dist/presets.js.map +1 -1
- package/dist/renderer/WaveGeometry.d.ts +0 -1
- package/dist/renderer/WaveGeometry.js.map +1 -1
- package/dist/renderer/WaveRenderer.d.ts +2 -2
- package/dist/renderer/WaveRenderer.js +17 -2
- package/dist/renderer/WaveRenderer.js.map +1 -1
- package/dist/renderer/heroPalette.d.ts +0 -1
- package/dist/renderer/interaction.d.ts +0 -2
- package/dist/renderer/interaction.js +9 -0
- package/dist/renderer/interaction.js.map +1 -1
- package/dist/renderer/palette.d.ts +0 -1
- package/dist/renderer/shaders.js +46 -0
- package/dist/renderer/shaders.js.map +1 -1
- package/dist/shell/createWave.d.ts +0 -1
- package/dist/standalone/wave3d.standalone.js +449 -292
- package/dist/standalone.d.ts +0 -1
- package/dist/studio/StudioWaveRenderer.d.ts +0 -1
- package/dist/studio/randomize.d.ts +0 -1
- package/dist/studio/thumbnail.d.ts +0 -1
- package/dist/studio/thumbnail.js +5 -1
- package/dist/studio/thumbnail.js.map +1 -1
- package/package.json +5 -3
- package/skills/wave3d/SKILL.md +30 -12
package/dist/standalone.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { BackgroundImageFit, BackgroundMode, BasicGradientType, BlendMode, CAMER
|
|
|
2
2
|
import { WaveRenderer } from "./renderer/WaveRenderer.js";
|
|
3
3
|
import { FallbackReason, SnapshotOptions, WaveHandle, WaveOptions, WaveState } from "./shell/createWave.js";
|
|
4
4
|
import { PRESETS } from "./presets.js";
|
|
5
|
-
|
|
6
5
|
//#region src/standalone.d.ts
|
|
7
6
|
/** {@link createWaveImpl} with the engine already bundled in (synchronous upgrade). */
|
|
8
7
|
declare function createWave(container: HTMLElement, config?: Partial<StudioConfig>, options?: WaveOptions): WaveHandle;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { StudioConfig, WaveConfig } from "../config/model.js";
|
|
2
|
-
|
|
3
2
|
//#region src/studio/randomize.d.ts
|
|
4
3
|
/** "Tasteful Randomize": keep the scene (background, camera, lights, quality) and randomize the
|
|
5
4
|
* post-fx plus every wave independently — so a multi-wave stack becomes visibly varied.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { StudioConfig } from "../config/model.js";
|
|
2
2
|
import { WaveRenderer } from "../renderer/WaveRenderer.js";
|
|
3
|
-
|
|
4
3
|
//#region src/studio/thumbnail.d.ts
|
|
5
4
|
/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */
|
|
6
5
|
declare function createThumbHost(width: number, height: number): HTMLDivElement;
|
package/dist/studio/thumbnail.js
CHANGED
|
@@ -11,7 +11,11 @@ function createThumbHost(width, height) {
|
|
|
11
11
|
function prepThumbConfig(cfg) {
|
|
12
12
|
cfg.paused = true;
|
|
13
13
|
cfg.transparentBackground = false;
|
|
14
|
-
if (cfg.waves[0]?.theme !== "wireframe")
|
|
14
|
+
if (cfg.waves[0]?.theme !== "wireframe") {
|
|
15
|
+
cfg.background = "#ffffff";
|
|
16
|
+
cfg.bloomStrength = 0;
|
|
17
|
+
cfg.innerLight = 0;
|
|
18
|
+
}
|
|
15
19
|
}
|
|
16
20
|
/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */
|
|
17
21
|
function renderThumbFrame(renderer, host) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thumbnail.js","names":[],"sources":["../../src/studio/thumbnail.ts"],"sourcesContent":["/**\n * Offscreen thumbnail rendering: turn a config into a still frame with one hidden, reused\n * WaveRenderer. Used by the studio's preset + history thumbnails and by the wave gallery grid.\n */\nimport type { WaveRenderer } from \"../renderer/WaveRenderer\";\nimport type { StudioConfig } from \"../config/model\";\n\n/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */\nexport function createThumbHost(width: number, height: number): HTMLDivElement {\n const host = document.createElement(\"div\");\n host.style.cssText = `position:fixed;left:-10000px;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;`;\n document.body.appendChild(host);\n return host;\n}\n\n/** Mutate `cfg` for a thumbnail still: static frame, opaque, white page behind solid themes\n * (wireframe keys its between-line colour off the dark page background, so keep it). */\nexport function prepThumbConfig(cfg: StudioConfig): void {\n cfg.paused = true;\n cfg.transparentBackground = false;\n if (cfg.waves[0]?.theme !== \"wireframe\") cfg.background = \"#ffffff\";\n}\n\n/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */\nexport function renderThumbFrame(\n renderer: WaveRenderer,\n host: HTMLElement,\n): HTMLCanvasElement | null {\n renderer.resize();\n renderer.renderOnce();\n renderer.renderOnce(); // 2nd pass so any shader recompile (theme/blend variant) is applied\n const gl = host.querySelector(\"canvas\");\n if (!gl) return null;\n // Copy to a 2D canvas before encoding (reliable read of the WebGL drawing buffer).\n const out = document.createElement(\"canvas\");\n out.width = gl.width;\n out.height = gl.height;\n out.getContext(\"2d\")?.drawImage(gl, 0, 0);\n return out;\n}\n"],"mappings":";;AAQA,SAAgB,gBAAgB,OAAe,QAAgC;CAC7E,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,MAAM,UAAU,4CAA4C,MAAM,YAAY,OAAO;CAC1F,SAAS,KAAK,YAAY,IAAI;CAC9B,OAAO;AACT;;;AAIA,SAAgB,gBAAgB,KAAyB;CACvD,IAAI,SAAS;CACb,IAAI,wBAAwB;CAC5B,IAAI,IAAI,MAAM,EAAE,EAAE,UAAU,aAAa,IAAI,aAAa;
|
|
1
|
+
{"version":3,"file":"thumbnail.js","names":[],"sources":["../../src/studio/thumbnail.ts"],"sourcesContent":["/**\n * Offscreen thumbnail rendering: turn a config into a still frame with one hidden, reused\n * WaveRenderer. Used by the studio's preset + history thumbnails and by the wave gallery grid.\n */\nimport type { WaveRenderer } from \"../renderer/WaveRenderer\";\nimport type { StudioConfig } from \"../config/model\";\n\n/** A hidden host div that is in layout (so clientWidth/Height are real) but off-screen. */\nexport function createThumbHost(width: number, height: number): HTMLDivElement {\n const host = document.createElement(\"div\");\n host.style.cssText = `position:fixed;left:-10000px;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;`;\n document.body.appendChild(host);\n return host;\n}\n\n/** Mutate `cfg` for a thumbnail still: static frame, opaque, white page behind solid themes\n * (wireframe keys its between-line colour off the dark page background, so keep it). */\nexport function prepThumbConfig(cfg: StudioConfig): void {\n cfg.paused = true;\n cfg.transparentBackground = false;\n if (cfg.waves[0]?.theme !== \"wireframe\") {\n cfg.background = \"#ffffff\";\n // Swapping an authored dark background for the white card breaks the passes that SCATTER light\n // out of bright pixels: they were tuned against the dark original, and white sits far above any\n // sane threshold, so they bloom the card itself and wash the whole frame out. A preset with\n // bloom rendered a blank white thumbnail — 0.3% non-white pixels — until this zeroed them.\n cfg.bloomStrength = 0;\n cfg.innerLight = 0;\n }\n}\n\n/** Render the current config to a fresh 2D canvas (null if the WebGL canvas is missing). */\nexport function renderThumbFrame(\n renderer: WaveRenderer,\n host: HTMLElement,\n): HTMLCanvasElement | null {\n renderer.resize();\n renderer.renderOnce();\n renderer.renderOnce(); // 2nd pass so any shader recompile (theme/blend variant) is applied\n const gl = host.querySelector(\"canvas\");\n if (!gl) return null;\n // Copy to a 2D canvas before encoding (reliable read of the WebGL drawing buffer).\n const out = document.createElement(\"canvas\");\n out.width = gl.width;\n out.height = gl.height;\n out.getContext(\"2d\")?.drawImage(gl, 0, 0);\n return out;\n}\n"],"mappings":";;AAQA,SAAgB,gBAAgB,OAAe,QAAgC;CAC7E,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,MAAM,UAAU,4CAA4C,MAAM,YAAY,OAAO;CAC1F,SAAS,KAAK,YAAY,IAAI;CAC9B,OAAO;AACT;;;AAIA,SAAgB,gBAAgB,KAAyB;CACvD,IAAI,SAAS;CACb,IAAI,wBAAwB;CAC5B,IAAI,IAAI,MAAM,EAAE,EAAE,UAAU,aAAa;EACvC,IAAI,aAAa;EAKjB,IAAI,gBAAgB;EACpB,IAAI,aAAa;CACnB;AACF;;AAGA,SAAgB,iBACd,UACA,MAC0B;CAC1B,SAAS,OAAO;CAChB,SAAS,WAAW;CACpB,SAAS,WAAW;CACpB,MAAM,KAAK,KAAK,cAAc,QAAQ;CACtC,IAAI,CAAC,IAAI,OAAO;CAEhB,MAAM,MAAM,SAAS,cAAc,QAAQ;CAC3C,IAAI,QAAQ,GAAG;CACf,IAAI,SAAS,GAAG;CAChB,IAAI,WAAW,IAAI,CAAC,EAAE,UAAU,IAAI,GAAG,CAAC;CACxC,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wave3d/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Framework-agnostic 3D gradient-wave renderer + config model (the engine behind Wave Studio)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"animation",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"@tanstack/intent": "^0.3.4",
|
|
58
58
|
"@types/three": "^0.185.0",
|
|
59
59
|
"three": "^0.185.0",
|
|
60
|
-
"vite": "^8.1.0"
|
|
60
|
+
"vite": "^8.1.0",
|
|
61
|
+
"vitest": "^4.1.10"
|
|
61
62
|
},
|
|
62
63
|
"peerDependencies": {
|
|
63
64
|
"@types/three": ">=0.180",
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
"scripts": {
|
|
72
73
|
"typecheck": "tsc --noEmit",
|
|
73
74
|
"build": "tsdown && pnpm build:standalone",
|
|
74
|
-
"build:standalone": "vite build --config vite.standalone.config.ts"
|
|
75
|
+
"build:standalone": "vite build --config vite.standalone.config.ts",
|
|
76
|
+
"test": "vitest run"
|
|
75
77
|
}
|
|
76
78
|
}
|
package/skills/wave3d/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
metadata:
|
|
11
11
|
type: core
|
|
12
12
|
library: "@wave3d/core"
|
|
13
|
-
library_version: "0.
|
|
13
|
+
library_version: "0.6.0"
|
|
14
14
|
sources:
|
|
15
15
|
- "wave3d/wave3d:README.md"
|
|
16
16
|
- "wave3d/wave3d:packages/core/src/config/model.ts"
|
|
@@ -87,13 +87,7 @@ import { Wave3D } from "@wave3d/react";
|
|
|
87
87
|
```ts
|
|
88
88
|
import { createWave } from "@wave3d/core";
|
|
89
89
|
|
|
90
|
-
const handle = createWave(
|
|
91
|
-
document.getElementById("wave"),
|
|
92
|
-
{
|
|
93
|
-
/* config */
|
|
94
|
-
},
|
|
95
|
-
{ poster: "/wave.png" },
|
|
96
|
-
);
|
|
90
|
+
const handle = createWave(document.getElementById("wave"), {/* config */}, { poster: "/wave.png" });
|
|
97
91
|
// handle: { state, renderer, set(config), play(), pause(), destroy() }
|
|
98
92
|
```
|
|
99
93
|
|
|
@@ -101,8 +95,22 @@ const handle = createWave(
|
|
|
101
95
|
|
|
102
96
|
A wave is one JSON-serializable `StudioConfig`: scene fields (`background`, `quality`, `dprMax`,
|
|
103
97
|
`loopSeconds`, `paused`, camera…) plus a `waves: WaveConfig[]` array (each wave has its own
|
|
104
|
-
`palette`, `fiberCount`, `speed`, `displaceAmount`, `twist…`, `blendMode`, `theme`,
|
|
105
|
-
Omitted fields fall back to `createDefaultConfig()`.
|
|
98
|
+
`palette`, `fiberCount`, `speed`, `displaceAmount`, `twist…`, `helix…`, `blendMode`, `theme`,
|
|
99
|
+
transform…). Omitted fields fall back to `createDefaultConfig()`.
|
|
100
|
+
|
|
101
|
+
**Shape: twist vs helix.** `twistFrequency`/`twistPower` rotate by `freq * expStep(uv, power)`, a
|
|
102
|
+
MONOTONE falloff — good for one dramatic ramp, but it can never repeat, so it cannot make a coil.
|
|
103
|
+
`helixTurns` sweeps a _periodic_ angle along the ribbon's length instead, and is the only way to get
|
|
104
|
+
a repeating helix:
|
|
105
|
+
|
|
106
|
+
- `helixRadius` carries the whole ribbon around the axis (orientation intact). A narrow ribbon
|
|
107
|
+
(small `scale.z`) then reads as one strand — **two waves 180° apart in `helixPhase` are a double
|
|
108
|
+
helix**, and they genuinely swap depth at every crossing.
|
|
109
|
+
- `helixRoll` rolls the ribbon's own cross-section in step (1 = rigid twisted ribbon), swinging its
|
|
110
|
+
two long edges onto opposite sides of the axis, so **one wave becomes a ladder whose edges are
|
|
111
|
+
both strands**. Add `rungAmount` (wireframe theme) for the rungs between them.
|
|
112
|
+
- Both are off at 0, and the helix code path isn't compiled unless `helixRadius` or `helixRoll` is
|
|
113
|
+
non-zero — a wave without one renders byte-identically to before.
|
|
106
114
|
|
|
107
115
|
**React flat props** are a shortcut mapped onto `waves[0]` and the scene:
|
|
108
116
|
`palette` (`string[]` | `ColorStop[]`), `fiberCount`, `fiberStrength`, `sheen`, `iridescence`,
|
|
@@ -124,13 +132,23 @@ Sources: `scroll`, `hover`, `pointerX` / `pointerY`, `pointerSpeed`, `press`, `s
|
|
|
124
132
|
`appear`, or a developer-fed `custom:*` (via `handle.setInteractionInput(name, value)` /
|
|
125
133
|
`renderer.setInteractionInput`). Each binding rests at the authored value and moves toward `to` as
|
|
126
134
|
its input rises 0→1 — `{ source: "hover", target: "displaceAmount", to: 12 }` grows the folds on
|
|
127
|
-
hover.
|
|
128
|
-
|
|
135
|
+
hover. `helixPhase` / `helixTurns` / `helixRadius` are bindable too, so
|
|
136
|
+
`{ source: "scroll", target: "helixPhase", to: 360 }` spins a helix exactly one turn down the page
|
|
137
|
+
(and the helix path is compiled for a wave that binds one but authors radius/roll at 0). Each wave's
|
|
138
|
+
`hover.smoothing` sets its own cursor-follow lag (vary it across a stack for a parallax drag). **Shared inputs** (one cursor + scroll: `radius`, `touch`) and **scene-param
|
|
129
139
|
bindings** (`timeOffset`, `cameraZoom`, `blur`, `grain` — e.g. `scroll → timeOffset` scrubs the whole
|
|
130
140
|
wave with the page) live on `SceneConfig.interaction`. In React the flat `interaction` prop targets
|
|
131
141
|
the first wave; the studio authors it per wave (Hover / Click & touch / Bindings) plus a global
|
|
132
142
|
Interaction folder for the shared inputs and a scroll preview.
|
|
133
143
|
|
|
144
|
+
**Touch is ignored unless you opt in.** `SceneConfig.interaction.touch` defaults to `false`, and
|
|
145
|
+
coarse pointers are dropped before any handler runs — so on a phone, `hover`, `press`/`ripple` and
|
|
146
|
+
the `hover` / `pointerX` / `pointerY` / `pointerSpeed` / `press` sources are all inert no matter what
|
|
147
|
+
you tune. Set `interaction: { touch: true }` on the scene to follow the finger while it is down
|
|
148
|
+
(listeners are passive, so this does **not** block page scrolling). Untouched by the gate: `scroll`,
|
|
149
|
+
`scrollVelocity` and `appear` read container progress through the viewport, not pointer events, so
|
|
150
|
+
they drive normally on mobile — scroll bindings are the way to stay reactive with `touch` off.
|
|
151
|
+
|
|
134
152
|
## Post effects (optional)
|
|
135
153
|
|
|
136
154
|
Passes over the finished composite. Each is a plain **scene-level** `SceneConfig` field (a sibling of
|