minimojs 1.0.0-alpha.2 → 1.0.0-alpha.21

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.
Files changed (55) hide show
  1. package/README.md +82 -285
  2. package/dist/internal/AnimationSystem.js +345 -0
  3. package/dist/internal/AssetSystem.d.ts +1 -0
  4. package/dist/internal/AssetSystem.js +101 -0
  5. package/dist/internal/BackgroundSystem.d.ts +1 -0
  6. package/dist/internal/BackgroundSystem.js +26 -0
  7. package/dist/internal/CanvasSystem.d.ts +1 -0
  8. package/dist/internal/CanvasSystem.js +51 -0
  9. package/dist/internal/ExplosionSystem.d.ts +1 -0
  10. package/dist/internal/ExplosionSystem.js +540 -0
  11. package/dist/internal/InputSystem.d.ts +1 -0
  12. package/dist/internal/InputSystem.js +265 -0
  13. package/dist/internal/LoopSystem.d.ts +1 -0
  14. package/dist/internal/LoopSystem.js +61 -0
  15. package/dist/internal/PhysicsSystem.d.ts +1 -0
  16. package/dist/internal/PhysicsSystem.js +174 -0
  17. package/dist/internal/RenderSystem.d.ts +1 -0
  18. package/dist/internal/RenderSystem.js +910 -0
  19. package/dist/internal/SoundSystem.d.ts +1 -0
  20. package/dist/internal/SoundSystem.js +55 -0
  21. package/dist/internal/SpriteSystem.d.ts +1 -0
  22. package/dist/internal/SpriteSystem.js +32 -0
  23. package/dist/internal/TextSystem.d.ts +1 -0
  24. package/dist/internal/TextSystem.js +16 -0
  25. package/dist/internal/TimerSystem.d.ts +1 -0
  26. package/dist/internal/TimerSystem.js +43 -0
  27. package/dist/internal/TrailSystem.d.ts +1 -0
  28. package/dist/internal/TrailSystem.js +116 -0
  29. package/dist/internal/TransitionSystem.d.ts +1 -0
  30. package/dist/internal/TransitionSystem.js +74 -0
  31. package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.d.ts +1 -0
  32. package/dist/internal/arcade-racer/ArcadeRacerCollisionSystem.js +198 -0
  33. package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.d.ts +1 -0
  34. package/dist/internal/arcade-racer/ArcadeRacerLaneSystem.js +120 -0
  35. package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.d.ts +1 -0
  36. package/dist/internal/arcade-racer/ArcadeRacerRenderSystem.js +599 -0
  37. package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.d.ts +1 -0
  38. package/dist/internal/arcade-racer/ArcadeRacerRoadSprite.js +13 -0
  39. package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.d.ts +1 -0
  40. package/dist/internal/arcade-racer/ArcadeRacerTrackSystem.js +447 -0
  41. package/dist/minimo-arcaderacer.d.ts +1431 -0
  42. package/dist/minimo-arcaderacer.js +2060 -0
  43. package/dist/minimo.d.ts +1412 -162
  44. package/dist/minimo.js +2083 -816
  45. package/package.json +3 -2
  46. package/dist/animations.js +0 -30
  47. package/dist/audio.js +0 -17
  48. package/dist/game.js +0 -1105
  49. package/dist/input.js +0 -185
  50. package/dist/internal-types.js +0 -4
  51. package/dist/physics.js +0 -10
  52. package/dist/render.js +0 -75
  53. package/dist/sprite.js +0 -149
  54. package/dist/timers.js +0 -23
  55. /package/dist/{pointer-info.js → internal/AnimationSystem.d.ts} +0 -0
@@ -0,0 +1,345 @@
1
+ /** @internal */
2
+ export class AnimationSystem {
3
+ constructor() {
4
+ /** @internal */
5
+ this._animations = [];
6
+ /** @internal */
7
+ this._deformAnimations = [];
8
+ /** @internal */
9
+ this._motionAnimations = [];
10
+ /** @internal */
11
+ this._shakeAnimations = [];
12
+ /** @internal */
13
+ this._blinkAnimations = [];
14
+ /** @internal */
15
+ this._flickerAnimations = [];
16
+ /** @internal */
17
+ this._renderDataSprites = new Set();
18
+ }
19
+ update(dtMs) {
20
+ const animations = this._animations;
21
+ const toRemove = [];
22
+ for (let i = 0; i < animations.length; i++) {
23
+ const anim = animations[i];
24
+ anim.elapsed += dtMs;
25
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
26
+ anim.sprite[anim.property] = anim.from + (anim.to - anim.from) * t;
27
+ if (t >= 1) {
28
+ toRemove.push(i);
29
+ anim.onComplete?.();
30
+ }
31
+ }
32
+ for (let i = toRemove.length - 1; i >= 0; i--) {
33
+ animations.splice(toRemove[i], 1);
34
+ }
35
+ const deformAnimations = this._deformAnimations;
36
+ const deformToRemove = [];
37
+ for (let i = 0; i < deformAnimations.length; i++) {
38
+ const anim = deformAnimations[i];
39
+ anim.elapsed += dtMs;
40
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
41
+ if (t >= 1) {
42
+ deformToRemove.push(i);
43
+ anim.onComplete?.();
44
+ }
45
+ }
46
+ for (let i = deformToRemove.length - 1; i >= 0; i--) {
47
+ deformAnimations.splice(deformToRemove[i], 1);
48
+ }
49
+ const motionAnimations = this._motionAnimations;
50
+ const motionToRemove = [];
51
+ for (let i = 0; i < motionAnimations.length; i++) {
52
+ const anim = motionAnimations[i];
53
+ anim.elapsed += dtMs;
54
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
55
+ if (anim.kind === "arc") {
56
+ anim.sprite.x = anim.fromX + (anim.toX - anim.fromX) * t;
57
+ anim.sprite.y =
58
+ anim.fromY +
59
+ (anim.toY - anim.fromY) * t -
60
+ anim.magnitude * 4 * t * (1 - t);
61
+ }
62
+ else if (anim.kind === "bounce") {
63
+ anim.sprite.x = anim.fromX;
64
+ anim.sprite.y = anim.fromY - anim.magnitude * 4 * t * (1 - t);
65
+ }
66
+ else {
67
+ anim.sprite.x = anim.fromX;
68
+ anim.sprite.y = anim.fromY - Math.sin(t * Math.PI) * anim.magnitude;
69
+ }
70
+ if (t >= 1) {
71
+ motionToRemove.push(i);
72
+ anim.onComplete?.();
73
+ }
74
+ }
75
+ for (let i = motionToRemove.length - 1; i >= 0; i--) {
76
+ motionAnimations.splice(motionToRemove[i], 1);
77
+ }
78
+ this.rebuildRenderData(dtMs);
79
+ }
80
+ animateAlpha(sprite, to, durationMs, onComplete) {
81
+ const animations = this._animations.filter((a) => !(a.sprite === sprite && a.property === "alpha"));
82
+ animations.push({
83
+ sprite,
84
+ property: "alpha",
85
+ from: sprite.alpha,
86
+ to,
87
+ durationMs,
88
+ elapsed: 0,
89
+ onComplete,
90
+ });
91
+ this._animations = animations;
92
+ }
93
+ animateRotation(sprite, to, durationMs, onComplete) {
94
+ const animations = this._animations.filter((a) => !(a.sprite === sprite && a.property === "rotation"));
95
+ animations.push({
96
+ sprite,
97
+ property: "rotation",
98
+ from: sprite.rotation,
99
+ to,
100
+ durationMs,
101
+ elapsed: 0,
102
+ onComplete,
103
+ });
104
+ this._animations = animations;
105
+ }
106
+ animateDeform(sprite, toScaleX, toScaleY, pivotX, pivotY, durationMs, onComplete) {
107
+ const current = sprite._renderData;
108
+ const fromScaleX = current?.scaleX ?? 1;
109
+ const fromScaleY = current?.scaleY ?? 1;
110
+ const deformAnimations = this._deformAnimations.filter((a) => a.sprite !== sprite);
111
+ deformAnimations.push({
112
+ sprite,
113
+ fromScaleX,
114
+ fromScaleY,
115
+ toScaleX: this.sanitizeScale(toScaleX),
116
+ toScaleY: this.sanitizeScale(toScaleY),
117
+ pivotX: this.sanitizePivot(pivotX),
118
+ pivotY: this.sanitizePivot(pivotY),
119
+ durationMs,
120
+ elapsed: 0,
121
+ onComplete,
122
+ });
123
+ this._deformAnimations = deformAnimations;
124
+ }
125
+ animateShake(sprite, intensity, durationMs, onComplete) {
126
+ const animations = this._shakeAnimations.filter((a) => a.sprite !== sprite);
127
+ animations.push({
128
+ sprite,
129
+ intensity: this.sanitizeNonNegative(intensity),
130
+ durationMs: this.sanitizeDuration(durationMs),
131
+ elapsed: 0,
132
+ onComplete,
133
+ });
134
+ this._shakeAnimations = animations;
135
+ }
136
+ animateBounce(sprite, height, durationMs, onComplete) {
137
+ this.replaceMotionAnimation({
138
+ sprite,
139
+ kind: "bounce",
140
+ fromX: sprite.x,
141
+ fromY: sprite.y,
142
+ toX: sprite.x,
143
+ toY: sprite.y,
144
+ magnitude: this.sanitizeNonNegative(height),
145
+ durationMs: this.sanitizeDuration(durationMs),
146
+ elapsed: 0,
147
+ onComplete,
148
+ });
149
+ }
150
+ animateFloat(sprite, distance, durationMs, onComplete) {
151
+ this.replaceMotionAnimation({
152
+ sprite,
153
+ kind: "float",
154
+ fromX: sprite.x,
155
+ fromY: sprite.y,
156
+ toX: sprite.x,
157
+ toY: sprite.y,
158
+ magnitude: this.sanitizeNonNegative(distance),
159
+ durationMs: this.sanitizeDuration(durationMs),
160
+ elapsed: 0,
161
+ onComplete,
162
+ });
163
+ }
164
+ animateArc(sprite, toX, toY, arcHeight, durationMs, onComplete) {
165
+ this.replaceMotionAnimation({
166
+ sprite,
167
+ kind: "arc",
168
+ fromX: sprite.x,
169
+ fromY: sprite.y,
170
+ toX,
171
+ toY,
172
+ magnitude: this.sanitizeNonNegative(arcHeight),
173
+ durationMs: this.sanitizeDuration(durationMs),
174
+ elapsed: 0,
175
+ onComplete,
176
+ });
177
+ }
178
+ animateBlink(sprite, times, durationMs, onComplete) {
179
+ const animations = this._blinkAnimations.filter((a) => a.sprite !== sprite);
180
+ animations.push({
181
+ sprite,
182
+ times: Math.max(1, Math.round(Number.isFinite(times) ? times : 1)),
183
+ durationMs: this.sanitizeDuration(durationMs),
184
+ elapsed: 0,
185
+ onComplete,
186
+ });
187
+ this._blinkAnimations = animations;
188
+ }
189
+ animateFlicker(sprite, durationMs, onComplete) {
190
+ const animations = this._flickerAnimations.filter((a) => a.sprite !== sprite);
191
+ animations.push({
192
+ sprite,
193
+ durationMs: this.sanitizeDuration(durationMs),
194
+ elapsed: 0,
195
+ onComplete,
196
+ });
197
+ this._flickerAnimations = animations;
198
+ }
199
+ clearSpriteAnimations(sprite) {
200
+ this._animations = this._animations.filter((a) => a.sprite !== sprite);
201
+ this._deformAnimations = this._deformAnimations.filter((a) => a.sprite !== sprite);
202
+ this._motionAnimations = this._motionAnimations.filter((a) => a.sprite !== sprite);
203
+ this._shakeAnimations = this._shakeAnimations.filter((a) => a.sprite !== sprite);
204
+ this._blinkAnimations = this._blinkAnimations.filter((a) => a.sprite !== sprite);
205
+ this._flickerAnimations = this._flickerAnimations.filter((a) => a.sprite !== sprite);
206
+ this._renderDataSprites.delete(sprite);
207
+ sprite._renderData = null;
208
+ }
209
+ clearAll() {
210
+ for (const sprite of this._renderDataSprites) {
211
+ sprite._renderData = null;
212
+ }
213
+ this._animations = [];
214
+ this._deformAnimations = [];
215
+ this._motionAnimations = [];
216
+ this._shakeAnimations = [];
217
+ this._blinkAnimations = [];
218
+ this._flickerAnimations = [];
219
+ this._renderDataSprites.clear();
220
+ }
221
+ /** @internal */
222
+ sanitizeScale(value) {
223
+ if (!Number.isFinite(value))
224
+ return 1;
225
+ return Math.max(0, value);
226
+ }
227
+ /** @internal */
228
+ sanitizeDuration(value) {
229
+ if (!Number.isFinite(value))
230
+ return 1;
231
+ return Math.max(1, value);
232
+ }
233
+ /** @internal */
234
+ sanitizeNonNegative(value) {
235
+ if (!Number.isFinite(value))
236
+ return 0;
237
+ return Math.max(0, value);
238
+ }
239
+ /** @internal */
240
+ sanitizePivot(value) {
241
+ if (!Number.isFinite(value))
242
+ return 0.5;
243
+ return Math.max(0, Math.min(1, value));
244
+ }
245
+ /** @internal */
246
+ replaceMotionAnimation(entry) {
247
+ const animations = this._motionAnimations.filter((a) => a.sprite !== entry.sprite);
248
+ animations.push(entry);
249
+ this._motionAnimations = animations;
250
+ }
251
+ /** @internal */
252
+ rebuildRenderData(dtMs) {
253
+ const next = new Map();
254
+ const nextSprites = new Set();
255
+ const upsert = (sprite) => {
256
+ let state = next.get(sprite);
257
+ if (!state) {
258
+ state = {
259
+ scaleX: 1,
260
+ scaleY: 1,
261
+ pivotX: 0.5,
262
+ pivotY: 0.5,
263
+ offsetX: 0,
264
+ offsetY: 0,
265
+ alphaMultiplier: 1,
266
+ };
267
+ next.set(sprite, state);
268
+ nextSprites.add(sprite);
269
+ }
270
+ return state;
271
+ };
272
+ for (const anim of this._deformAnimations) {
273
+ const state = upsert(anim.sprite);
274
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
275
+ state.scaleX = anim.fromScaleX + (anim.toScaleX - anim.fromScaleX) * t;
276
+ state.scaleY = anim.fromScaleY + (anim.toScaleY - anim.fromScaleY) * t;
277
+ state.pivotX = anim.pivotX;
278
+ state.pivotY = anim.pivotY;
279
+ }
280
+ const shakeToRemove = [];
281
+ for (let i = 0; i < this._shakeAnimations.length; i++) {
282
+ const anim = this._shakeAnimations[i];
283
+ anim.elapsed += dtMs;
284
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
285
+ const decay = 1 - t;
286
+ const state = upsert(anim.sprite);
287
+ state.offsetX += (Math.random() * 2 - 1) * anim.intensity * decay;
288
+ state.offsetY += (Math.random() * 2 - 1) * anim.intensity * decay;
289
+ if (t >= 1) {
290
+ shakeToRemove.push(i);
291
+ anim.onComplete?.();
292
+ }
293
+ }
294
+ for (let i = shakeToRemove.length - 1; i >= 0; i--) {
295
+ this._shakeAnimations.splice(shakeToRemove[i], 1);
296
+ }
297
+ const blinkToRemove = [];
298
+ for (let i = 0; i < this._blinkAnimations.length; i++) {
299
+ const anim = this._blinkAnimations[i];
300
+ anim.elapsed += dtMs;
301
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
302
+ const state = upsert(anim.sprite);
303
+ const step = Math.floor(t * anim.times * 2);
304
+ state.alphaMultiplier *= step % 2 === 0 ? 1 : 0;
305
+ if (t >= 1) {
306
+ blinkToRemove.push(i);
307
+ anim.onComplete?.();
308
+ }
309
+ }
310
+ for (let i = blinkToRemove.length - 1; i >= 0; i--) {
311
+ this._blinkAnimations.splice(blinkToRemove[i], 1);
312
+ }
313
+ const flickerToRemove = [];
314
+ for (let i = 0; i < this._flickerAnimations.length; i++) {
315
+ const anim = this._flickerAnimations[i];
316
+ anim.elapsed += dtMs;
317
+ const t = Math.min(anim.elapsed / anim.durationMs, 1);
318
+ const state = upsert(anim.sprite);
319
+ state.alphaMultiplier *= 0.25 + Math.random() * 0.75;
320
+ if (t >= 1) {
321
+ flickerToRemove.push(i);
322
+ anim.onComplete?.();
323
+ }
324
+ }
325
+ for (let i = flickerToRemove.length - 1; i >= 0; i--) {
326
+ this._flickerAnimations.splice(flickerToRemove[i], 1);
327
+ }
328
+ for (const sprite of this._renderDataSprites) {
329
+ if (!nextSprites.has(sprite)) {
330
+ sprite._renderData = null;
331
+ }
332
+ }
333
+ for (const [sprite, state] of next.entries()) {
334
+ const isDefault = state.scaleX === 1 &&
335
+ state.scaleY === 1 &&
336
+ state.pivotX === 0.5 &&
337
+ state.pivotY === 0.5 &&
338
+ state.offsetX === 0 &&
339
+ state.offsetY === 0 &&
340
+ state.alphaMultiplier === 1;
341
+ sprite._renderData = isDefault ? null : state;
342
+ }
343
+ this._renderDataSprites = nextSprites;
344
+ }
345
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,101 @@
1
+ /** @internal */
2
+ export class AssetSystem {
3
+ constructor() {
4
+ /** @internal */
5
+ this._queuedImages = new Map();
6
+ /** @internal */
7
+ this._loadedImages = new Map();
8
+ /** @internal */
9
+ this._imageSources = new Map();
10
+ /** @internal */
11
+ this._imageVersions = new Map();
12
+ }
13
+ queueImage(key, src) {
14
+ const safeKey = key.trim();
15
+ const safeSrc = src.trim();
16
+ if (safeKey.length === 0) {
17
+ throw new Error("MinimoJS: Image keys must be non-empty strings.");
18
+ }
19
+ if (safeSrc.length === 0) {
20
+ throw new Error("MinimoJS: Image sources must be non-empty strings.");
21
+ }
22
+ const existingSrc = this._imageSources.get(safeKey);
23
+ if (existingSrc !== undefined && existingSrc !== safeSrc) {
24
+ throw new Error(`MinimoJS: Image key "${safeKey}" is already registered with a different source.`);
25
+ }
26
+ this._imageSources.set(safeKey, safeSrc);
27
+ this._queuedImages.set(safeKey, safeSrc);
28
+ }
29
+ createTexture(key, width, height, painter) {
30
+ const safeKey = key.trim();
31
+ if (safeKey.length === 0) {
32
+ throw new Error("MinimoJS: Texture keys must be non-empty strings.");
33
+ }
34
+ if (typeof painter !== "function") {
35
+ throw new Error("MinimoJS: createTexture() requires a painter function.");
36
+ }
37
+ const canvas = document.createElement("canvas");
38
+ canvas.width = Math.max(1, Math.round(Number.isFinite(width) ? width : 1));
39
+ canvas.height = Math.max(1, Math.round(Number.isFinite(height) ? height : 1));
40
+ const ctx = canvas.getContext("2d");
41
+ if (!ctx) {
42
+ throw new Error("MinimoJS: Could not acquire a texture rendering context.");
43
+ }
44
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
45
+ painter(ctx, canvas);
46
+ this._queuedImages.delete(safeKey);
47
+ this._imageSources.delete(safeKey);
48
+ this._loadedImages.set(safeKey, canvas);
49
+ this.bumpImageVersion(safeKey);
50
+ return canvas;
51
+ }
52
+ getImage(key) {
53
+ return this._loadedImages.get(key);
54
+ }
55
+ getImageVersion(key) {
56
+ return this._imageVersions.get(key) ?? 0;
57
+ }
58
+ hasImage(key) {
59
+ return this._loadedImages.has(key);
60
+ }
61
+ getQueuedImageCount() {
62
+ let total = 0;
63
+ for (const key of this._queuedImages.keys()) {
64
+ if (!this._loadedImages.has(key)) {
65
+ total++;
66
+ }
67
+ }
68
+ return total;
69
+ }
70
+ async loadQueuedImages(onProgress) {
71
+ const entries = [...this._queuedImages.entries()].filter(([key]) => !this._loadedImages.has(key));
72
+ const total = entries.length;
73
+ let loaded = 0;
74
+ onProgress?.(0, total, null);
75
+ if (total === 0)
76
+ return;
77
+ await Promise.all(entries.map(async ([key, src]) => {
78
+ const image = await this.loadImage(src);
79
+ this._loadedImages.set(key, image);
80
+ this.bumpImageVersion(key);
81
+ loaded += 1;
82
+ onProgress?.(loaded, total, key);
83
+ }));
84
+ this._queuedImages.clear();
85
+ }
86
+ /** @internal */
87
+ loadImage(src) {
88
+ return new Promise((resolve, reject) => {
89
+ const image = new Image();
90
+ image.onload = () => resolve(image);
91
+ image.onerror = () => {
92
+ reject(new Error(`MinimoJS: Failed to load image "${src}".`));
93
+ };
94
+ image.src = src;
95
+ });
96
+ }
97
+ /** @internal */
98
+ bumpImageVersion(key) {
99
+ this._imageVersions.set(key, (this._imageVersions.get(key) ?? 0) + 1);
100
+ }
101
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ /** @internal */
2
+ export class BackgroundSystem {
3
+ constructor() {
4
+ /** @internal */
5
+ this._layers = [];
6
+ }
7
+ add(layer) {
8
+ this._layers.push(layer);
9
+ return layer;
10
+ }
11
+ destroyLayer(layer) {
12
+ const index = this._layers.indexOf(layer);
13
+ if (index !== -1) {
14
+ this._layers.splice(index, 1);
15
+ }
16
+ }
17
+ getLayers() {
18
+ return [...this._layers];
19
+ }
20
+ getMutableLayers() {
21
+ return this._layers;
22
+ }
23
+ clearAll() {
24
+ this._layers = [];
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ /** @internal */
2
+ export class CanvasSystem {
3
+ constructor(/** @internal */ canvas) {
4
+ this.canvas = canvas;
5
+ /** @internal */
6
+ this.onResize = () => this.applyResponsiveCanvasLayout();
7
+ /** @internal */
8
+ this.onViewportChange = () => this.applyResponsiveCanvasLayout();
9
+ }
10
+ initialize() {
11
+ const mountCanvas = () => {
12
+ if (document.body && !this.canvas.isConnected) {
13
+ document.body.appendChild(this.canvas);
14
+ }
15
+ this.applyResponsiveCanvasLayout();
16
+ };
17
+ if (document.body) {
18
+ mountCanvas();
19
+ }
20
+ else {
21
+ window.addEventListener("DOMContentLoaded", mountCanvas, { once: true });
22
+ }
23
+ window.addEventListener("resize", this.onResize);
24
+ window.visualViewport?.addEventListener("resize", this.onViewportChange);
25
+ window.visualViewport?.addEventListener("scroll", this.onViewportChange);
26
+ }
27
+ /** @internal */
28
+ applyResponsiveCanvasLayout() {
29
+ if (!document.body)
30
+ return;
31
+ const viewportW = Math.max(1, Math.floor(window.visualViewport?.width ??
32
+ window.innerWidth ??
33
+ document.documentElement.clientWidth ??
34
+ 1));
35
+ const viewportH = Math.max(1, Math.floor(window.visualViewport?.height ??
36
+ window.innerHeight ??
37
+ document.documentElement.clientHeight ??
38
+ 1));
39
+ document.body.style.margin = "0";
40
+ document.body.style.height = `${viewportH}px`;
41
+ document.body.style.minHeight = `${viewportH}px`;
42
+ document.body.style.display = "grid";
43
+ document.body.style.placeItems = "center";
44
+ document.body.style.touchAction = "manipulation";
45
+ document.body.style.overflow = "hidden";
46
+ const scale = Math.min(viewportW / this.canvas.width, viewportH / this.canvas.height);
47
+ const safeScale = Number.isFinite(scale) && scale > 0 ? scale : 1;
48
+ this.canvas.style.width = `${Math.floor(this.canvas.width * safeScale)}px`;
49
+ this.canvas.style.height = `${Math.floor(this.canvas.height * safeScale)}px`;
50
+ }
51
+ }
@@ -0,0 +1 @@
1
+ export {};