@zakkster/lite-scratch-fx 1.6.0 → 1.7.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 +16 -0
- package/README.md +7 -3
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/llms.txt +5 -2
- package/package.json +1 -1
- package/src/ScratchRecipes.js +18 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.0] - 2026-08-24
|
|
4
|
+
|
|
5
|
+
Optional per-frame glow cap for the two haloed particle recipes (B-20). Additive and
|
|
6
|
+
backward compatible: the knob defaults to `Infinity` (unlimited), so default output is
|
|
7
|
+
byte-identical to 1.6.0. The per-frame render path gains one counter and one guard.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`glowBudget` option on `BurnRecipe` and `DragonBreathRecipe` (default `Infinity` =
|
|
11
|
+
unlimited).** Caps how many particles paint their outer glow halo per frame. Once the
|
|
12
|
+
budget is spent that tick, further particles draw only their brighter core arc; the
|
|
13
|
+
counter resets every frame. The core arc, physics, `alive` accounting, and the
|
|
14
|
+
offscreen cull are never capped. Uses a strict `g < glowBudget` compare (never a falsy
|
|
15
|
+
coercion), so `glowBudget: 0` means exactly zero halos (cores only) and the unset
|
|
16
|
+
default `Infinity` keeps every halo. `glowBudget: 80` matches the game's `GLOW_BUDGET`
|
|
17
|
+
parity. Zero new hot-path allocation.
|
|
18
|
+
|
|
3
19
|
## [1.6.0] - 2026-08-23
|
|
4
20
|
|
|
5
21
|
Reduced-motion support (N-4). Backward compatible: the new controller/stage option
|
package/README.md
CHANGED
|
@@ -268,8 +268,12 @@ current built-in look:
|
|
|
268
268
|
- **`DragonBreathRecipe`** / **`IceBreathRecipe`** -- `spread` (jet half-angle in radians,
|
|
269
269
|
defaults `0.3` / `0.225`), `speedMin`/`speedMax` (spawn speed range, defaults `12`-`25`
|
|
270
270
|
/ `15`-`28`).
|
|
271
|
-
- **`
|
|
272
|
-
|
|
271
|
+
- **`BurnRecipe`** / **`DragonBreathRecipe`** -- `glowBudget` (default `Infinity` =
|
|
272
|
+
unlimited): a per-frame cap on how many particles paint their outer glow halo. The
|
|
273
|
+
brighter core arc, physics, and offscreen cull are never capped -- only the halo is
|
|
274
|
+
skipped once the budget is spent that frame; the counter resets every tick. `0` means
|
|
275
|
+
zero halos (cores only); `80` matches the game's `GLOW_BUDGET` parity. Default output is
|
|
276
|
+
byte-identical to earlier versions.
|
|
273
277
|
|
|
274
278
|
### Reduced motion
|
|
275
279
|
|
|
@@ -303,7 +307,7 @@ two ways to respond, plus a flag for building a picker.
|
|
|
303
307
|
|
|
304
308
|
| Constant | Value | Meaning |
|
|
305
309
|
| ------------------------- | ----------------------------------------------------------- | ---------------------------------------------------- |
|
|
306
|
-
| `VERSION` | `'1.
|
|
310
|
+
| `VERSION` | `'1.7.0'` | Package version string (synced to package.json). |
|
|
307
311
|
| `maxParticles` (default) | `2000` | Controller / stage pool capacity. |
|
|
308
312
|
| `scanPrecision` (default) | `32` | Horizontal resolution of the spawn-point scan. |
|
|
309
313
|
| stage `capacity` (default)| `300` | Slots a stage controller reserves from the pool. |
|
package/index.d.ts
CHANGED
|
@@ -242,11 +242,11 @@ export interface ThemeableRecipeOptions extends RecipeOptions {
|
|
|
242
242
|
theme?: Theme;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
export function BurnRecipe(opts?: ThemeableRecipeOptions): Recipe;
|
|
245
|
+
export function BurnRecipe(opts?: ThemeableRecipeOptions & { glowBudget?: number }): Recipe;
|
|
246
246
|
export function ShatterRecipe(opts?: RecipeOptions & { gravity?: number }): Recipe;
|
|
247
247
|
export function DissolveRecipe(opts?: ThemeableRecipeOptions & { fadeSpeed?: number }): Recipe;
|
|
248
248
|
export function ExplodeRecipe(opts?: ThemeableRecipeOptions & { force?: number }): Recipe;
|
|
249
|
-
export function DragonBreathRecipe(opts?: ThemeableRecipeOptions & { spread?: number; speedMin?: number; speedMax?: number }): Recipe;
|
|
249
|
+
export function DragonBreathRecipe(opts?: ThemeableRecipeOptions & { spread?: number; speedMin?: number; speedMax?: number; glowBudget?: number }): Recipe;
|
|
250
250
|
export function IceBreathRecipe(opts?: ThemeableRecipeOptions & { spread?: number; speedMin?: number; speedMax?: number }): Recipe;
|
|
251
251
|
export function ShineWaveRecipe(opts?: ThemeableRecipeOptions & { beamWidth?: number; particleCount?: number; ease?: (t: number) => number }): Recipe;
|
|
252
252
|
export function LightningCrawlRecipe(opts?: ThemeableRecipeOptions & { boltCount?: number }): Recipe;
|
package/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
// Three-place version sync: this constant, package.json "version", and the top
|
|
15
15
|
// CHANGELOG.md heading must always match. /release keeps them locked.
|
|
16
|
-
export const VERSION = '1.
|
|
16
|
+
export const VERSION = '1.7.0';
|
|
17
17
|
|
|
18
18
|
export { createScratchController } from './src/ScratchController.js';
|
|
19
19
|
export { default as ScratchController } from './src/ScratchController.js';
|
package/llms.txt
CHANGED
|
@@ -78,8 +78,11 @@ Every factory takes an options bag; all options default to the shipped tuning, s
|
|
|
78
78
|
no args reproduces the stock effect. Beyond { colors, theme } (the 14 themeable recipes) and
|
|
79
79
|
count/duration, the FX_CONFIG-style knobs are: dissolve fadeSpeed (fade window as a fraction of
|
|
80
80
|
duration); dragonBreath and iceBreath spread / speedMin / speedMax (spawn cone half-angle in
|
|
81
|
-
radians and speed range);
|
|
82
|
-
|
|
81
|
+
radians and speed range); burn and dragonBreath glowBudget (per-frame cap on how many particles
|
|
82
|
+
paint their outer glow halo, default Infinity = unlimited; 0 = zero halos, cores only; 80 =
|
|
83
|
+
game GLOW_BUDGET parity -- the core arc, physics and offscreen cull are never capped and the
|
|
84
|
+
counter resets each tick, so the default output is byte-identical to earlier versions); peel and
|
|
85
|
+
shineWave ease (a t -> t easing function). A non-function ease falls back to the recipe's default curve.
|
|
83
86
|
|
|
84
87
|
## Registry, metadata, extension
|
|
85
88
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakkster/lite-scratch-fx",
|
|
3
3
|
"author": "Zahary Shinikchiev <shinikchiev@yahoo.com>",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.7.0",
|
|
5
5
|
"description": "One-shot scratch-card reveal effects on canvas. A controller scans the still-covered pixels of a scratch layer, spawns particles from them, and delegates physics and rendering to a recipe. 21 themeable recipes (burn, shatter, dissolve, glitch, gold dust, cosmic dust, and more), a registry with a register hook, zero GSAP, a zero-GC hot path, deterministic seeded RNG, and a stage that runs a grid of simultaneous reveals over one shared pool.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./index.js",
|
package/src/ScratchRecipes.js
CHANGED
|
@@ -28,7 +28,7 @@ import {resolvePalette} from './Palette.js';
|
|
|
28
28
|
// Spawn particles from the scratch layer's still-covered pixels and run their own physics.
|
|
29
29
|
// ===========================================================================
|
|
30
30
|
|
|
31
|
-
export function BurnRecipe({count = 100, duration = 200, colors, theme} = {}) {
|
|
31
|
+
export function BurnRecipe({count = 100, duration = 200, glowBudget = Infinity, colors, theme} = {}) {
|
|
32
32
|
let pSize, pDecay, pColorIdx;
|
|
33
33
|
const palette = resolvePalette(colors, theme, ['#ff6b00', '#ff8c00', '#ffaa00', '#ff4500', '#8B0000']);
|
|
34
34
|
|
|
@@ -49,6 +49,7 @@ export function BurnRecipe({count = 100, duration = 200, colors, theme} = {}) {
|
|
|
49
49
|
const {x, y, vx, vy, life, data, max} = engine;
|
|
50
50
|
const ds = dt * 60;
|
|
51
51
|
let alive = 0;
|
|
52
|
+
let g = 0;
|
|
52
53
|
const progress = clamp(elapsed / duration, 0, 1);
|
|
53
54
|
src.style.opacity = 1 - easeIn(progress);
|
|
54
55
|
|
|
@@ -63,10 +64,13 @@ export function BurnRecipe({count = 100, duration = 200, colors, theme} = {}) {
|
|
|
63
64
|
if (y[i] < -50) continue;
|
|
64
65
|
ctx.globalCompositeOperation = 'lighter';
|
|
65
66
|
ctx.fillStyle = palette[pColorIdx[p]];
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
if (g < glowBudget) {
|
|
68
|
+
ctx.globalAlpha = life[i] * 0.3;
|
|
69
|
+
ctx.beginPath();
|
|
70
|
+
ctx.arc(x[i], y[i], pSize[p] * 2, 0, Math.PI * 2);
|
|
71
|
+
ctx.fill();
|
|
72
|
+
g++;
|
|
73
|
+
}
|
|
70
74
|
ctx.globalAlpha = life[i];
|
|
71
75
|
ctx.beginPath();
|
|
72
76
|
ctx.arc(x[i], y[i], pSize[p], 0, Math.PI * 2);
|
|
@@ -171,7 +175,7 @@ export function ExplodeRecipe({count = 80, duration = 750, force = 15, colors, t
|
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
|
|
174
|
-
export function DragonBreathRecipe({count = 200, duration = 1200, spread = 0.3, speedMin = 12, speedMax = 25, colors, theme} = {}) {
|
|
178
|
+
export function DragonBreathRecipe({count = 200, duration = 1200, spread = 0.3, speedMin = 12, speedMax = 25, glowBudget = Infinity, colors, theme} = {}) {
|
|
175
179
|
let pSize, pDecay, pColorIdx, r;
|
|
176
180
|
const palette = resolvePalette(colors, theme, ['#FFF', '#FFD700', '#FF4500', '#8B0000', '#2F2F2F']);
|
|
177
181
|
|
|
@@ -203,6 +207,7 @@ export function DragonBreathRecipe({count = 200, duration = 1200, spread = 0.3,
|
|
|
203
207
|
const dvy = Math.pow(0.98, ds);
|
|
204
208
|
const dsz = Math.pow(1.04, ds);
|
|
205
209
|
let alive = 0;
|
|
210
|
+
let g = 0;
|
|
206
211
|
const fadeT = clamp((elapsed - 100) / 400, 0, 1);
|
|
207
212
|
src.style.opacity = 1 - fadeT * fadeT;
|
|
208
213
|
|
|
@@ -220,10 +225,13 @@ export function DragonBreathRecipe({count = 200, duration = 1200, spread = 0.3,
|
|
|
220
225
|
if (y[i] < -50) continue;
|
|
221
226
|
ctx.globalCompositeOperation = 'lighter';
|
|
222
227
|
ctx.fillStyle = palette[pColorIdx[p]];
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
if (g < glowBudget) {
|
|
229
|
+
ctx.globalAlpha = life[i] * 0.3;
|
|
230
|
+
ctx.beginPath();
|
|
231
|
+
ctx.arc(x[i], y[i], pSize[p] * 2, 0, Math.PI * 2);
|
|
232
|
+
ctx.fill();
|
|
233
|
+
g++;
|
|
234
|
+
}
|
|
227
235
|
ctx.globalAlpha = life[i];
|
|
228
236
|
ctx.beginPath();
|
|
229
237
|
ctx.arc(x[i], y[i], pSize[p], 0, Math.PI * 2);
|