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