@wallarm-org/design-system 0.58.1 → 0.59.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/dist/components/AnimatedBackground/AnimatedBackground.js +18 -82
- package/dist/components/AnimatedBackground/GameHud.d.ts +1 -0
- package/dist/components/AnimatedBackground/GameHud.js +2 -2
- package/dist/components/AnimatedBackground/module/celebration-renderer.d.ts +5 -0
- package/dist/components/AnimatedBackground/module/celebration-renderer.js +60 -0
- package/dist/components/AnimatedBackground/module/celebration.d.ts +102 -0
- package/dist/components/AnimatedBackground/module/celebration.js +628 -0
- package/dist/components/AnimatedBackground/module/engine-grid.d.ts +8 -1
- package/dist/components/AnimatedBackground/module/engine-grid.js +19 -5
- package/dist/components/AnimatedBackground/module/engine.d.ts +2 -0
- package/dist/components/AnimatedBackground/module/engine.js +24 -5
- package/dist/components/AnimatedBackground/module/game-logic.d.ts +8 -0
- package/dist/components/AnimatedBackground/module/game-logic.js +81 -37
- package/dist/components/AnimatedBackground/module/game-renderer.d.ts +1 -0
- package/dist/components/AnimatedBackground/module/game-renderer.js +51 -12
- package/dist/components/AnimatedBackground/module/index.d.ts +1 -0
- package/dist/components/AnimatedBackground/module/index.js +2 -1
- package/dist/components/AnimatedBackground/module/math.d.ts +4 -0
- package/dist/components/AnimatedBackground/module/math.js +10 -0
- package/dist/components/AnimatedBackground/module/sfx.d.ts +15 -0
- package/dist/components/AnimatedBackground/module/sfx.js +143 -0
- package/dist/components/AnimatedBackground/module/useGame.d.ts +22 -0
- package/dist/components/AnimatedBackground/module/useGame.js +112 -0
- package/dist/components/AnimatedBackground/module/useGameKeyboard.d.ts +1 -1
- package/dist/components/AnimatedBackground/module/useGameKeyboard.js +23 -14
- package/dist/components/Flex/Flex.d.ts +1 -1
- package/dist/components/SegmentedControl/SegmentedControlSeparator.d.ts +1 -1
- package/dist/components/Separator/Separator.d.ts +1 -1
- package/dist/components/Skeleton/Skeleton.d.ts +1 -1
- package/dist/components/Stack/Stack.d.ts +1 -1
- package/dist/metadata/components.json +2 -2
- package/package.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import { cn } from "../../utils/cn.js";
|
|
4
|
-
import {
|
|
5
|
-
import { createSweepEngine, resolveOptions, useGameKeyboard } from "./module/index.js";
|
|
6
|
-
const GATE_TARGET = 5;
|
|
4
|
+
import { createSweepEngine, resolveOptions, useGame } from "./module/index.js";
|
|
7
5
|
const AnimatedBackground = (props)=>{
|
|
8
6
|
const { ref, texture, paused, game = false, excludeCardSize, className, children, ...rest } = props;
|
|
9
7
|
const canvasRef = useRef(null);
|
|
@@ -12,37 +10,23 @@ const AnimatedBackground = (props)=>{
|
|
|
12
10
|
const options = resolveOptions(props);
|
|
13
11
|
const optionsRef = useRef(options);
|
|
14
12
|
const pausedRef = useRef(paused);
|
|
15
|
-
const gameRef = useRef(game);
|
|
16
13
|
useEffect(()=>{
|
|
17
14
|
optionsRef.current = options;
|
|
18
15
|
pausedRef.current = paused;
|
|
19
|
-
gameRef.current = game;
|
|
20
|
-
}, []);
|
|
21
|
-
const [stats, setStats] = useState({
|
|
22
|
-
kills: 0,
|
|
23
|
-
stopped: 0,
|
|
24
|
-
escaped: 0,
|
|
25
|
-
spawned: 0,
|
|
26
|
-
done: false
|
|
27
16
|
});
|
|
28
|
-
const [catchKey, setCatchKey] = useState(0);
|
|
29
17
|
const isHalftone = (texture ?? 'halftone') === 'halftone';
|
|
30
|
-
const gameActive
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const hasStartedRoundRef = useRef(false);
|
|
18
|
+
const { gameActive, onPointerDown, hudElement, onEngineCreated, onEngineDestroyed } = useGame({
|
|
19
|
+
game,
|
|
20
|
+
isHalftone,
|
|
21
|
+
excludeCardSize,
|
|
22
|
+
canvasRef
|
|
23
|
+
});
|
|
37
24
|
useEffect(()=>{
|
|
38
25
|
const canvas = canvasRef.current;
|
|
39
26
|
if (!canvas) return;
|
|
40
27
|
const engine = createSweepEngine(canvas, optionsRef.current);
|
|
41
28
|
engineRef.current = engine;
|
|
42
|
-
engine
|
|
43
|
-
setStats(s);
|
|
44
|
-
if (gameRef.current && !s.done) setCatchKey((prev)=>prev + 1);
|
|
45
|
-
});
|
|
29
|
+
onEngineCreated(engine);
|
|
46
30
|
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
47
31
|
reducedMotionRef.current = reduced;
|
|
48
32
|
const apply = ()=>{
|
|
@@ -75,8 +59,12 @@ const AnimatedBackground = (props)=>{
|
|
|
75
59
|
cancelAnimationFrame(frame);
|
|
76
60
|
engine.stop();
|
|
77
61
|
engineRef.current = null;
|
|
62
|
+
onEngineDestroyed();
|
|
78
63
|
};
|
|
79
|
-
}, [
|
|
64
|
+
}, [
|
|
65
|
+
onEngineCreated,
|
|
66
|
+
onEngineDestroyed
|
|
67
|
+
]);
|
|
80
68
|
const syncKey = `${JSON.stringify(options)}|${paused}`;
|
|
81
69
|
useEffect(()=>{
|
|
82
70
|
const engine = engineRef.current;
|
|
@@ -90,58 +78,6 @@ const AnimatedBackground = (props)=>{
|
|
|
90
78
|
}, [
|
|
91
79
|
syncKey
|
|
92
80
|
]);
|
|
93
|
-
useEffect(()=>{
|
|
94
|
-
engineRef.current?.setGameActive(game && isHalftone);
|
|
95
|
-
}, [
|
|
96
|
-
game,
|
|
97
|
-
isHalftone
|
|
98
|
-
]);
|
|
99
|
-
const exW = excludeCardSize?.width;
|
|
100
|
-
const exH = excludeCardSize?.height;
|
|
101
|
-
useEffect(()=>{
|
|
102
|
-
engineRef.current?.setExclusion(null != exW && null != exH ? {
|
|
103
|
-
width: exW,
|
|
104
|
-
height: exH
|
|
105
|
-
} : null);
|
|
106
|
-
}, [
|
|
107
|
-
exW,
|
|
108
|
-
exH
|
|
109
|
-
]);
|
|
110
|
-
useEffect(()=>{
|
|
111
|
-
if (!game || !armed) return;
|
|
112
|
-
if (hasStartedRoundRef.current) return void engineRef.current?.setMode('armed');
|
|
113
|
-
hasStartedRoundRef.current = true;
|
|
114
|
-
engineRef.current?.startRound();
|
|
115
|
-
}, [
|
|
116
|
-
game,
|
|
117
|
-
armed
|
|
118
|
-
]);
|
|
119
|
-
useGameKeyboard(engineRef, game, armed, roundOver, hasStartedRoundRef);
|
|
120
|
-
const onPointerDown = useCallback((e)=>{
|
|
121
|
-
if (!game) return;
|
|
122
|
-
const canvas = canvasRef.current;
|
|
123
|
-
if (!canvas) return;
|
|
124
|
-
const rect = canvas.getBoundingClientRect();
|
|
125
|
-
const x = e.clientX - rect.left;
|
|
126
|
-
const y = e.clientY - rect.top;
|
|
127
|
-
engineRef.current?.catchAt(x, y);
|
|
128
|
-
}, [
|
|
129
|
-
game
|
|
130
|
-
]);
|
|
131
|
-
const handleTryAgain = useCallback(()=>{
|
|
132
|
-
engineRef.current?.startRound();
|
|
133
|
-
}, []);
|
|
134
|
-
const gameHud = gameActive && /*#__PURE__*/ jsx(GameHud, {
|
|
135
|
-
caught: caught,
|
|
136
|
-
armed: armed,
|
|
137
|
-
roundOver: roundOver,
|
|
138
|
-
stats: stats,
|
|
139
|
-
accuracy: accuracy,
|
|
140
|
-
faced: faced,
|
|
141
|
-
catchKey: catchKey,
|
|
142
|
-
gateTarget: GATE_TARGET,
|
|
143
|
-
onTryAgain: handleTryAgain
|
|
144
|
-
});
|
|
145
81
|
if (null == children) return /*#__PURE__*/ jsxs(Fragment, {
|
|
146
82
|
children: [
|
|
147
83
|
/*#__PURE__*/ jsx("canvas", {
|
|
@@ -154,9 +90,9 @@ const AnimatedBackground = (props)=>{
|
|
|
154
90
|
},
|
|
155
91
|
"aria-hidden": "true",
|
|
156
92
|
className: cn('h-full w-full pointer-events-none', gameActive && 'pointer-events-auto', className),
|
|
157
|
-
onPointerDown:
|
|
93
|
+
onPointerDown: onPointerDown
|
|
158
94
|
}),
|
|
159
|
-
|
|
95
|
+
hudElement
|
|
160
96
|
]
|
|
161
97
|
});
|
|
162
98
|
return /*#__PURE__*/ jsxs("div", {
|
|
@@ -169,9 +105,9 @@ const AnimatedBackground = (props)=>{
|
|
|
169
105
|
ref: canvasRef,
|
|
170
106
|
"aria-hidden": "true",
|
|
171
107
|
className: cn('absolute inset-0 h-full w-full pointer-events-none', gameActive && 'pointer-events-auto'),
|
|
172
|
-
onPointerDown:
|
|
108
|
+
onPointerDown: onPointerDown
|
|
173
109
|
}),
|
|
174
|
-
|
|
110
|
+
hudElement,
|
|
175
111
|
/*#__PURE__*/ jsx("div", {
|
|
176
112
|
className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center",
|
|
177
113
|
children: /*#__PURE__*/ jsx("div", {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../utils/cn.js";
|
|
3
|
-
const GameHud = ({ caught, armed, roundOver, stats, accuracy, faced, catchKey, gateTarget, onTryAgain })=>{
|
|
3
|
+
const GameHud = ({ caught, armed, roundOver, stats, accuracy, faced, catchKey, gateTarget, onTryAgain, soundOn })=>{
|
|
4
4
|
const showCounter = caught > 0;
|
|
5
5
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
6
6
|
children: [
|
|
@@ -99,7 +99,7 @@ const GameHud = ({ caught, armed, roundOver, stats, accuracy, faced, catchKey, g
|
|
|
99
99
|
}),
|
|
100
100
|
armed && !roundOver && /*#__PURE__*/ jsx("span", {
|
|
101
101
|
className: "text-2xs leading-sm text-text-secondary",
|
|
102
|
-
children:
|
|
102
|
+
children: `\u2190 \u2192 move \u00B7 space fire \u00B7 esc exit \u00B7 m sound ${soundOn ? 'off' : 'on'}`
|
|
103
103
|
})
|
|
104
104
|
]
|
|
105
105
|
}),
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CelState } from './celebration';
|
|
2
|
+
import type { GameEngineHost } from './game-logic';
|
|
3
|
+
import type { GameRenderCtx } from './game-renderer';
|
|
4
|
+
export declare function drawCelebrationOverlay(rc: GameRenderCtx, cel: CelState, t: number, host: GameEngineHost, fontLoaded: boolean): void;
|
|
5
|
+
export declare function getCelCannonOffset(cel: CelState | null, cannonAway: boolean, t: number, h: number): number;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cannonLiftOffset, computeHeadlineY, headlineAlpha, headlineRise } from "./celebration.js";
|
|
2
|
+
import { easeOut } from "./math.js";
|
|
3
|
+
function drawCelebrationOverlay(rc, cel, t, host, fontLoaded) {
|
|
4
|
+
const { ctx } = rc;
|
|
5
|
+
const { w, h, gridSp } = host;
|
|
6
|
+
for (const rocket of cel.rockets){
|
|
7
|
+
const re = t - rocket.t0;
|
|
8
|
+
if (re < 0 || rocket.burst) continue;
|
|
9
|
+
if (re >= rocket.dur) continue;
|
|
10
|
+
const p = easeOut(re / rocket.dur);
|
|
11
|
+
const rx = snap(rocket.sx + (rocket.tx - rocket.sx) * p, gridSp);
|
|
12
|
+
const ry = snap(rocket.sy + (rocket.ty - rocket.sy) * p, gridSp);
|
|
13
|
+
ctx.fillStyle = rc.dotPalette[rc.dotPalette.length - 1];
|
|
14
|
+
ctx.fillRect(rx - 4, ry - 4, 8, 8);
|
|
15
|
+
}
|
|
16
|
+
for (const p of cel.particles){
|
|
17
|
+
const alpha = Math.ceil((1 - p.age / p.life) * 4) / 4;
|
|
18
|
+
if (alpha <= 0) continue;
|
|
19
|
+
const sx = snap(p.x, gridSp);
|
|
20
|
+
const sy = snap(p.y, gridSp);
|
|
21
|
+
ctx.globalAlpha = alpha;
|
|
22
|
+
ctx.fillStyle = p.color;
|
|
23
|
+
ctx.fillRect(sx - p.half, sy - p.half, 2 * p.half, 2 * p.half);
|
|
24
|
+
}
|
|
25
|
+
ctx.globalAlpha = 1;
|
|
26
|
+
if (cel.headline && fontLoaded) {
|
|
27
|
+
const alpha = headlineAlpha(cel, t);
|
|
28
|
+
if (alpha > 0) {
|
|
29
|
+
const baseY = computeHeadlineY(h, host.exclusionBox);
|
|
30
|
+
const rise = headlineRise(cel, t);
|
|
31
|
+
const y = baseY + rise;
|
|
32
|
+
ctx.save();
|
|
33
|
+
ctx.globalAlpha = alpha;
|
|
34
|
+
ctx.font = '13px "Press Start 2P"';
|
|
35
|
+
ctx.textAlign = 'center';
|
|
36
|
+
ctx.textBaseline = 'middle';
|
|
37
|
+
ctx.fillStyle = `rgba(0,0,0,${(0.3 * alpha).toFixed(3)})`;
|
|
38
|
+
ctx.fillText(cel.headline, w / 2 + 1, y + 1);
|
|
39
|
+
ctx.fillStyle = rc.caughtPalette[rc.caughtPalette.length - 1];
|
|
40
|
+
ctx.fillText(cel.headline, w / 2, y);
|
|
41
|
+
if (cel.subline) {
|
|
42
|
+
ctx.font = '11px "Geist Mono", monospace';
|
|
43
|
+
ctx.globalAlpha = 0.85 * alpha;
|
|
44
|
+
ctx.fillStyle = rc.dotPalette[rc.dotPalette.length - 1];
|
|
45
|
+
ctx.fillText(cel.subline, w / 2, y + 24);
|
|
46
|
+
}
|
|
47
|
+
ctx.restore();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getCelCannonOffset(cel, cannonAway, t, h) {
|
|
52
|
+
if (cannonAway) return h + 120;
|
|
53
|
+
if (!cel || cel.tier < 3) return 0;
|
|
54
|
+
return cannonLiftOffset(cel, t, h);
|
|
55
|
+
}
|
|
56
|
+
function snap(v, gridSp) {
|
|
57
|
+
if (gridSp <= 0) return v;
|
|
58
|
+
return gridSp / 2 + Math.round((v - gridSp / 2) / gridSp) * gridSp;
|
|
59
|
+
}
|
|
60
|
+
export { drawCelebrationOverlay, getCelCannonOffset };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { GameEngineHost } from './game-logic';
|
|
2
|
+
export declare const CEL_DUR: readonly [0, 2.6, 3.8, 4, 5.2];
|
|
3
|
+
export declare const CEL_MAX_PARTICLES = 900;
|
|
4
|
+
export declare const CEL_LIFT_AT = 0.5;
|
|
5
|
+
export declare const CEL_LIFT_DUR = 1.2;
|
|
6
|
+
export declare const CEL_PAL: readonly ["rgb(255,60,60)", "rgb(255,180,40)", "rgb(60,220,80)", "rgb(60,200,220)", "rgb(80,100,255)", "rgb(180,80,220)", "rgb(255,100,180)"];
|
|
7
|
+
/** Sentinel value for celDotEffect — means "use caught-green palette". */
|
|
8
|
+
export declare const CEL_CAUGHT_COL: "__caught__";
|
|
9
|
+
export interface CelParticle {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
vx: number;
|
|
13
|
+
vy: number;
|
|
14
|
+
age: number;
|
|
15
|
+
life: number;
|
|
16
|
+
half: number;
|
|
17
|
+
color: string;
|
|
18
|
+
conf: boolean;
|
|
19
|
+
drag: number;
|
|
20
|
+
gravity: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CelRocket {
|
|
23
|
+
t0: number;
|
|
24
|
+
sx: number;
|
|
25
|
+
sy: number;
|
|
26
|
+
tx: number;
|
|
27
|
+
ty: number;
|
|
28
|
+
dur: number;
|
|
29
|
+
burst: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface CelPulse {
|
|
32
|
+
t0: number;
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
dur: number;
|
|
36
|
+
}
|
|
37
|
+
export interface CelState {
|
|
38
|
+
t0: number;
|
|
39
|
+
tier: number;
|
|
40
|
+
score: number;
|
|
41
|
+
settled: boolean;
|
|
42
|
+
particles: CelParticle[];
|
|
43
|
+
rockets: CelRocket[];
|
|
44
|
+
pulses: CelPulse[];
|
|
45
|
+
headline: string;
|
|
46
|
+
subline: string;
|
|
47
|
+
scoreCells: Map<number, true>;
|
|
48
|
+
scoreColCount: number;
|
|
49
|
+
liftStarted: boolean;
|
|
50
|
+
confettiSpawned2: number;
|
|
51
|
+
confettiVolley1: boolean;
|
|
52
|
+
confettiVolley2: boolean;
|
|
53
|
+
burstSpawned: boolean;
|
|
54
|
+
rainAccum: number;
|
|
55
|
+
}
|
|
56
|
+
export declare function tierForScore(score: number): number;
|
|
57
|
+
export declare function buildScoreCells(score: number, gridCols: number, gridSp: number, w: number, h: number, exclusionBox: {
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
} | null): {
|
|
61
|
+
cells: Map<number, true>;
|
|
62
|
+
colCount: number;
|
|
63
|
+
};
|
|
64
|
+
export declare function startCelebration(score: number, t: number, host: GameEngineHost): CelState | null;
|
|
65
|
+
export declare function stepCelebration(cel: CelState, t: number, dt: number, host: GameEngineHost, caughtColor: string, dotColor: string): void;
|
|
66
|
+
export declare function adjustCelebrationTimeMarkers(cel: CelState, skip: number): void;
|
|
67
|
+
export interface CelDotParams {
|
|
68
|
+
elapsed: number;
|
|
69
|
+
tier: number;
|
|
70
|
+
waveRadius: number;
|
|
71
|
+
waveStrength: number;
|
|
72
|
+
waveOriginX: number;
|
|
73
|
+
waveOriginY: number;
|
|
74
|
+
waveSigmaSq2: number;
|
|
75
|
+
blastRadius: number;
|
|
76
|
+
blastStrength: number;
|
|
77
|
+
blastOriginX: number;
|
|
78
|
+
blastOriginY: number;
|
|
79
|
+
blastSigmaSq2: number;
|
|
80
|
+
sweepX: number;
|
|
81
|
+
sweepActive: boolean;
|
|
82
|
+
rainbowQ: number;
|
|
83
|
+
rainbowActive: boolean;
|
|
84
|
+
t: number;
|
|
85
|
+
cel: CelState;
|
|
86
|
+
}
|
|
87
|
+
export declare function computeCelFrameParams(cel: CelState, t: number, w: number, h: number): CelDotParams;
|
|
88
|
+
/**
|
|
89
|
+
* Per-dot celebration effect — returns boost/color modifiers or null.
|
|
90
|
+
* Called inside the hot dot loop only when cel !== null.
|
|
91
|
+
*/
|
|
92
|
+
export declare function celDotEffect(dotIndex: number, dotX: number, dotY: number, p: CelDotParams, w: number, h: number): {
|
|
93
|
+
celBoost: number;
|
|
94
|
+
celCol: string | null;
|
|
95
|
+
} | null;
|
|
96
|
+
export declare function computeHeadlineY(h: number, exclusionBox: {
|
|
97
|
+
width: number;
|
|
98
|
+
height: number;
|
|
99
|
+
} | null): number;
|
|
100
|
+
export declare function headlineAlpha(cel: CelState, t: number): number;
|
|
101
|
+
export declare function headlineRise(cel: CelState, t: number): number;
|
|
102
|
+
export declare function cannonLiftOffset(cel: CelState, t: number, h: number): number;
|