@vpmedia/phaser 1.123.0 → 1.125.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.
Files changed (221) hide show
  1. package/dist/index.js +635 -623
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation.d.ts +3 -3
  4. package/dist/phaser/core/animation.d.ts.map +1 -1
  5. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  6. package/dist/phaser/core/animation_parser.d.ts +27 -2
  7. package/dist/phaser/core/animation_parser.d.ts.map +1 -1
  8. package/dist/phaser/core/array_set.d.ts +11 -11
  9. package/dist/phaser/core/array_set.d.ts.map +1 -1
  10. package/dist/phaser/core/cache.d.ts.map +1 -1
  11. package/dist/phaser/core/callback.d.ts.map +1 -1
  12. package/dist/phaser/core/dom.d.ts +125 -19
  13. package/dist/phaser/core/dom.d.ts.map +1 -1
  14. package/dist/phaser/core/factory.d.ts +3 -2
  15. package/dist/phaser/core/factory.d.ts.map +1 -1
  16. package/dist/phaser/core/frame_data.d.ts.map +1 -1
  17. package/dist/phaser/core/input.d.ts +3 -2
  18. package/dist/phaser/core/input.d.ts.map +1 -1
  19. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  20. package/dist/phaser/core/input_mouse.d.ts +5 -1
  21. package/dist/phaser/core/input_mouse.d.ts.map +1 -1
  22. package/dist/phaser/core/input_pointer.d.ts +3 -3
  23. package/dist/phaser/core/input_pointer.d.ts.map +1 -1
  24. package/dist/phaser/core/input_touch.d.ts +8 -8
  25. package/dist/phaser/core/input_touch.d.ts.map +1 -1
  26. package/dist/phaser/core/loader.d.ts +11 -2
  27. package/dist/phaser/core/loader.d.ts.map +1 -1
  28. package/dist/phaser/core/loader_parser.d.ts +14 -1
  29. package/dist/phaser/core/loader_parser.d.ts.map +1 -1
  30. package/dist/phaser/core/raf.d.ts +1 -1
  31. package/dist/phaser/core/raf.d.ts.map +1 -1
  32. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  33. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  34. package/dist/phaser/core/signal.d.ts.map +1 -1
  35. package/dist/phaser/core/sound.d.ts.map +1 -1
  36. package/dist/phaser/core/sound_manager.d.ts +1 -1
  37. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  38. package/dist/phaser/core/stage.d.ts +16 -6
  39. package/dist/phaser/core/stage.d.ts.map +1 -1
  40. package/dist/phaser/core/time.d.ts.map +1 -1
  41. package/dist/phaser/core/timer.d.ts +1 -1
  42. package/dist/phaser/core/timer.d.ts.map +1 -1
  43. package/dist/phaser/core/tween.d.ts +13 -5
  44. package/dist/phaser/core/tween.d.ts.map +1 -1
  45. package/dist/phaser/core/tween_data.d.ts +9 -7
  46. package/dist/phaser/core/tween_data.d.ts.map +1 -1
  47. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  48. package/dist/phaser/display/button.d.ts +6 -5
  49. package/dist/phaser/display/button.d.ts.map +1 -1
  50. package/dist/phaser/display/canvas/buffer.d.ts +1 -1
  51. package/dist/phaser/display/canvas/buffer.d.ts.map +1 -1
  52. package/dist/phaser/display/canvas/masker.d.ts +1 -1
  53. package/dist/phaser/display/canvas/masker.d.ts.map +1 -1
  54. package/dist/phaser/display/canvas/pool.d.ts +2 -2
  55. package/dist/phaser/display/canvas/pool.d.ts.map +1 -1
  56. package/dist/phaser/display/canvas/renderer.d.ts +9 -8
  57. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  58. package/dist/phaser/display/display_object.d.ts +5 -3
  59. package/dist/phaser/display/display_object.d.ts.map +1 -1
  60. package/dist/phaser/display/graphics.d.ts +1 -1
  61. package/dist/phaser/display/graphics.d.ts.map +1 -1
  62. package/dist/phaser/display/group.d.ts +1 -1
  63. package/dist/phaser/display/group.d.ts.map +1 -1
  64. package/dist/phaser/display/image.d.ts +6 -5
  65. package/dist/phaser/display/image.d.ts.map +1 -1
  66. package/dist/phaser/display/sprite_util.d.ts +3 -2
  67. package/dist/phaser/display/sprite_util.d.ts.map +1 -1
  68. package/dist/phaser/display/text.d.ts +2 -16
  69. package/dist/phaser/display/text.d.ts.map +1 -1
  70. package/dist/phaser/display/webgl/abstract_filter.d.ts +14 -8
  71. package/dist/phaser/display/webgl/abstract_filter.d.ts.map +1 -1
  72. package/dist/phaser/display/webgl/blend_manager.d.ts +2 -3
  73. package/dist/phaser/display/webgl/blend_manager.d.ts.map +1 -1
  74. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  75. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts +11 -5
  76. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts.map +1 -1
  77. package/dist/phaser/display/webgl/filter_manager.d.ts +6 -4
  78. package/dist/phaser/display/webgl/filter_manager.d.ts.map +1 -1
  79. package/dist/phaser/display/webgl/filter_texture.d.ts +4 -4
  80. package/dist/phaser/display/webgl/filter_texture.d.ts.map +1 -1
  81. package/dist/phaser/display/webgl/graphics.d.ts +2 -2
  82. package/dist/phaser/display/webgl/graphics.d.ts.map +1 -1
  83. package/dist/phaser/display/webgl/mask_manager.d.ts +3 -2
  84. package/dist/phaser/display/webgl/mask_manager.d.ts.map +1 -1
  85. package/dist/phaser/display/webgl/render_texture.d.ts +11 -8
  86. package/dist/phaser/display/webgl/render_texture.d.ts.map +1 -1
  87. package/dist/phaser/display/webgl/renderer.d.ts +8 -4
  88. package/dist/phaser/display/webgl/renderer.d.ts.map +1 -1
  89. package/dist/phaser/display/webgl/shader/normal.d.ts +28 -3
  90. package/dist/phaser/display/webgl/shader/normal.d.ts.map +1 -1
  91. package/dist/phaser/display/webgl/sprite_batch.d.ts +6 -5
  92. package/dist/phaser/display/webgl/sprite_batch.d.ts.map +1 -1
  93. package/dist/phaser/display/webgl/stencil_manager.d.ts +3 -3
  94. package/dist/phaser/display/webgl/stencil_manager.d.ts.map +1 -1
  95. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  96. package/dist/phaser/display/webgl/texture_util.d.ts +2 -2
  97. package/dist/phaser/display/webgl/texture_util.d.ts.map +1 -1
  98. package/dist/phaser/display/webgl/util.d.ts +1 -1
  99. package/dist/phaser/display/webgl/util.d.ts.map +1 -1
  100. package/dist/phaser/geom/circle.d.ts.map +1 -1
  101. package/dist/phaser/geom/line.d.ts +10 -1
  102. package/dist/phaser/geom/line.d.ts.map +1 -1
  103. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  104. package/dist/phaser/geom/util/circle.d.ts +2 -1
  105. package/dist/phaser/geom/util/circle.d.ts.map +1 -1
  106. package/dist/phaser/geom/util/point.d.ts +1 -1
  107. package/dist/phaser/geom/util/point.d.ts.map +1 -1
  108. package/dist/phaser/geom/util/rectangle.d.ts +1 -1
  109. package/dist/phaser/geom/util/rectangle.d.ts.map +1 -1
  110. package/dist/phaser/util/math.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/phaser/core/animation.ts +11 -11
  113. package/src/phaser/core/animation_manager.ts +12 -12
  114. package/src/phaser/core/animation_parser.ts +39 -37
  115. package/src/phaser/core/array_set.ts +28 -28
  116. package/src/phaser/core/cache.test.ts +33 -33
  117. package/src/phaser/core/cache.ts +7 -7
  118. package/src/phaser/core/callback.ts +1 -0
  119. package/src/phaser/core/callback_context.test.ts +8 -8
  120. package/src/phaser/core/device_util.ts +4 -4
  121. package/src/phaser/core/dom.ts +38 -24
  122. package/src/phaser/core/factory.ts +9 -2
  123. package/src/phaser/core/frame_data.test.ts +23 -23
  124. package/src/phaser/core/frame_data.ts +2 -5
  125. package/src/phaser/core/game.ts +12 -12
  126. package/src/phaser/core/input.ts +11 -10
  127. package/src/phaser/core/input_handler.test.ts +18 -18
  128. package/src/phaser/core/input_handler.ts +14 -14
  129. package/src/phaser/core/input_mouse.ts +13 -12
  130. package/src/phaser/core/input_pointer.test.ts +26 -26
  131. package/src/phaser/core/input_pointer.ts +4 -4
  132. package/src/phaser/core/input_touch.ts +8 -8
  133. package/src/phaser/core/loader.test.ts +26 -26
  134. package/src/phaser/core/loader.ts +63 -75
  135. package/src/phaser/core/loader_parser.test.ts +16 -16
  136. package/src/phaser/core/loader_parser.ts +16 -6
  137. package/src/phaser/core/raf.ts +1 -1
  138. package/src/phaser/core/registry.test.ts +6 -6
  139. package/src/phaser/core/scale_manager.ts +12 -11
  140. package/src/phaser/core/scene_manager.test.ts +30 -30
  141. package/src/phaser/core/scene_manager.ts +3 -3
  142. package/src/phaser/core/signal.test.ts +8 -8
  143. package/src/phaser/core/signal.ts +3 -3
  144. package/src/phaser/core/signal_dispatch.test.ts +30 -30
  145. package/src/phaser/core/sound.ts +45 -40
  146. package/src/phaser/core/sound_manager.ts +18 -22
  147. package/src/phaser/core/sound_sprite.ts +1 -1
  148. package/src/phaser/core/stage.ts +22 -12
  149. package/src/phaser/core/time.ts +1 -1
  150. package/src/phaser/core/timer.test.ts +47 -47
  151. package/src/phaser/core/timer.ts +2 -2
  152. package/src/phaser/core/tween.ts +34 -36
  153. package/src/phaser/core/tween_data.test.ts +119 -0
  154. package/src/phaser/core/tween_data.ts +54 -39
  155. package/src/phaser/core/tween_easing.test.ts +5 -5
  156. package/src/phaser/core/tween_manager.ts +2 -2
  157. package/src/phaser/display/bitmap_text.test.ts +30 -30
  158. package/src/phaser/display/bitmap_text.ts +3 -3
  159. package/src/phaser/display/button.ts +20 -18
  160. package/src/phaser/display/canvas/buffer.ts +2 -2
  161. package/src/phaser/display/canvas/graphics.ts +2 -2
  162. package/src/phaser/display/canvas/masker.ts +1 -1
  163. package/src/phaser/display/canvas/pool.test.ts +20 -20
  164. package/src/phaser/display/canvas/pool.ts +6 -6
  165. package/src/phaser/display/canvas/renderer.ts +18 -16
  166. package/src/phaser/display/canvas/tinter.ts +1 -1
  167. package/src/phaser/display/canvas/util.test.ts +25 -25
  168. package/src/phaser/display/canvas/util.ts +2 -2
  169. package/src/phaser/display/display_object.ts +16 -15
  170. package/src/phaser/display/graphics.test.ts +70 -20
  171. package/src/phaser/display/graphics.ts +46 -42
  172. package/src/phaser/display/group.ts +13 -12
  173. package/src/phaser/display/image.ts +35 -31
  174. package/src/phaser/display/sprite_util.ts +4 -3
  175. package/src/phaser/display/text.test.ts +28 -25
  176. package/src/phaser/display/text.ts +56 -62
  177. package/src/phaser/display/webgl/abstract_filter.ts +21 -11
  178. package/src/phaser/display/webgl/blend_manager.ts +4 -5
  179. package/src/phaser/display/webgl/earcut.test.ts +20 -20
  180. package/src/phaser/display/webgl/earcut.ts +46 -42
  181. package/src/phaser/display/webgl/fast_sprite_batch.ts +21 -16
  182. package/src/phaser/display/webgl/filter_manager.ts +7 -4
  183. package/src/phaser/display/webgl/filter_texture.ts +5 -5
  184. package/src/phaser/display/webgl/graphics.ts +3 -3
  185. package/src/phaser/display/webgl/mask_manager.ts +9 -14
  186. package/src/phaser/display/webgl/render_texture.ts +15 -14
  187. package/src/phaser/display/webgl/renderer.ts +10 -9
  188. package/src/phaser/display/webgl/shader/normal.ts +45 -35
  189. package/src/phaser/display/webgl/shader/shader.test.ts +24 -24
  190. package/src/phaser/display/webgl/shader_manager.test.ts +18 -18
  191. package/src/phaser/display/webgl/shader_manager.ts +1 -1
  192. package/src/phaser/display/webgl/sprite_batch.ts +10 -10
  193. package/src/phaser/display/webgl/stencil_manager.ts +4 -4
  194. package/src/phaser/display/webgl/texture.ts +2 -1
  195. package/src/phaser/display/webgl/texture_util.ts +2 -2
  196. package/src/phaser/display/webgl/util.ts +4 -4
  197. package/src/phaser/geom/circle.test.ts +38 -38
  198. package/src/phaser/geom/circle.ts +7 -7
  199. package/src/phaser/geom/ellipse.test.ts +12 -12
  200. package/src/phaser/geom/line.test.ts +15 -15
  201. package/src/phaser/geom/line.ts +5 -2
  202. package/src/phaser/geom/matrix.test.ts +31 -31
  203. package/src/phaser/geom/point.test.ts +31 -31
  204. package/src/phaser/geom/polygon.test.ts +27 -27
  205. package/src/phaser/geom/polygon.ts +2 -2
  206. package/src/phaser/geom/rectangle.test.ts +87 -87
  207. package/src/phaser/geom/rectangle.ts +12 -13
  208. package/src/phaser/geom/rounded_rectangle.test.ts +9 -9
  209. package/src/phaser/geom/util/circle.test.ts +5 -5
  210. package/src/phaser/geom/util/circle.ts +8 -11
  211. package/src/phaser/geom/util/ellipse.test.ts +2 -2
  212. package/src/phaser/geom/util/line.test.ts +2 -2
  213. package/src/phaser/geom/util/matrix.test.ts +4 -4
  214. package/src/phaser/geom/util/point.test.ts +2 -2
  215. package/src/phaser/geom/util/point.ts +6 -8
  216. package/src/phaser/geom/util/polygon.test.ts +2 -2
  217. package/src/phaser/geom/util/rectangle.test.ts +2 -2
  218. package/src/phaser/geom/util/rectangle.ts +1 -1
  219. package/src/phaser/geom/util/rounded_rectangle.test.ts +2 -2
  220. package/src/phaser/util/math.test.ts +41 -41
  221. package/src/phaser/util/math.ts +7 -5
@@ -6,6 +6,7 @@ import { TweenData } from './tween_data.js';
6
6
  import type { Game } from './game.js';
7
7
  import type { TweenManager } from './tween_manager.js';
8
8
  import type { AppliedCallback, Callback, EasingFunction, InterpolationFunction } from './callback.js';
9
+ import type { TweenValues } from './tween_data.js';
9
10
 
10
11
  export class Tween {
11
12
  public game!: Game;
@@ -23,7 +24,7 @@ export class Tween {
23
24
  public onComplete!: Signal;
24
25
  public isRunning!: boolean;
25
26
  public current!: number;
26
- public properties!: any;
27
+ public properties!: TweenValues;
27
28
  public chainedTween!: Tween | null;
28
29
  public isPaused!: boolean;
29
30
  public _onUpdateCallback!: Callback | null;
@@ -92,7 +93,7 @@ export class Tween {
92
93
  * @returns {Tween} This Tween object for chaining.
93
94
  */
94
95
  public to(
95
- properties: any,
96
+ properties: TweenValues,
96
97
  duration = 1000,
97
98
  ease: string | EasingFunction = 'Linear',
98
99
  autoStart = false,
@@ -100,13 +101,11 @@ export class Tween {
100
101
  repeat = 0,
101
102
  yoyo = false
102
103
  ): this {
103
- if (typeof ease === 'string' && this.manager.easeMap[ease]) {
104
- ease = this.manager.easeMap[ease]!;
105
- }
104
+ const easing = this.resolveEasing(ease);
106
105
  if (this.isRunning) {
107
106
  return this;
108
107
  }
109
- this.timeline.push(new TweenData(this).to(properties, duration, ease, delay, repeat, yoyo));
108
+ this.timeline.push(new TweenData(this).to(properties, duration, easing, delay, repeat, yoyo));
110
109
  if (autoStart) {
111
110
  this.start();
112
111
  }
@@ -125,7 +124,7 @@ export class Tween {
125
124
  * @returns {Tween} This Tween object for chaining.
126
125
  */
127
126
  public from(
128
- properties: any,
127
+ properties: TweenValues,
129
128
  duration = 1000,
130
129
  ease: string | EasingFunction = 'Linear',
131
130
  autoStart = false,
@@ -133,14 +132,11 @@ export class Tween {
133
132
  repeat = 0,
134
133
  yoyo = false
135
134
  ): this {
136
- if (typeof ease === 'string' && this.manager.easeMap[ease]) {
137
- ease = this.manager.easeMap[ease]!;
138
- }
139
135
  if (this.isRunning) {
140
136
  this.game.logger.warn('Tween.from cannot be called after Tween.start');
141
137
  return this;
142
138
  }
143
- this.timeline.push(new TweenData(this).from(properties, duration, ease, delay, repeat, yoyo));
139
+ this.timeline.push(new TweenData(this).from(properties, duration, this.resolveEasing(ease), delay, repeat, yoyo));
144
140
  if (autoStart) {
145
141
  this.start();
146
142
  }
@@ -161,12 +157,9 @@ export class Tween {
161
157
  // Build our master property list with the starting values
162
158
  const keys = Object.keys(tweenData.vEnd);
163
159
  for (const property of keys) {
164
- const targetProperties = this.target as unknown as Record<string, number>;
165
- this.properties[property] = targetProperties[property] ?? 0;
166
- if (!Array.isArray(this.properties[property])) {
167
- // Ensures we're using numbers, not strings
168
- this.properties[property] *= 1;
169
- }
160
+ const targetProperties = this.target as unknown as Record<string, number | string | undefined>;
161
+ // Ensures we're using numbers, not strings
162
+ this.properties[property] = Number(targetProperties[property] ?? 0);
170
163
  }
171
164
  }
172
165
  for (const tweenData of this.timeline) {
@@ -174,10 +167,7 @@ export class Tween {
174
167
  }
175
168
  this.manager.add(this);
176
169
  this.isRunning = true;
177
- if (index < 0 || index > this.timeline.length - 1) {
178
- index = 0;
179
- }
180
- this.current = index;
170
+ this.current = index < 0 || index > this.timeline.length - 1 ? 0 : index;
181
171
  this.timeline[this.current]!.start();
182
172
  return this;
183
173
  }
@@ -209,7 +199,7 @@ export class Tween {
209
199
  * @param {number} index - The index in the timeline to update.
210
200
  * @returns {Tween} This Tween object for chaining.
211
201
  */
212
- public updateTweenData(property: string, value: any, index = 0): this {
202
+ public updateTweenData(property: string, value: unknown, index = 0): this {
213
203
  if (this.timeline.length === 0) {
214
204
  return this;
215
205
  }
@@ -283,11 +273,21 @@ export class Tween {
283
273
  * @param {number} index - The index in the timeline to apply easing to.
284
274
  * @returns {Tween} This Tween object for chaining.
285
275
  */
286
- public easing(ease: string | EasingFunction, index: number): this {
287
- if (typeof ease === 'string' && this.manager.easeMap[ease]) {
288
- ease = this.manager.easeMap[ease]!;
276
+ public resolveEasing(ease: string | EasingFunction): EasingFunction {
277
+ if (typeof ease !== 'string') {
278
+ return ease;
289
279
  }
290
- return this.updateTweenData('easingFunction', ease, index);
280
+ return this.manager.easeMap[ease] ?? this.manager.easeMap['Linear']!;
281
+ }
282
+
283
+ /**
284
+ * Sets the easing function for a tween in the timeline.
285
+ * @param {string | Function} ease - The easing function or its name.
286
+ * @param {number} index - The index of the tween in the timeline.
287
+ * @returns {Tween} This Tween object for chaining.
288
+ */
289
+ public easing(ease: string | EasingFunction, index: number): this {
290
+ return this.updateTweenData('easingFunction', this.resolveEasing(ease), index);
291
291
  }
292
292
 
293
293
  /**
@@ -401,7 +401,7 @@ export class Tween {
401
401
  * @returns {boolean} True if the tween should continue running, false if it's complete.
402
402
  */
403
403
  public update(time: number): boolean {
404
- if (this.pendingDelete || !this.target) {
404
+ if (this.pendingDelete || !(this.target as TweenTarget | undefined)) {
405
405
  return false;
406
406
  }
407
407
  if (this.isPaused) {
@@ -484,7 +484,7 @@ export class Tween {
484
484
  * @param {object[]} data - The array to store the generated data in.
485
485
  * @returns {object[]} The populated data array.
486
486
  */
487
- public generateData(frameRate = 60, data: any[] = []) {
487
+ public generateData(frameRate = 60, data: Record<string, number>[] = []): Record<string, number>[] | null {
488
488
  if (this.game === null || this.target === null) {
489
489
  return null;
490
490
  }
@@ -493,21 +493,19 @@ export class Tween {
493
493
  // Build our master property list with the starting values
494
494
  const keys = Object.keys(tweenData.vEnd);
495
495
  for (const property of keys) {
496
- const targetProperties = this.target as unknown as Record<string, number>;
497
- this.properties[property] = targetProperties[property] ?? 0;
498
- if (!Array.isArray(this.properties[property])) {
499
- // Ensures we're using numbers, not strings
500
- this.properties[property] *= 1;
501
- }
496
+ const targetProperties = this.target as unknown as Record<string, number | string | undefined>;
497
+ // Ensures we're using numbers, not strings
498
+ this.properties[property] = Number(targetProperties[property] ?? 0);
502
499
  }
503
500
  }
504
501
  for (const tweenData of this.timeline) {
505
502
  tweenData.loadValues();
506
503
  }
504
+ const frames = [...data];
507
505
  for (const tweenData of this.timeline) {
508
- data = [...data, ...tweenData.generateData(frameRate)];
506
+ frames.push(...tweenData.generateData(frameRate));
509
507
  }
510
- return data;
508
+ return frames;
511
509
  }
512
510
 
513
511
  /**
@@ -0,0 +1,119 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { TWEEN_COMPLETE, TWEEN_RUNNING } from './const.js';
3
+ import { Signal } from './signal.js';
4
+ import { Tween } from './tween.js';
5
+ import { TweenManager } from './tween_manager.js';
6
+ import type { Game } from './game.js';
7
+
8
+ const createGame = (): Game =>
9
+ ({
10
+ time: { time: 0, elapsedMS: 0 },
11
+ logger: { warn: (): void => undefined },
12
+ onPause: new Signal(),
13
+ onResume: new Signal(),
14
+ }) as unknown as Game;
15
+
16
+ const createTween = (target: Record<string, number>): { game: Game; tween: Tween } => {
17
+ const game = createGame();
18
+ const manager = new TweenManager(game);
19
+ return { game, tween: new Tween(target, game, manager) };
20
+ };
21
+
22
+ const advance = (game: Game, tween: Tween, ms: number): void => {
23
+ game.time.time += ms;
24
+ game.time.elapsedMS = ms;
25
+ tween.update(game.time.time);
26
+ };
27
+
28
+ describe('TweenData', (): void => {
29
+ describe('resolveEasing', (): void => {
30
+ it('takes a function as-is', (): void => {
31
+ const { tween } = createTween({ x: 0 });
32
+ const ease = (k: number): number => k * k;
33
+ expect(tween.resolveEasing(ease)).toBe(ease);
34
+ });
35
+
36
+ it('looks a named curve up in the manager', (): void => {
37
+ const { tween } = createTween({ x: 0 });
38
+ expect(tween.resolveEasing('Quad.easeIn')).toBe(tween.manager.easeMap['Quad.easeIn']);
39
+ });
40
+
41
+ it('falls back to linear for a name the manager does not know', (): void => {
42
+ const { tween } = createTween({ x: 0 });
43
+ expect(tween.resolveEasing('No.Such.Curve')).toBe(tween.manager.easeMap['Linear']);
44
+ });
45
+ });
46
+
47
+ describe('to', (): void => {
48
+ it('drives a numeric property from its start value to the end value', (): void => {
49
+ const target = { x: 0 };
50
+ const { game, tween } = createTween(target);
51
+ tween.to({ x: 100 }, 100, 'Linear', true);
52
+ advance(game, tween, 50);
53
+ expect(target.x).toBeCloseTo(50);
54
+ advance(game, tween, 50);
55
+ expect(target.x).toBeCloseTo(100);
56
+ });
57
+
58
+ it('reads a relative end value against the start', (): void => {
59
+ const target = { x: 20 };
60
+ const { game, tween } = createTween(target);
61
+ tween.to({ x: '+10' }, 100, 'Linear', true);
62
+ advance(game, tween, 100);
63
+ expect(target.x).toBeCloseTo(30);
64
+ });
65
+
66
+ it('interpolates through a list of waypoints', (): void => {
67
+ const target = { x: 0 };
68
+ const { game, tween } = createTween(target);
69
+ tween.to({ x: [0, 50, 100] }, 100, 'Linear', true);
70
+ advance(game, tween, 100);
71
+ expect(target.x).toBeCloseTo(100);
72
+ });
73
+
74
+ // An empty waypoint list has nothing to interpolate between, so the property goes to NaN.
75
+ it('survives an empty waypoint list', (): void => {
76
+ const target = { x: 7 };
77
+ const { game, tween } = createTween(target);
78
+ tween.to({ x: [] }, 100, 'Linear', true);
79
+ expect((): void => {
80
+ advance(game, tween, 100);
81
+ }).not.toThrow();
82
+ });
83
+
84
+ it('reports the tween running until the duration has passed', (): void => {
85
+ const target = { x: 0 };
86
+ const { game, tween } = createTween(target);
87
+ tween.to({ x: 10 }, 100, 'Linear', true);
88
+ game.time.time += 50;
89
+ game.time.elapsedMS = 50;
90
+ expect(tween.timeline[0]?.update(game.time.time)).toBe(TWEEN_RUNNING);
91
+ game.time.time += 100;
92
+ game.time.elapsedMS = 100;
93
+ expect(tween.timeline[0]?.update(game.time.time)).toBe(TWEEN_COMPLETE);
94
+ });
95
+ });
96
+
97
+ describe('from', (): void => {
98
+ it('starts at the given value and lands on the target original', (): void => {
99
+ const target = { x: 100 };
100
+ const { game, tween } = createTween(target);
101
+ tween.from({ x: 0 }, 100, 'Linear', true);
102
+ advance(game, tween, 0);
103
+ expect(target.x).toBeCloseTo(0);
104
+ advance(game, tween, 100);
105
+ expect(target.x).toBeCloseTo(100);
106
+ });
107
+ });
108
+
109
+ describe('generateData', (): void => {
110
+ it('produces one frame of values per step', (): void => {
111
+ const { tween } = createTween({ x: 0 });
112
+ tween.to({ x: 100 }, 100, 'Linear');
113
+ tween.start();
114
+ const data = tween.generateData(10) ?? [];
115
+ expect(data.length).toBeGreaterThan(0);
116
+ expect(data.at(-1)?.['x']).toBeCloseTo(100);
117
+ });
118
+ });
119
+ });
@@ -2,15 +2,18 @@ import type { Game } from './game.js';
2
2
  import * as MathUtils from '../util/math.js';
3
3
  import { TWEEN_COMPLETE, TWEEN_LOOPED, TWEEN_PENDING, TWEEN_RUNNING } from './const.js';
4
4
  import type { Tween } from './tween.js';
5
- import type { InterpolationFunction } from './callback.js';
5
+ import type { EasingFunction, InterpolationFunction } from './callback.js';
6
+
7
+ /** The properties a tween drives, each holding either a single target or a list of waypoints. */
8
+ export type TweenValues = Record<string, number | number[] | string>;
6
9
 
7
10
  export class TweenData {
8
11
  public parent!: Tween;
9
12
  public game!: Game;
10
- public vStart!: Record<string, any>;
11
- public vStartCache!: Record<string, any>;
12
- public vEnd!: Record<string, any>;
13
- public vEndCache!: Record<string, any>;
13
+ public vStart!: TweenValues;
14
+ public vStartCache!: TweenValues;
15
+ public vEnd!: TweenValues;
16
+ public vEndCache!: TweenValues;
14
17
  public duration!: number;
15
18
  public percent!: number;
16
19
  public value!: number;
@@ -71,7 +74,14 @@ export class TweenData {
71
74
  * @param {boolean} yoyo - Whether to reverse the tween on each repeat.
72
75
  * @returns {TweenData} This TweenData object for chaining.
73
76
  */
74
- public to(properties: any, duration: number, ease: any, delay: number, repeat: number, yoyo: boolean): this {
77
+ public to(
78
+ properties: TweenValues,
79
+ duration: number,
80
+ ease: EasingFunction,
81
+ delay: number,
82
+ repeat: number,
83
+ yoyo: boolean
84
+ ): this {
75
85
  this.vEnd = properties;
76
86
  this.duration = duration;
77
87
  this.easingFunction = ease;
@@ -92,7 +102,14 @@ export class TweenData {
92
102
  * @param {boolean} yoyo - Whether to reverse the tween on each repeat.
93
103
  * @returns {TweenData} This TweenData object for chaining.
94
104
  */
95
- public from(properties: any, duration: number, ease: any, delay: number, repeat: number, yoyo: boolean): this {
105
+ public from(
106
+ properties: TweenValues,
107
+ duration: number,
108
+ ease: EasingFunction,
109
+ delay: number,
110
+ repeat: number,
111
+ yoyo: boolean
112
+ ): this {
96
113
  this.vEnd = properties;
97
114
  this.duration = duration;
98
115
  this.easingFunction = ease;
@@ -115,9 +132,9 @@ export class TweenData {
115
132
  // Reverse them all and instant set them
116
133
  const keys = Object.keys(this.vStartCache);
117
134
  for (const property of keys) {
118
- this.vStart[property] = this.vEndCache[property];
119
- this.vEnd[property] = this.vStartCache[property];
120
- (this.parent.target as unknown as Record<string, any>)[property] = this.vStart[property];
135
+ this.vStart[property] = this.vEndCache[property]!;
136
+ this.vEnd[property] = this.vStartCache[property]!;
137
+ (this.parent.target as unknown as Record<string, unknown>)[property] = this.vStart[property];
121
138
  }
122
139
  }
123
140
  this.value = 0;
@@ -134,29 +151,27 @@ export class TweenData {
134
151
  const keys = Object.keys(this.parent.properties);
135
152
  for (const property of keys) {
136
153
  // Load the property from the parent object
137
- this.vStart[property] = this.parent.properties[property];
154
+ const start = this.parent.properties[property]!;
155
+ this.vStart[property] = start;
138
156
  // Check if an Array was provided as property value
139
- if (Array.isArray(this.vEnd[property])) {
140
- if (this.vEnd[property].length === 0) {
141
- // an empty end array leaves the property untouched for this step
142
- } else if (this.percent === 0) {
143
- // Put the start value at the beginning of the array
144
- // but we only want to do this once, if the Tween hasn't run before
145
- this.vEnd[property] = [this.vStart[property], ...this.vEnd[property]];
146
- }
157
+ const end = this.vEnd[property];
158
+ if (Array.isArray(end) && end.length > 0 && this.percent === 0) {
159
+ // Put the start value at the beginning of the array
160
+ // but we only want to do this once, if the Tween hasn't run before
161
+ this.vEnd[property] = [Number(start), ...end];
147
162
  }
148
- if (this.vEnd[property] !== undefined) {
149
- if (typeof this.vEnd[property] === 'string') {
163
+ if (end === undefined) {
164
+ // Null tween
165
+ this.vEnd[property] = start;
166
+ } else {
167
+ if (typeof end === 'string') {
150
168
  // Parses relative end values with start as base (e.g.: +10, -3)
151
- this.vEnd[property] = this.vStart[property] + Number(this.vEnd[property]);
169
+ this.vEnd[property] = Number(start) + Number(end);
152
170
  }
153
- this.parent.properties[property] = this.vEnd[property];
154
- } else {
155
- // Null tween
156
- this.vEnd[property] = this.vStart[property];
171
+ this.parent.properties[property] = this.vEnd[property]!;
157
172
  }
158
- this.vStartCache[property] = this.vStart[property];
159
- this.vEndCache[property] = this.vEnd[property];
173
+ this.vStartCache[property] = this.vStart[property]!;
174
+ this.vEndCache[property] = this.vEnd[property]!;
160
175
  }
161
176
  return this;
162
177
  }
@@ -189,11 +204,11 @@ export class TweenData {
189
204
  this.value = this.easingFunction(this.percent);
190
205
  const keys = Object.keys(this.vEnd);
191
206
  for (const property of keys) {
192
- const start = this.vStart[property];
193
207
  const end = this.vEnd[property];
194
- (this.parent.target as unknown as Record<string, any>)[property] = Array.isArray(end)
208
+ const start = Number(this.vStart[property]);
209
+ (this.parent.target as unknown as Record<string, unknown>)[property] = Array.isArray(end)
195
210
  ? this.interpolationFunction.call(this.interpolationContext, end, this.value)
196
- : start + (end - start) * this.value;
211
+ : start + (Number(end) - start) * this.value;
197
212
  }
198
213
  if ((!this.parent.reverse && this.percent === 1) || (this.parent.reverse && this.percent === 0)) {
199
214
  return this.repeat();
@@ -224,11 +239,11 @@ export class TweenData {
224
239
  const blob: Record<string, number> = {};
225
240
  const keys = Object.keys(this.vEnd);
226
241
  for (const property of keys) {
227
- const start = this.vStart[property];
228
242
  const end = this.vEnd[property];
243
+ const start = Number(this.vStart[property]);
229
244
  blob[property] = Array.isArray(end)
230
245
  ? this.interpolationFunction(end, this.value)
231
- : start + (end - start) * this.value;
246
+ : start + (Number(end) - start) * this.value;
232
247
  }
233
248
  data.push(blob);
234
249
  if ((!this.parent.reverse && this.percent === 1) || (this.parent.reverse && this.percent === 0)) {
@@ -253,8 +268,8 @@ export class TweenData {
253
268
  // Restore the properties
254
269
  const keys = Object.keys(this.vStartCache);
255
270
  for (const property of keys) {
256
- this.vStart[property] = this.vStartCache[property];
257
- this.vEnd[property] = this.vEndCache[property];
271
+ this.vStart[property] = this.vStartCache[property]!;
272
+ this.vEnd[property] = this.vEndCache[property]!;
258
273
  }
259
274
  this.inReverse = false;
260
275
  return TWEEN_COMPLETE;
@@ -267,15 +282,15 @@ export class TweenData {
267
282
  // If inReverse we're going from vEnd to vStartCache
268
283
  const keys = Object.keys(this.vStartCache);
269
284
  for (const property of keys) {
270
- this.vStart[property] = this.vEndCache[property];
271
- this.vEnd[property] = this.vStartCache[property];
285
+ this.vStart[property] = this.vEndCache[property]!;
286
+ this.vEnd[property] = this.vStartCache[property]!;
272
287
  }
273
288
  } else {
274
289
  // If not inReverse we're just repopulating the cache again
275
290
  const keys = Object.keys(this.vStartCache);
276
291
  for (const property of keys) {
277
- this.vStart[property] = this.vStartCache[property];
278
- this.vEnd[property] = this.vEndCache[property];
292
+ this.vStart[property] = this.vStartCache[property]!;
293
+ this.vEnd[property] = this.vEndCache[property]!;
279
294
  }
280
295
  // -1 means repeat forever, otherwise decrement the repeatCounter
281
296
  // We only decrement this counter if the tween isn't doing a yoyo, as that doesn't count towards the repeat total
@@ -82,22 +82,22 @@ const EXPECTED: Record<string, number[]> = {
82
82
  ],
83
83
  };
84
84
 
85
- describe('tween_easing', () => {
86
- it.each(Object.keys(EXPECTED))('%s matches its reference curve', (name) => {
85
+ describe('tween_easing', (): void => {
86
+ it.each(Object.keys(EXPECTED))('%s matches its reference curve', (name): void => {
87
87
  const fn = (Easing as unknown as Record<string, (k: number) => number>)[name]!;
88
- const actual = SAMPLE_POINTS.map((k) => fn(k));
88
+ const actual = SAMPLE_POINTS.map((k): number => fn(k));
89
89
  for (const [index, value] of actual.entries()) {
90
90
  expect(value).toBeCloseTo(EXPECTED[name]![index]!, 12);
91
91
  }
92
92
  });
93
93
 
94
- it.each(Object.keys(EXPECTED))('%s is anchored at both ends', (name) => {
94
+ it.each(Object.keys(EXPECTED))('%s is anchored at both ends', (name): void => {
95
95
  const fn = (Easing as unknown as Record<string, (k: number) => number>)[name]!;
96
96
  expect(fn(0)).toBeCloseTo(0, 12);
97
97
  expect(fn(1)).toBeCloseTo(1, 12);
98
98
  });
99
99
 
100
- it.each(Object.keys(EXPECTED))('%s does not mutate beyond its return value', (name) => {
100
+ it.each(Object.keys(EXPECTED))('%s does not mutate beyond its return value', (name): void => {
101
101
  const fn = (Easing as unknown as Record<string, (k: number) => number>)[name]!;
102
102
  for (const k of SAMPLE_POINTS) {
103
103
  expect(fn(k)).toBe(fn(k));
@@ -106,7 +106,7 @@ export class TweenManager {
106
106
  * Get all tweens managed by this manager.
107
107
  * @returns {Tween[]} An array of all active tweens.
108
108
  */
109
- public getAll() {
109
+ public getAll(): Tween[] {
110
110
  return this._tweens;
111
111
  }
112
112
 
@@ -128,7 +128,7 @@ export class TweenManager {
128
128
  */
129
129
  public removeFrom(obj: TweenTarget, children: object[] | null = null): void {
130
130
  if (Array.isArray(obj)) {
131
- for (const entry of obj) {
131
+ for (const entry of obj as object[]) {
132
132
  this.removeFrom(entry);
133
133
  }
134
134
  return;