@zakkster/lite-scratch-fx 1.2.0 → 1.3.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/CHANGELOG.md +42 -13
- package/README.md +348 -189
- package/index.d.ts +31 -4
- package/index.js +2 -2
- package/llms.txt +6 -6
- package/package.json +2 -1
- package/src/HostStyle.js +42 -0
- package/src/ScratchController.js +67 -26
- package/src/ScratchRecipes.js +13 -13
- package/src/ScratchStage.js +60 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.0] - 2026-08-22
|
|
4
|
+
|
|
5
|
+
Abortable, self-restoring reveals. A reveal can now be cancelled, and the host scratch
|
|
6
|
+
layer is returned to exactly the inline style it had before the reveal on completion,
|
|
7
|
+
cancel, AND destroy -- so a grid can reset every round. Purely additive.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`controller.cancel()`** on both the standalone controller and stage-managed
|
|
11
|
+
controllers -- abort an in-flight reveal: stop the effect, restore the host canvas's
|
|
12
|
+
inline style, and do NOT fire `onDone`; the controller is revealable again afterwards.
|
|
13
|
+
A no-op when idle or destroyed. A stage `cancel()` does not touch other controllers'
|
|
14
|
+
in-flight reveals and does not reclaim the sub-range. See
|
|
15
|
+
`decisions/0002-host-style-and-cancel.md`.
|
|
16
|
+
- `src/HostStyle.js` -- `captureHostStyle` / `restoreHostStyle`, a shared cold-path helper
|
|
17
|
+
that snapshots and restores the host layer's 5 inline style props.
|
|
18
|
+
- **`dpr` option** for sharp rendering on high-DPI displays; recipes still author in CSS pixels.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Recipes stranded inline style on the host `sourceCanvas`.** The reveal recipes write
|
|
22
|
+
`opacity`, `transform`, `filter`, `clipPath`, and `transformOrigin` on the host layer
|
|
23
|
+
during `tick`; `opacity` was never restored and the others only on the completion frame,
|
|
24
|
+
so an aborted (or opacity-fading) reveal left the cover invisible or transformed forever.
|
|
25
|
+
The controller now snapshots those 5 props at reveal start and restores them on every
|
|
26
|
+
terminal transition -- completion, cancel, and destroy -- via a single `endReveal`
|
|
27
|
+
terminator. No inline `opacity`/`transform`/`filter`/`clipPath`/`transformOrigin`
|
|
28
|
+
survives a reveal.
|
|
29
|
+
|
|
3
30
|
## [1.2.0] - 2026-08-22
|
|
4
31
|
|
|
5
32
|
Concurrent scratch-card reveals over one shared particle pool. Purely additive --
|
|
6
33
|
`createScratchController` and its options are unchanged.
|
|
7
34
|
|
|
8
35
|
### Added
|
|
9
|
-
- **`createScratchStage({ maxParticles, seed })`**
|
|
36
|
+
- **`createScratchStage({ maxParticles, seed })`** -- a stage that owns one particle
|
|
10
37
|
engine and lets any number of controllers reveal **at the same time** over that one
|
|
11
38
|
pool (where `{ engine }` allowed only one reveal at a time). Each
|
|
12
39
|
`stage.createController(src, fx, { capacity })` reserves a fixed sub-range of the lanes,
|
|
@@ -14,8 +41,10 @@ Concurrent scratch-card reveals over one shared particle pool. Purely additive -
|
|
|
14
41
|
and keeps its own deterministic RNG. `stage.tick(dt)` (seconds) drives every active reveal
|
|
15
42
|
from one clock; reserving past `maxParticles` throws. Stage-managed controllers have no
|
|
16
43
|
`tick` of their own. See `decisions/0001-concurrent-shared-reveals.md`.
|
|
17
|
-
- `src/PixelScan.js`
|
|
44
|
+
- `src/PixelScan.js` -- the reveal-scan logic extracted into a shared `createPixelScanner`,
|
|
18
45
|
now used by both the controller and the stage (no behaviour change).
|
|
46
|
+
- A `VERSION`-matches-`package.json` test, and a README rebuilt on the LiteSepforge
|
|
47
|
+
documentation spine. No API or behaviour change.
|
|
19
48
|
|
|
20
49
|
### Notes
|
|
21
50
|
- Feasible with no change to `@zakkster/lite-soa-particle-engine`: raw-mode `tick(dt)` is
|
|
@@ -25,28 +54,28 @@ Concurrent scratch-card reveals over one shared particle pool. Purely additive -
|
|
|
25
54
|
## [1.1.0] - 2026-08-22
|
|
26
55
|
|
|
27
56
|
Recipe organization and an extensible registry, modeled on `@zakkster/lite-ambient-fx`.
|
|
28
|
-
No behaviour change to any recipe
|
|
57
|
+
No behaviour change to any recipe -- all 21 bodies are byte-identical to 1.0.0.
|
|
29
58
|
|
|
30
59
|
### Added
|
|
31
|
-
- **`RECIPE_META`**
|
|
60
|
+
- **`RECIPE_META`** -- a live array of `{ id, name, category, themeable, needsUntaintedCanvas }`
|
|
32
61
|
for every recipe, so a host can build a picker (group by `category`, filter by `themeable`)
|
|
33
62
|
without hardcoding the list. Mirrors lite-ambient's `THEME_META`.
|
|
34
|
-
- **`registerRecipe(id, factory, meta?)`**
|
|
63
|
+
- **`registerRecipe(id, factory, meta?)`** -- register a custom recipe or override a built-in;
|
|
35
64
|
it lands in `RECIPES` and `RECIPE_META` immediately so existing pickers keep working.
|
|
36
65
|
Mirrors lite-ambient's `registerTheme()`. Omitted meta fields fall back to the prior entry,
|
|
37
66
|
then a de-camelCased name, `category: 'custom'`, and `false` flags.
|
|
38
67
|
- `resolvePalette` is now exported (and typed) for authors writing themeable recipes.
|
|
39
|
-
- **Host-driven mode**
|
|
68
|
+
- **Host-driven mode** -- `createScratchController(src, fx, { driven: true })` skips the
|
|
40
69
|
engine's RAF loop; the host calls `controller.tick(dt)` (dt in seconds) so one page clock
|
|
41
70
|
drives N controllers. `tick()` is a no-op unless driven and a reveal is active.
|
|
42
|
-
- **Shared engine**
|
|
71
|
+
- **Shared engine** -- `createScratchController(src, fx, { engine })` reuses a caller-supplied
|
|
43
72
|
particle engine, so a grid of scratch boxes holds one lane pool instead of one per box.
|
|
44
73
|
A shared engine serves one reveal at a time (a reveal on a busy shared engine is ignored);
|
|
45
74
|
`destroy()` never tears down a caller-supplied engine. The two options compose.
|
|
46
75
|
|
|
47
76
|
### Changed
|
|
48
77
|
- **The two recipe files are merged into one `src/ScratchRecipes.js`, grouped by family**
|
|
49
|
-
(particle / image / beam / css) with section banners
|
|
78
|
+
(particle / image / beam / css) with section banners -- replacing the arbitrary
|
|
50
79
|
`ScratchRecipes.js` (12) + `ScratchRecipes2.js` (9) split. `ScratchRecipes2.js` is removed
|
|
51
80
|
from the package. All imports resolve through `index.js`, so the public API is unchanged.
|
|
52
81
|
- `RECIPES` is now an extensible null-prototype registry (was a frozen object) so
|
|
@@ -60,18 +89,18 @@ zero-GC line on the reveal path, and stands up the torture gate.
|
|
|
60
89
|
|
|
61
90
|
### Added
|
|
62
91
|
- `createScratchController(sourceCanvas, effectCanvas, { maxParticles, seed, scanPrecision })`
|
|
63
|
-
|
|
92
|
+
-- scans the still-covered pixels of a scratch layer for spawn points, runs a reveal
|
|
64
93
|
recipe to completion, clears the overlay, and calls the completion callback.
|
|
65
94
|
- 21 reveal recipes: `burn`, `shatter`, `dissolve`, `explode`, `dragonBreath`,
|
|
66
95
|
`iceBreath`, `shineWave`, `lightningCrawl`, `shine`, `peel`, `fade`, `implosion`,
|
|
67
96
|
`glitchReveal`, `matrixDecay`, `goldDust`, `pixelShatter`, `laserScan`,
|
|
68
97
|
`confettiBlast`, `liquidMelt`, `neonPulse`, `cosmicDust`.
|
|
69
98
|
- **Themeable palettes.** The 14 recipes with a visible palette take a unified
|
|
70
|
-
`{ colors, theme }` pair
|
|
99
|
+
`{ colors, theme }` pair -- pass a `{ light, mid, dark }` `theme` shorthand or an
|
|
71
100
|
explicit `colors` ramp; `colors` wins, and both default to the recipe's own palette.
|
|
72
101
|
Replaces three inconsistent conventions (`color`, `colors`, hardcoded). New
|
|
73
102
|
`resolvePalette` helper in `src/Palette.js`.
|
|
74
|
-
- `RECIPES` registry (short-name
|
|
103
|
+
- `RECIPES` registry (short-name -> factory) and `RECIPE_NAMES` for data-driven pickers.
|
|
75
104
|
- Full TypeScript declarations (`index.d.ts`), including `Theme` and `ThemeableRecipeOptions`.
|
|
76
105
|
- Test suite under `node:test` (46 checks): controller lifecycle, the `data[]` dataFlag
|
|
77
106
|
indexing invariant, a reveal-to-completion smoke test for every recipe, and theming
|
|
@@ -84,7 +113,7 @@ zero-GC line on the reveal path, and stands up the torture gate.
|
|
|
84
113
|
|
|
85
114
|
### Changed
|
|
86
115
|
- **Zero-GC reveal path.** `scanPixels` no longer mints an offscreen canvas or an
|
|
87
|
-
object per surviving pixel on every reveal
|
|
116
|
+
object per surviving pixel on every reveal -- the scan canvas is built once and the
|
|
88
117
|
spawn points fill preallocated `Float32Array`s read through one reused `spot` object.
|
|
89
118
|
The per-frame `tick` no longer allocates a fresh particle-view object; it reuses one.
|
|
90
119
|
- Test runner is `node:test` (was vitest); `vitest` and `typescript` devDependencies
|
|
@@ -97,7 +126,7 @@ zero-GC line on the reveal path, and stands up the torture gate.
|
|
|
97
126
|
`engine._loop` every frame, but the SoA engine already owns its own
|
|
98
127
|
`requestAnimationFrame` loop and computes its own `dt`. Driving it a second time
|
|
99
128
|
corrupted `dt`, so particle life never decayed and the completion condition
|
|
100
|
-
(`alive === 0 && elapsed >= duration`) was never met
|
|
129
|
+
(`alive === 0 && elapsed >= duration`) was never met -- a reveal would run forever. The
|
|
101
130
|
external ticker is removed; the engine self-drives via `start()` / `stop()`.
|
|
102
131
|
- **`count: 0` recipes spawned a full pool.** `recipe.count || maxParticles` turned an
|
|
103
132
|
explicit `0` (pure-canvas reveals like `shine`, `implosion`, `laserScan`) into
|