canvasengine 2.0.0-beta.5 → 2.0.0-beta.51

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 (172) hide show
  1. package/dist/components/Button.d.ts +185 -0
  2. package/dist/components/Button.d.ts.map +1 -0
  3. package/dist/components/Canvas.d.ts +17 -0
  4. package/dist/components/Canvas.d.ts.map +1 -0
  5. package/dist/components/Container.d.ts +86 -0
  6. package/dist/components/Container.d.ts.map +1 -0
  7. package/dist/components/DOMContainer.d.ts +98 -0
  8. package/dist/components/DOMContainer.d.ts.map +1 -0
  9. package/dist/components/DOMElement.d.ts +54 -0
  10. package/dist/components/DOMElement.d.ts.map +1 -0
  11. package/dist/components/DOMSprite.d.ts +127 -0
  12. package/dist/components/DOMSprite.d.ts.map +1 -0
  13. package/dist/components/DisplayObject.d.ts +94 -0
  14. package/dist/components/DisplayObject.d.ts.map +1 -0
  15. package/dist/components/FocusContainer.d.ts +129 -0
  16. package/dist/components/FocusContainer.d.ts.map +1 -0
  17. package/dist/components/Graphic.d.ts +64 -0
  18. package/dist/components/Graphic.d.ts.map +1 -0
  19. package/dist/components/Joystick.d.ts +36 -0
  20. package/dist/components/Joystick.d.ts.map +1 -0
  21. package/dist/components/Mesh.d.ts +208 -0
  22. package/dist/components/Mesh.d.ts.map +1 -0
  23. package/dist/components/NineSliceSprite.d.ts +16 -0
  24. package/dist/components/NineSliceSprite.d.ts.map +1 -0
  25. package/dist/components/ParticleEmitter.d.ts +4 -0
  26. package/dist/components/ParticleEmitter.d.ts.map +1 -0
  27. package/dist/components/Scene.d.ts +2 -0
  28. package/dist/components/Scene.d.ts.map +1 -0
  29. package/dist/components/Sprite.d.ts +242 -0
  30. package/dist/components/Sprite.d.ts.map +1 -0
  31. package/dist/components/Text.d.ts +25 -0
  32. package/dist/components/Text.d.ts.map +1 -0
  33. package/dist/components/TilingSprite.d.ts +17 -0
  34. package/dist/components/TilingSprite.d.ts.map +1 -0
  35. package/dist/components/Video.d.ts +14 -0
  36. package/dist/components/Video.d.ts.map +1 -0
  37. package/dist/components/Viewport.d.ts +121 -0
  38. package/dist/components/Viewport.d.ts.map +1 -0
  39. package/dist/components/index.d.ts +20 -0
  40. package/dist/components/index.d.ts.map +1 -0
  41. package/dist/components/types/DisplayObject.d.ts +106 -0
  42. package/dist/components/types/DisplayObject.d.ts.map +1 -0
  43. package/dist/components/types/MouseEvent.d.ts +4 -0
  44. package/dist/components/types/MouseEvent.d.ts.map +1 -0
  45. package/dist/components/types/Spritesheet.d.ts +248 -0
  46. package/dist/components/types/Spritesheet.d.ts.map +1 -0
  47. package/dist/components/types/index.d.ts +4 -0
  48. package/dist/components/types/index.d.ts.map +1 -0
  49. package/dist/directives/Controls.d.ts +112 -0
  50. package/dist/directives/Controls.d.ts.map +1 -0
  51. package/dist/directives/ControlsBase.d.ts +199 -0
  52. package/dist/directives/ControlsBase.d.ts.map +1 -0
  53. package/dist/directives/Drag.d.ts +69 -0
  54. package/dist/directives/Drag.d.ts.map +1 -0
  55. package/dist/directives/Flash.d.ts +116 -0
  56. package/dist/directives/Flash.d.ts.map +1 -0
  57. package/dist/directives/FocusNavigation.d.ts +52 -0
  58. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  59. package/dist/directives/GamepadControls.d.ts +224 -0
  60. package/dist/directives/GamepadControls.d.ts.map +1 -0
  61. package/dist/directives/JoystickControls.d.ts +171 -0
  62. package/dist/directives/JoystickControls.d.ts.map +1 -0
  63. package/dist/directives/KeyboardControls.d.ts +219 -0
  64. package/dist/directives/KeyboardControls.d.ts.map +1 -0
  65. package/dist/directives/Scheduler.d.ts +35 -0
  66. package/dist/directives/Scheduler.d.ts.map +1 -0
  67. package/dist/directives/Shake.d.ts +98 -0
  68. package/dist/directives/Shake.d.ts.map +1 -0
  69. package/dist/directives/Sound.d.ts +25 -0
  70. package/dist/directives/Sound.d.ts.map +1 -0
  71. package/dist/directives/Transition.d.ts +10 -0
  72. package/dist/directives/Transition.d.ts.map +1 -0
  73. package/dist/directives/ViewportCull.d.ts +11 -0
  74. package/dist/directives/ViewportCull.d.ts.map +1 -0
  75. package/dist/directives/ViewportFollow.d.ts +18 -0
  76. package/dist/directives/ViewportFollow.d.ts.map +1 -0
  77. package/dist/directives/index.d.ts +13 -0
  78. package/dist/directives/index.d.ts.map +1 -0
  79. package/dist/engine/FocusManager.d.ts +174 -0
  80. package/dist/engine/FocusManager.d.ts.map +1 -0
  81. package/dist/engine/animation.d.ts +72 -0
  82. package/dist/engine/animation.d.ts.map +1 -0
  83. package/dist/engine/bootstrap.d.ts +48 -0
  84. package/dist/engine/bootstrap.d.ts.map +1 -0
  85. package/dist/engine/directive.d.ts +13 -0
  86. package/dist/engine/directive.d.ts.map +1 -0
  87. package/dist/engine/reactive.d.ts +134 -0
  88. package/dist/engine/reactive.d.ts.map +1 -0
  89. package/dist/engine/signal.d.ts +71 -0
  90. package/dist/engine/signal.d.ts.map +1 -0
  91. package/dist/engine/trigger.d.ts +54 -0
  92. package/dist/engine/trigger.d.ts.map +1 -0
  93. package/dist/engine/utils.d.ts +89 -0
  94. package/dist/engine/utils.d.ts.map +1 -0
  95. package/dist/hooks/addContext.d.ts +2 -0
  96. package/dist/hooks/addContext.d.ts.map +1 -0
  97. package/dist/hooks/useFocus.d.ts +60 -0
  98. package/dist/hooks/useFocus.d.ts.map +1 -0
  99. package/dist/hooks/useProps.d.ts +42 -0
  100. package/dist/hooks/useProps.d.ts.map +1 -0
  101. package/dist/hooks/useRef.d.ts +4 -0
  102. package/dist/hooks/useRef.d.ts.map +1 -0
  103. package/dist/index-DaGekQUW.js +2218 -0
  104. package/dist/index-DaGekQUW.js.map +1 -0
  105. package/dist/index.d.ts +19 -1099
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.global.js +5 -0
  108. package/dist/index.global.js.map +1 -0
  109. package/dist/index.js +11749 -2901
  110. package/dist/index.js.map +1 -1
  111. package/dist/utils/Ease.d.ts +17 -0
  112. package/dist/utils/Ease.d.ts.map +1 -0
  113. package/dist/utils/GlobalAssetLoader.d.ts +141 -0
  114. package/dist/utils/GlobalAssetLoader.d.ts.map +1 -0
  115. package/dist/utils/RadialGradient.d.ts +57 -0
  116. package/dist/utils/RadialGradient.d.ts.map +1 -0
  117. package/dist/utils/functions.d.ts +2 -0
  118. package/dist/utils/functions.d.ts.map +1 -0
  119. package/dist/utils/tabindex.d.ts +16 -0
  120. package/dist/utils/tabindex.d.ts.map +1 -0
  121. package/package.json +13 -7
  122. package/src/components/Button.ts +399 -0
  123. package/src/components/Canvas.ts +62 -46
  124. package/src/components/Container.ts +21 -2
  125. package/src/components/DOMContainer.ts +379 -0
  126. package/src/components/DOMElement.ts +556 -0
  127. package/src/components/DOMSprite.ts +1040 -0
  128. package/src/components/DisplayObject.ts +392 -201
  129. package/src/components/FocusContainer.ts +368 -0
  130. package/src/components/Graphic.ts +227 -66
  131. package/src/components/Joystick.ts +363 -0
  132. package/src/components/Mesh.ts +222 -0
  133. package/src/components/NineSliceSprite.ts +4 -1
  134. package/src/components/ParticleEmitter.ts +12 -8
  135. package/src/components/Sprite.ts +297 -31
  136. package/src/components/Text.ts +125 -18
  137. package/src/components/Video.ts +2 -2
  138. package/src/components/Viewport.ts +118 -63
  139. package/src/components/index.ts +9 -2
  140. package/src/components/types/DisplayObject.ts +41 -4
  141. package/src/components/types/Spritesheet.ts +0 -118
  142. package/src/directives/Controls.ts +254 -0
  143. package/src/directives/ControlsBase.ts +267 -0
  144. package/src/directives/Drag.ts +357 -52
  145. package/src/directives/Flash.ts +419 -0
  146. package/src/directives/FocusNavigation.ts +113 -0
  147. package/src/directives/GamepadControls.ts +537 -0
  148. package/src/directives/JoystickControls.ts +396 -0
  149. package/src/directives/KeyboardControls.ts +85 -430
  150. package/src/directives/Scheduler.ts +12 -4
  151. package/src/directives/Shake.ts +298 -0
  152. package/src/directives/Sound.ts +94 -31
  153. package/src/directives/ViewportFollow.ts +40 -9
  154. package/src/directives/index.ts +12 -6
  155. package/src/engine/FocusManager.ts +510 -0
  156. package/src/engine/animation.ts +175 -21
  157. package/src/engine/bootstrap.ts +93 -3
  158. package/src/engine/directive.ts +4 -4
  159. package/src/engine/reactive.ts +901 -161
  160. package/src/engine/signal.ts +113 -25
  161. package/src/engine/trigger.ts +34 -7
  162. package/src/engine/utils.ts +19 -3
  163. package/src/hooks/useFocus.ts +91 -0
  164. package/src/hooks/useProps.ts +1 -1
  165. package/src/index.ts +8 -2
  166. package/src/types/pixi-cull.d.ts +7 -0
  167. package/src/utils/GlobalAssetLoader.ts +257 -0
  168. package/src/utils/functions.ts +7 -0
  169. package/src/utils/tabindex.ts +70 -0
  170. package/testing/index.ts +35 -4
  171. package/tsconfig.json +18 -0
  172. package/vite.config.ts +39 -0
@@ -1,5 +1,7 @@
1
- import { computed, effect, isSignal, Signal, WritableSignal } from "@signe/reactive";
1
+ import { Howl } from 'howler';
2
+ import { computed, effect, isSignal, Signal } from "@signe/reactive";
2
3
  import {
4
+ Application,
3
5
  Assets,
4
6
  Container,
5
7
  Sprite as PixiSprite,
@@ -10,7 +12,9 @@ import { Subscription } from "rxjs";
10
12
  import {
11
13
  Element,
12
14
  createComponent,
15
+ isElement,
13
16
  registerComponent,
17
+ isElementFrozen,
14
18
  } from "../engine/reactive";
15
19
  import { arrayEquals, isFunction } from "../engine/utils";
16
20
  import { DisplayObject } from "./DisplayObject";
@@ -24,6 +28,8 @@ import {
24
28
  import { ComponentFunction } from "../engine/signal";
25
29
  import { DisplayObjectProps } from "./types/DisplayObject";
26
30
  import { AnimatedSignal, isAnimatedSignal } from "../engine/animation";
31
+ import { Layout } from '@pixi/layout';
32
+ import { GlobalAssetLoader } from "../utils/GlobalAssetLoader";
27
33
 
28
34
  const log = console.log;
29
35
 
@@ -73,15 +79,127 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
73
79
  private subscriptionSheet: Subscription[] = [];
74
80
  private sheetParams: any = {};
75
81
  private sheetCurrentAnimation: string = StandardAnimation.Stand;
82
+ private app: Application | null = null;
76
83
  onFinish: () => void;
84
+ private globalLoader: GlobalAssetLoader | null = null;
85
+ private trackedAssetIds: Set<string> = new Set();
86
+
87
+ get renderer() {
88
+ return this.app?.renderer;
89
+ }
77
90
 
78
91
  private currentAnimationContainer: Container | null = null;
79
92
 
93
+ /**
94
+ * Auto-detects image dimensions by loading the image and reading its natural size
95
+ * This is used when width/height are not explicitly provided in the spritesheet definition
96
+ *
97
+ * @param imagePath - Path to the image file
98
+ * @returns Object containing the detected width and height of the image
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const { width, height } = await sprite.detectImageDimensions('path/to/image.png');
103
+ * // width: 256, height: 128
104
+ * ```
105
+ */
106
+ private async detectImageDimensions(imagePath: string): Promise<{ width: number; height: number }> {
107
+ if (!imagePath || typeof imagePath !== 'string' || imagePath.trim() === '') {
108
+ throw new Error(`Invalid image path provided to detectImageDimensions: ${imagePath}`);
109
+ }
110
+
111
+ // Register asset in global loader if available
112
+ let assetId: string | null = null;
113
+ if (this.globalLoader) {
114
+ assetId = this.globalLoader.registerAsset(imagePath);
115
+ this.trackedAssetIds.add(assetId);
116
+ }
117
+
118
+ const texture = await Assets.load(imagePath, (progress) => {
119
+ if (this.globalLoader && assetId) {
120
+ this.globalLoader.updateProgress(assetId, progress);
121
+ }
122
+ });
123
+
124
+ // Mark as complete
125
+ if (this.globalLoader && assetId) {
126
+ this.globalLoader.completeAsset(assetId);
127
+ }
128
+
129
+ return {
130
+ width: texture.width,
131
+ height: texture.height,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Creates textures from a spritesheet image by cutting it into frames
137
+ * Automatically detects image dimensions if width/height are not provided
138
+ *
139
+ * @param options - Texture options containing image path, dimensions, and frame configuration
140
+ * @returns A 2D array of textures organized by rows and columns
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // With explicit dimensions
145
+ * const textures = await sprite.createTextures({
146
+ * image: 'path/to/image.png',
147
+ * width: 256,
148
+ * height: 128,
149
+ * framesWidth: 4,
150
+ * framesHeight: 2,
151
+ * spriteWidth: 64,
152
+ * spriteHeight: 64
153
+ * });
154
+ *
155
+ * // Without dimensions (automatically detected)
156
+ * const textures = await sprite.createTextures({
157
+ * image: 'path/to/image.png',
158
+ * framesWidth: 4,
159
+ * framesHeight: 2,
160
+ * spriteWidth: 64,
161
+ * spriteHeight: 64
162
+ * });
163
+ * ```
164
+ */
80
165
  private async createTextures(
81
166
  options: Required<TextureOptionsMerging>
82
167
  ): Promise<Texture[][]> {
83
- const { width, height, framesHeight, framesWidth, image, offset } = options;
84
- const texture = await Assets.load(image);
168
+ let { width, height, framesHeight, framesWidth, image, offset } = options;
169
+
170
+ if (!image || typeof image !== 'string' || image.trim() === '') {
171
+ console.warn('Invalid image path provided to createTextures:', image);
172
+ return [];
173
+ }
174
+
175
+ // Register asset in global loader if available
176
+ let assetId: string | null = null;
177
+ if (this.globalLoader) {
178
+ assetId = this.globalLoader.registerAsset(image);
179
+ this.trackedAssetIds.add(assetId);
180
+ }
181
+
182
+ const texture = await Assets.load(image, (progress) => {
183
+ if (this.globalLoader && assetId) {
184
+ this.globalLoader.updateProgress(assetId, progress);
185
+ }
186
+ });
187
+
188
+ // Mark as complete
189
+ if (this.globalLoader && assetId) {
190
+ this.globalLoader.completeAsset(assetId);
191
+ }
192
+
193
+ // Auto-detect width and height from the image if not provided
194
+ if (!width || width <= 0) {
195
+ width = texture.width;
196
+ options.width = width;
197
+ }
198
+ if (!height || height <= 0) {
199
+ height = texture.height;
200
+ options.height = height;
201
+ }
202
+
85
203
  const spriteWidth = options.spriteWidth;
86
204
  const spriteHeight = options.spriteHeight;
87
205
  const frames: Texture[][] = [];
@@ -140,12 +258,30 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
140
258
  } as any;
141
259
  const {
142
260
  rectWidth,
143
- width = 0,
261
+ width: widthOption = 0,
144
262
  framesWidth = 1,
145
263
  rectHeight,
146
- height = 0,
264
+ height: heightOption = 0,
147
265
  framesHeight = 1,
266
+ image,
148
267
  } = optionsTextures;
268
+
269
+ // Auto-detect width and height from the image if not provided
270
+ let width = widthOption;
271
+ let height = heightOption;
272
+
273
+ if (image && ((!width || width <= 0) || (!height || height <= 0))) {
274
+ const dimensions = await this.detectImageDimensions(image);
275
+ if (!width || width <= 0) {
276
+ width = dimensions.width;
277
+ optionsTextures.width = width;
278
+ }
279
+ if (!height || height <= 0) {
280
+ height = dimensions.height;
281
+ optionsTextures.height = height;
282
+ }
283
+ }
284
+
149
285
  optionsTextures.spriteWidth = rectWidth ? rectWidth : width / framesWidth;
150
286
  optionsTextures.spriteHeight = rectHeight
151
287
  ? rectHeight
@@ -163,20 +299,37 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
163
299
  }
164
300
  }
165
301
 
166
- async onMount(params: Element<CanvasSprite>) {
302
+ async onMount(params: Element<any>) {
303
+ // Set #element manually for freeze checking before calling super.onMount
304
+ // We need to set it early so update() can check freeze state
305
+ (this as any)['#element'] = params;
306
+
167
307
  const { props, propObservables } = params;
168
308
  const tick: Signal = props.context.tick;
169
309
  const sheet = props.sheet ?? {};
310
+ const definition = props.sheet?.definition ?? {};
311
+ this.app = props.context.app();
312
+ // Get global loader from context if available
313
+ this.globalLoader = props.context?.globalLoader || null;
170
314
  if (sheet?.onFinish) {
171
315
  this.onFinish = sheet.onFinish;
172
316
  }
173
317
  this.subscriptionTick = tick.observable.subscribe((value) => {
318
+ if (this.destroyed) return
174
319
  this.update(value);
175
320
  });
176
- if (props.sheet?.definition) {
177
- this.spritesheet = props.sheet.definition;
321
+ if (definition) {
322
+ const resolvedDefinition = definition instanceof Promise ? await definition : definition;
323
+ this.spritesheet = resolvedDefinition.value ?? resolvedDefinition;
178
324
  await this.createAnimations();
179
325
  }
326
+ if (sheet?.params) {
327
+ this.sheetParams = sheet.params;
328
+ }
329
+ if (sheet?.playing && this.has(sheet.playing)) {
330
+ this.sheetCurrentAnimation = sheet.playing;
331
+ this.play(this.sheetCurrentAnimation, [this.sheetParams]);
332
+ }
180
333
  if (sheet.params) {
181
334
  for (let key in propObservables?.sheet["params"]) {
182
335
  const value = propObservables?.sheet["params"][key] as Signal;
@@ -184,11 +337,13 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
184
337
  this.subscriptionSheet.push(
185
338
  value.observable.subscribe((value) => {
186
339
  if (this.animations.size == 0) return;
187
- this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
340
+ if (!this.has(this.sheetCurrentAnimation)) return;
341
+ this.play(this.sheetCurrentAnimation, [{ ...this.sheetParams, [key]: value }]);
188
342
  })
189
343
  );
190
344
  } else {
191
- this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
345
+ if (!this.has(this.sheetCurrentAnimation)) continue;
346
+ this.play(this.sheetCurrentAnimation, [{ ...this.sheetParams, [key]: value }]);
192
347
  }
193
348
  }
194
349
  }
@@ -218,20 +373,46 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
218
373
  this.sheetCurrentAnimation = StandardAnimation.Stand;
219
374
  }
220
375
 
221
- this.play(this.sheetCurrentAnimation, [this.sheetParams]);
376
+ if (this.spritesheet && this.has(this.sheetCurrentAnimation)) this.play(this.sheetCurrentAnimation, [this.sheetParams]);
222
377
  });
223
-
224
378
  super.onMount(params);
225
379
  }
226
380
 
227
381
  async onUpdate(props) {
382
+ if (this.destroyed) return
228
383
  super.onUpdate(props);
229
384
 
385
+ // Initialize globalLoader from context if not already set
386
+ if (!this.globalLoader && props.context?.globalLoader) {
387
+ this.globalLoader = props.context.globalLoader;
388
+ }
389
+
230
390
  const setTexture = async (image: string) => {
391
+ if (!image || typeof image !== 'string' || image.trim() === '') {
392
+ console.warn('Invalid image path provided to setTexture:', image);
393
+ return null;
394
+ }
395
+
396
+ // Register asset in global loader if available
397
+ let assetId: string | null = null;
398
+ if (this.globalLoader) {
399
+ assetId = this.globalLoader.registerAsset(image);
400
+ this.trackedAssetIds.add(assetId);
401
+ }
402
+
231
403
  const onProgress = this.fullProps.loader?.onProgress;
232
404
  const texture = await Assets.load(image, (progress) => {
405
+ // Update global loader progress
406
+ if (this.globalLoader && assetId) {
407
+ this.globalLoader.updateProgress(assetId, progress);
408
+ }
409
+ // Call local loader callback if provided
233
410
  if (onProgress) onProgress(progress);
234
411
  if (progress == 1) {
412
+ // Mark as complete in global loader
413
+ if (this.globalLoader && assetId) {
414
+ this.globalLoader.completeAsset(assetId);
415
+ }
235
416
  const onComplete = this.fullProps.loader?.onComplete;
236
417
  if (onComplete) {
237
418
  // hack to memoize the texture
@@ -245,39 +426,71 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
245
426
  return texture
246
427
  }
247
428
 
248
- const sheet = props.sheet;
429
+ const sheet = props.sheet
430
+ const definition = props.sheet?.definition ?? {};
431
+
432
+ if (definition?.type === 'reset') {
433
+ const resolvedValue = definition.value instanceof Promise ? await definition.value : definition.value;
434
+ this.spritesheet = resolvedValue ?? definition;
435
+ await this.resetAnimations();
436
+ }
437
+
249
438
  if (sheet?.params) this.sheetParams = sheet?.params;
250
439
 
251
- if (sheet?.playing && this.isMounted) {
440
+ if (sheet?.playing && this.isMounted && this.spritesheet && this.animations.size > 0) {
252
441
  this.sheetCurrentAnimation = sheet?.playing;
253
442
  this.play(this.sheetCurrentAnimation, [this.sheetParams]);
254
443
  }
255
444
 
256
- if (props.hitbox) this.hitbox = props.hitbox;
445
+ if (props.hitbox) this.hitbox = props.hitbox.value ?? props.hitbox;
257
446
 
258
447
  if (props.scaleMode) this.baseTexture.scaleMode = props.scaleMode;
259
448
  else if (props.image && this.fullProps.rectangle === undefined) {
260
- this.texture = await setTexture(this.fullProps.image);
449
+ const texture = await setTexture(this.fullProps.image);
450
+ if (texture) {
451
+ this.texture = texture;
452
+ }
261
453
  } else if (props.texture) {
262
- this.texture = props.texture;
454
+ if (isElement(props.texture)) {
455
+ const textureInstance = props.texture.componentInstance;
456
+ textureInstance.subjectInit
457
+ .subscribe()
458
+ this.texture = this.renderer?.generateTexture(props.texture.componentInstance);
459
+ } else {
460
+ this.texture = props.texture;
461
+ }
263
462
  }
264
463
  if (props.rectangle !== undefined) {
265
464
  const { x, y, width, height } = props.rectangle?.value ?? props.rectangle;
266
465
  const texture = await setTexture(this.fullProps.image);
267
- this.texture = new Texture({
268
- source: texture.source,
269
- frame: new Rectangle(x, y, width, height),
270
- });
466
+ if (texture) {
467
+ this.texture = new Texture({
468
+ source: texture.source,
469
+ frame: new Rectangle(x, y, width, height),
470
+ });
471
+ }
271
472
  }
272
473
  }
273
474
 
274
- onDestroy(): void {
275
- super.onDestroy();
276
- this.subscriptionSheet.forEach((sub) => sub.unsubscribe());
277
- this.subscriptionTick.unsubscribe();
278
- if (this.currentAnimationContainer && this.parent instanceof Container) {
279
- this.parent.removeChild(this.currentAnimationContainer);
280
- }
475
+ async onDestroy(parent: Element, afterDestroy: () => void): Promise<void> {
476
+ const _afterDestroy = async () => {
477
+ // Clean up tracked assets from global loader
478
+ if (this.globalLoader) {
479
+ this.trackedAssetIds.forEach((assetId) => {
480
+ this.globalLoader!.removeAsset(assetId);
481
+ });
482
+ this.trackedAssetIds.clear();
483
+ }
484
+ this.subscriptionSheet.forEach((sub) => sub.unsubscribe());
485
+ this.subscriptionTick.unsubscribe();
486
+ if (this.currentAnimationContainer && this.parent instanceof Container) {
487
+ this.parent.removeChild(this.currentAnimationContainer);
488
+ }
489
+ if (afterDestroy) {
490
+ afterDestroy();
491
+ }
492
+ };
493
+ await super.onDestroy(parent, _afterDestroy);
281
494
  }
282
495
 
283
496
  has(name: string): boolean {
@@ -296,7 +509,6 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
296
509
 
297
510
  stop() {
298
511
  this.currentAnimation = null;
299
- this.destroy();
300
512
  }
301
513
 
302
514
  play(name: string, params: any[] = []) {
@@ -338,7 +550,12 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
338
550
  const sound = this.currentAnimation.data.sound;
339
551
 
340
552
  if (sound) {
341
- //RpgSound.get(sound).play()
553
+ new Howl({
554
+ src: sound,
555
+ autoplay: true,
556
+ loop: false,
557
+ volume: 1,
558
+ })
342
559
  }
343
560
 
344
561
  // Updates immediately to avoid flickering
@@ -347,7 +564,51 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
347
564
  });
348
565
  }
349
566
 
567
+ /**
568
+ * Resets the sprite by destroying and recreating all animations
569
+ * This method clears the current animation state, destroys existing textures,
570
+ * and recreates all animations from the spritesheet
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * // Reset all animations to their initial state
575
+ * sprite.resetAnimations();
576
+ *
577
+ * // Reset and then play a specific animation
578
+ * await sprite.resetAnimations();
579
+ * sprite.play('walk');
580
+ * ```
581
+ */
582
+ async resetAnimations(): Promise<void> {
583
+ // Stop current animation
584
+ this.stop();
585
+
586
+ // Clear all animations and textures
587
+ this.animations.clear();
588
+
589
+ // Reset animation state
590
+ this.currentAnimation = null;
591
+ this.currentAnimationContainer = null;
592
+ this.time = 0;
593
+ this.frameIndex = 0;
594
+
595
+ // Clear children
596
+ this.removeChildren();
597
+
598
+ // Recreate animations from spritesheet
599
+ if (this.spritesheet) {
600
+ await this.createAnimations();
601
+ this.play(this.sheetCurrentAnimation, [this.sheetParams]);
602
+ }
603
+ }
604
+
350
605
  update({ deltaRatio }) {
606
+ // Block animation update if element is frozen
607
+ const element = this.getElement();
608
+ if (element && isElementFrozen(element)) {
609
+ return;
610
+ }
611
+
351
612
  if (
352
613
  !this.isPlaying() ||
353
614
  !this.currentAnimation ||
@@ -413,6 +674,7 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
413
674
  const widthOfSprite =
414
675
  typeof realSize == "number" ? realSize : realSize?.width;
415
676
 
677
+
416
678
  const applyAnchorBySize = () => {
417
679
  if (heightOfSprite && this.hitbox) {
418
680
  const { spriteWidth, spriteHeight } = data;
@@ -457,7 +719,9 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
457
719
  }
458
720
  }
459
721
 
460
- export interface CanvasSprite extends PixiSprite {}
722
+ export interface CanvasSprite extends PixiSprite {
723
+ layout: Layout | null;
724
+ }
461
725
 
462
726
  registerComponent("Sprite", CanvasSprite);
463
727
 
@@ -510,5 +774,7 @@ export type SpritePropTypes = SpritePropsWithImage | SpritePropsWithSheet;
510
774
 
511
775
  // Update the Sprite function to use the props interface
512
776
  export const Sprite: ComponentFunction<SpritePropTypes> = (props) => {
777
+ // Ensure component is registered in test environments where module cache may differ
778
+ registerComponent("Sprite", CanvasSprite);
513
779
  return createComponent("Sprite", props);
514
780
  };