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,15 +1,16 @@
1
1
  import { Text as PixiText, TextStyle } from "pixi.js";
2
- import { createComponent, registerComponent } from "../engine/reactive";
3
- import { DisplayObject } from "./DisplayObject";
2
+ import { createComponent, registerComponent, Element, Props } from "../engine/reactive";
3
+ import { DisplayObject, ComponentInstance } from "./DisplayObject";
4
4
  import { DisplayObjectProps } from "./types/DisplayObject";
5
5
  import { Signal } from "@signe/reactive";
6
- import { on } from "../engine/trigger";
6
+ import { on, isTrigger } from "../engine/trigger";
7
+ import { Howl } from "howler";
7
8
 
8
9
  enum TextEffect {
9
10
  Typewriter = "typewriter",
10
11
  }
11
12
 
12
- interface TextProps extends DisplayObjectProps {
13
+ export interface TextProps extends DisplayObjectProps {
13
14
  text?: string;
14
15
  style?: Partial<TextStyle>;
15
16
  color?: string;
@@ -20,7 +21,13 @@ interface TextProps extends DisplayObjectProps {
20
21
  start?: () => void;
21
22
  onComplete?: () => void;
22
23
  skip?: () => void;
24
+ sound?: {
25
+ src: string;
26
+ volume?: number;
27
+ rate?: number;
28
+ };
23
29
  };
30
+ context?: any; // Ensure context is available, ideally typed from a base prop or injected
24
31
  }
25
32
 
26
33
  class CanvasText extends DisplayObject(PixiText) {
@@ -31,10 +38,19 @@ class CanvasText extends DisplayObject(PixiText) {
31
38
  private _wordWrapWidth: number = 0;
32
39
  private typewriterOptions: any = {};
33
40
  private skipSignal?: () => void;
41
+ private typewriterSound?: Howl;
42
+ private lastSoundTime: number = 0;
43
+ private soundDuration: number = 0; // Duration of the sound in milliseconds
34
44
 
35
- onMount(args) {
36
- super.onMount(args);
37
- const { props } = args;
45
+ /**
46
+ * Called when the component is mounted to the scene graph.
47
+ * Initializes the typewriter effect if configured.
48
+ * @param {Element<CanvasText>} element - The element being mounted with parent and props.
49
+ * @param {number} [index] - The index of the component among its siblings.
50
+ */
51
+ async onMount(element: Element<any>, index?: number): Promise<void> {
52
+ const { props } = element;
53
+ await super.onMount(element, index);
38
54
  const tick: Signal = props.context.tick;
39
55
 
40
56
  if (props.text && props.typewriter) {
@@ -44,12 +60,18 @@ class CanvasText extends DisplayObject(PixiText) {
44
60
  // Set typewriter options
45
61
  if (props.typewriter) {
46
62
  this.typewriterOptions = props.typewriter;
47
- if (this.typewriterOptions.skip) {
63
+ if (this.typewriterOptions.skip && isTrigger(this.typewriterOptions.skip)) {
48
64
  on(this.typewriterOptions.skip, () => {
49
65
  this.skipTypewriter();
50
66
  });
51
67
  }
68
+ // Initialize typewriter sound if configured
69
+ if (this.typewriterOptions.sound) {
70
+ this.initializeTypewriterSound();
71
+ }
52
72
  }
73
+ // Update layout after initializing typewriter
74
+ this.updateLayout();
53
75
  }
54
76
  this.subscriptionTick = tick.observable.subscribe(() => {
55
77
  if (props.typewriter) {
@@ -63,15 +85,21 @@ class CanvasText extends DisplayObject(PixiText) {
63
85
  if (props.typewriter) {
64
86
  if (props.typewriter) {
65
87
  this.typewriterOptions = props.typewriter;
88
+ // Reinitialize sound if sound configuration changed
89
+ if (props.typewriter.sound) {
90
+ this.initializeTypewriterSound();
91
+ }
66
92
  }
67
93
  }
68
- if (props.text) {
69
- this.text = props.text;
94
+ if (props.text !== undefined) {
95
+ this.text = ''+props.text;
70
96
  }
71
97
  if (props.text !== undefined && props.text !== this.fullText && this.fullProps.typewriter) {
72
98
  this.text = "";
73
99
  this.currentIndex = 0;
74
100
  this.fullText = props.text;
101
+ // Update layout after resetting typewriter
102
+ this.updateLayout();
75
103
  }
76
104
  if (props.style) {
77
105
  for (const key in props.style) {
@@ -90,6 +118,59 @@ class CanvasText extends DisplayObject(PixiText) {
90
118
  if (props.fontFamily) {
91
119
  this.style.fontFamily = props.fontFamily;
92
120
  }
121
+
122
+ // Use the centralized layout update method
123
+ this.updateLayout();
124
+ }
125
+
126
+ get onCompleteCallback() {
127
+ return this.typewriterOptions.onComplete;
128
+ }
129
+
130
+ /**
131
+ * Initializes the typewriter sound effect using Howler.
132
+ * Creates a Howl instance with the configured sound settings.
133
+ * Calculates the sound duration to prevent overlapping sounds.
134
+ */
135
+ private initializeTypewriterSound() {
136
+ if (!this.typewriterOptions.sound?.src) return;
137
+
138
+ this.typewriterSound = new Howl({
139
+ src: [this.typewriterOptions.sound.src],
140
+ volume: this.typewriterOptions.sound.volume ?? 0.5,
141
+ rate: this.typewriterOptions.sound.rate ?? 1.0,
142
+ preload: true,
143
+ onload: () => {
144
+ // Calculate sound duration in milliseconds
145
+ if (this.typewriterSound) {
146
+ const duration = this.typewriterSound.duration();
147
+ const rate = this.typewriterOptions.sound?.rate ?? 1.0;
148
+ this.soundDuration = (duration / rate) * 1000;
149
+ }
150
+ }
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Plays the typewriter sound with duration-based cooldown to prevent overlapping sounds.
156
+ * @param {number} currentTime - The current timestamp to check against sound duration.
157
+ */
158
+ private playTypewriterSound(currentTime: number) {
159
+ if (!this.typewriterSound || !this.typewriterOptions.sound) return;
160
+
161
+ // Check if enough time has passed since the last sound play
162
+ // Use the actual sound duration to prevent overlap
163
+ if (this.soundDuration > 0 && currentTime - this.lastSoundTime < this.soundDuration) return;
164
+
165
+ this.typewriterSound.play();
166
+ this.lastSoundTime = currentTime;
167
+ }
168
+
169
+ /**
170
+ * Updates the layout properties of the text component.
171
+ * This method ensures consistent width, height and word wrap behavior.
172
+ */
173
+ private updateLayout() {
93
174
  if (this._wordWrapWidth) {
94
175
  this.setWidth(this._wordWrapWidth);
95
176
  } else {
@@ -98,10 +179,6 @@ class CanvasText extends DisplayObject(PixiText) {
98
179
  this.setHeight(this.height);
99
180
  }
100
181
 
101
- get onCompleteCallback() {
102
- return this.typewriterOptions.onComplete;
103
- }
104
-
105
182
  private typewriterEffect() {
106
183
  if (this.currentIndex < this.fullText.length) {
107
184
  const nextIndex = Math.min(
@@ -111,6 +188,14 @@ class CanvasText extends DisplayObject(PixiText) {
111
188
  this.text = this.fullText.slice(0, nextIndex);
112
189
  this.currentIndex = nextIndex;
113
190
 
191
+ // Play typewriter sound if configured
192
+ if (this.typewriterOptions.sound) {
193
+ this.playTypewriterSound(Date.now());
194
+ }
195
+
196
+ // Update layout after text change to maintain proper word wrap and dimensions
197
+ this.updateLayout();
198
+
114
199
  // Check if typewriter effect is complete
115
200
  if (
116
201
  this.currentIndex === this.fullText.length &&
@@ -128,15 +213,37 @@ class CanvasText extends DisplayObject(PixiText) {
128
213
  }
129
214
  this.text = this.fullText;
130
215
  this.currentIndex = this.fullText.length;
216
+
217
+ // Update layout after setting full text to maintain proper word wrap and dimensions
218
+ this.updateLayout();
131
219
  }
132
220
 
133
- onDestroy(): void {
134
- super.onDestroy();
135
- this.subscriptionTick.unsubscribe();
221
+ /**
222
+ * Called when the component is about to be destroyed.
223
+ * Unsubscribes from the tick observable and cleans up sound resources.
224
+ * @param {Element<any>} parent - The parent element.
225
+ * @param {() => void} [afterDestroy] - An optional callback function to be executed after the component's own destruction logic.
226
+ */
227
+ async onDestroy(parent: Element<any>, afterDestroy?: () => void): Promise<void> {
228
+ const _afterDestroy = async () => {
229
+ if (this.subscriptionTick) {
230
+ this.subscriptionTick.unsubscribe();
231
+ }
232
+ // Clean up typewriter sound
233
+ if (this.typewriterSound) {
234
+ this.typewriterSound.stop();
235
+ this.typewriterSound.unload();
236
+ this.typewriterSound = undefined;
237
+ }
238
+ if (afterDestroy) {
239
+ afterDestroy();
240
+ }
241
+ }
242
+ await super.onDestroy(parent, _afterDestroy);
136
243
  }
137
244
  }
138
245
 
139
- interface CanvasText extends PixiText {}
246
+ // interface CanvasText extends PixiText {} // Removed as it's redundant and causes type conflicts
140
247
 
141
248
  registerComponent("Text", CanvasText);
142
249
 
@@ -5,7 +5,7 @@ import { Sprite } from "./Sprite";
5
5
  import { effect, Signal, signal } from "@signe/reactive";
6
6
 
7
7
  interface VideoProps {
8
- source: string;
8
+ src: string;
9
9
  paused?: boolean;
10
10
  loop?: boolean;
11
11
  muted?: boolean;
@@ -87,7 +87,7 @@ export function Video(props: VideoProps) {
87
87
 
88
88
  return h(Sprite, {
89
89
  ...props,
90
- image: props.source,
90
+ image: props.src,
91
91
  loader: {
92
92
  onComplete: (texture) => {
93
93
  const source = texture.source.resource
@@ -1,8 +1,9 @@
1
1
  import { Viewport as PixiViewport } from 'pixi-viewport';
2
2
  import { Subscription } from 'rxjs';
3
- import { createComponent, registerComponent } from '../engine/reactive';
4
- import { DisplayObject } from './DisplayObject';
5
- import { effect } from '@signe/reactive';
3
+ import { createComponent, registerComponent, Element, Props } from '../engine/reactive';
4
+ import { DisplayObject, ComponentInstance } from './DisplayObject';
5
+ import { effect, Signal } from '@signe/reactive';
6
+ import { Graphics, Container, ContainerChild, IRenderLayer } from 'pixi.js';
6
7
 
7
8
  const EVENTS = [
8
9
  'bounce-x-end',
@@ -28,55 +29,102 @@ const EVENTS = [
28
29
  'zoomed-end'
29
30
  ]
30
31
 
31
- export class CanvasViewport extends DisplayObject(PixiViewport) {
32
+ export interface ViewportProps extends Props {
33
+ screenWidth?: number;
34
+ screenHeight?: number;
35
+ worldWidth?: number;
36
+ worldHeight?: number;
37
+ clamp?: boolean | {
38
+ left?: number;
39
+ right?: number;
40
+ top?: number;
41
+ bottom?: number;
42
+ };
43
+ context?: any;
44
+ [key: string]: any;
45
+ }
46
+
47
+ export class CanvasViewport extends DisplayObject(Container) {
32
48
  private tickSubscription: Subscription
33
49
  overrideProps = ['wheel']
50
+ #mask: Graphics
51
+ public viewport: PixiViewport
34
52
 
35
53
  constructor() {
54
+ super()
36
55
  const defaultOptions = {
37
56
  noTicker: true,
38
57
  events: {
39
58
  domElement: {
40
- addEventListener: () => {}
59
+ addEventListener: () => { }
41
60
  }
42
61
  },
43
62
  }
44
63
  // @ts-ignore
45
- super(defaultOptions)
64
+ this.viewport = new PixiViewport(defaultOptions)
65
+ super.addChild(this.viewport)
66
+
67
+ this.#mask = new Graphics()
68
+ super.addChild(this.#mask)
69
+ this.mask = this.#mask
70
+ }
71
+
72
+ addChild<U extends any[]>(...children: U): U[0] {
73
+ return this.viewport.addChild(...children)
74
+ }
75
+
76
+ addChildAt<T extends ContainerChild | IRenderLayer>(child: T, index: number): T {
77
+ return this.viewport.addChildAt(child, index) as T
46
78
  }
47
79
 
48
80
  onInit(props) {
49
81
  super.onInit(props)
50
82
  for (let event of EVENTS) {
51
- const camelCaseEvent = event.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
52
- if (props[camelCaseEvent]) {
53
- this.on(event, props[camelCaseEvent])
54
- }
83
+ if (props[event]) this.viewport.on(event, props[event])
55
84
  }
56
85
  }
57
86
 
58
- onMount(element) {
59
- super.onMount(element)
60
- const { tick, renderer, canvasSize } = element.props.context
61
- let isDragging = false
62
-
87
+ /**
88
+ * Called when the component is mounted to the scene graph.
89
+ * Initializes viewport settings and subscriptions.
90
+ * @param {Element<CanvasViewport>} element - The element being mounted. Its `props` property (of type ViewportProps) contains component properties and context.
91
+ * @param {number} [index] - The index of the component among its siblings.
92
+ */
93
+ async onMount(element: Element<any>, index?: number): Promise<void> {
94
+ element.props.context.viewport = this.viewport
95
+ await super.onMount(element, index);
96
+ const { props } = element;
97
+ const { tick, app, canvasSize } = props.context;
98
+
99
+ effect(() => {
100
+ if (props.screenWidth === undefined) {
101
+ this.viewport.screenWidth = canvasSize().width
102
+ }
103
+ if (props.screenHeight === undefined) {
104
+ this.viewport.screenHeight = canvasSize().height
105
+ }
106
+ this.updateMask()
107
+ })
108
+
63
109
  effect(() => {
64
- this.screenWidth = canvasSize().width
65
- this.screenHeight = canvasSize().height
110
+ const _app = app()
111
+ if (!_app) return
112
+
113
+ const renderer = _app.renderer
114
+
115
+ renderer.events.domElement.addEventListener(
116
+ 'wheel',
117
+ this.viewport.input.wheelFunction
118
+ );
119
+
120
+ this.viewport.options.events = renderer.events
66
121
  })
67
122
 
68
- renderer.events.domElement.addEventListener(
69
- 'wheel',
70
- this.input.wheelFunction
71
- );
72
- this.options.events = renderer.events
73
-
74
123
  this.tickSubscription = tick.observable.subscribe(({ value }) => {
75
- this.update(value.timestamp)
124
+ this.viewport.update(value.deltaTime)
76
125
  })
77
126
 
78
- element.props.context.viewport = this
79
- this.updateViewportSettings(element.props)
127
+ this.updateViewportSettings(props)
80
128
  }
81
129
 
82
130
  onUpdate(props) {
@@ -86,74 +134,81 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
86
134
 
87
135
  private updateViewportSettings(props) {
88
136
  if (props.screenWidth !== undefined) {
89
- this.screenWidth = props.screenWidth
137
+ this.viewport.screenWidth = props.screenWidth
90
138
  }
91
139
  if (props.screenHeight !== undefined) {
92
- this.screenHeight = props.screenHeight
140
+ this.viewport.screenHeight = props.screenHeight
93
141
  }
142
+ this.updateMask()
94
143
  if (props.worldWidth !== undefined) {
95
- this.worldWidth = props.worldWidth
144
+ this.viewport.worldWidth = props.worldWidth
96
145
  }
97
146
  if (props.worldHeight !== undefined) {
98
- this.worldHeight = props.worldHeight
147
+ this.viewport.worldHeight = props.worldHeight
148
+ }
149
+ if (props.drag) {
150
+ this.viewport.drag(props.drag)
99
151
  }
100
- // if (props.drag) {
101
- // if (props.drag === true) {
102
-
103
- // } else {
104
- // this.drag(props.drag)
105
- // }
106
- // }
107
152
  if (props.clamp) {
108
- this.clamp(props.clamp)
153
+ this.viewport.clamp(props.clamp.value ?? props.clamp)
109
154
  }
110
155
  if (props.wheel) {
111
156
  if (props.wheel === true) {
112
- this.wheel()
157
+ this.viewport.wheel()
113
158
  } else {
114
- this.wheel(props.wheel)
159
+ this.viewport.wheel(props.wheel)
115
160
  }
116
161
  }
117
162
  if (props.decelerate) {
118
163
  if (props.decelerate === true) {
119
- this.decelerate()
164
+ this.viewport.decelerate()
120
165
  } else {
121
- this.decelerate(props.decelerate)
166
+ this.viewport.decelerate(props.decelerate)
122
167
  }
123
168
  }
124
169
  if (props.pinch) {
125
170
  if (props.pinch === true) {
126
- this.pinch()
171
+ this.viewport.pinch()
127
172
  } else {
128
- this.pinch(props.pinch)
173
+ this.viewport.pinch(props.pinch)
129
174
  }
130
175
  }
131
176
  }
132
177
 
133
- onDestroy(): void {
134
- super.onDestroy()
135
- this.tickSubscription.unsubscribe()
178
+ private updateMask() {
179
+ if (!this.#mask) return
180
+ this.#mask.clear()
181
+ this.#mask.beginFill(0xffffff)
182
+ this.#mask.drawRect(0, 0, this.viewport.screenWidth, this.viewport.screenHeight)
183
+ this.#mask.endFill()
136
184
  }
137
- }
138
185
 
139
- export interface CanvasViewport extends PixiViewport { }
186
+ /**
187
+ * Called when the component is about to be destroyed.
188
+ * Unsubscribes from the tick observable.
189
+ * @param {Element<any>} parent - The parent element.
190
+ * @param {() => void} [afterDestroy] - An optional callback function to be executed after the component's own destruction logic.
191
+ */
192
+ async onDestroy(parent: Element<any>, afterDestroy?: () => void): Promise<void> {
193
+ const _afterDestroy = async () => {
194
+ this.tickSubscription.unsubscribe()
195
+ afterDestroy()
196
+ }
197
+ await super.onDestroy(parent, _afterDestroy);
198
+ }
140
199
 
141
- registerComponent('Viewport', CanvasViewport)
200
+ // Proxy methods for viewport plugins
201
+ follow(...args: any[]) {
202
+ return (this.viewport.follow as any)(...args)
203
+ }
142
204
 
143
- export interface ViewportProps {
144
- screenWidth?: number;
145
- screenHeight?: number;
146
- worldWidth?: number;
147
- worldHeight?: number;
148
- clamp?: boolean | {
149
- left?: number;
150
- right?: number;
151
- top?: number;
152
- bottom?: number;
153
- };
154
- [key: string]: any;
205
+ get plugins() {
206
+ return this.viewport.plugins
207
+ }
155
208
  }
156
209
 
210
+ registerComponent('Viewport', CanvasViewport)
211
+
157
212
  export function Viewport(props: ViewportProps) {
158
213
  return createComponent('Viewport', props);
159
- }
214
+ }
@@ -1,6 +1,7 @@
1
1
  export { Canvas } from './Canvas'
2
2
  export { Container } from './Container'
3
- export { Graphics, Rect, Circle, Ellipse, Triangle, Svg as svg } from './Graphic'
3
+ export { Graphics, Rect, Circle, Ellipse, Triangle, Svg } from './Graphic'
4
+ export { Mesh } from './Mesh'
4
5
  export { Scene } from './Scene'
5
6
  export { ParticlesEmitter } from './ParticleEmitter'
6
7
  export { Sprite } from './Sprite'
@@ -9,4 +10,10 @@ export { Text } from './Text'
9
10
  export { TilingSprite } from './TilingSprite'
10
11
  export { Viewport } from './Viewport'
11
12
  export { NineSliceSprite } from './NineSliceSprite'
12
- export { type ComponentInstance } from './DisplayObject'
13
+ export { type ComponentInstance } from './DisplayObject'
14
+ export { DOMContainer } from './DOMContainer'
15
+ export { DOMElement } from './DOMElement'
16
+ export { DOMSprite } from './DOMSprite'
17
+ export { Button, ButtonState, type ButtonProps, type ButtonStyle } from './Button'
18
+ export { Joystick, type JoystickSettings } from './Joystick'
19
+ export { FocusContainer, Navigation, type FocusContainerProps } from './FocusContainer'
@@ -1,18 +1,47 @@
1
1
  import * as PIXI from "pixi.js";
2
2
  import { SignalOrPrimitive } from ".";
3
+ import { DragProps } from "../../directives/Drag";
4
+ import { ViewportFollowProps } from "../../directives/ViewportFollow";
5
+ import { ShakeProps } from "../../directives/Shake";
6
+ import { FlashProps } from "../../directives/Flash";
3
7
 
4
8
  export type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
5
9
  export type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
6
10
  export type AlignContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around';
7
11
  export type Size = number | `${number}%`
8
12
  export type EdgeSize = SignalOrPrimitive<Size | [Size, Size] | [Size, Size, Size, Size]>
13
+ export type ObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
14
+ export type ObjectPosition = string;
15
+ export type TransformOrigin = string;
16
+ export type PositionType = 'relative' | 'absolute' | 'static';
17
+
18
+ export type ObservablePointSignal = [number, number] | SignalOrPrimitive<[number, number]> | { x: number, y: number } | SignalOrPrimitive<{ x: number, y: number }>;
9
19
 
10
20
  export interface DisplayObjectProps {
21
+ attach?: any;
11
22
  ref?: string;
12
23
  x?: SignalOrPrimitive<number>;
13
24
  y?: SignalOrPrimitive<number>;
14
25
  width?: SignalOrPrimitive<Size>;
15
26
  height?: SignalOrPrimitive<Size>;
27
+ minWidth?: SignalOrPrimitive<Size>;
28
+ minHeight?: SignalOrPrimitive<Size>;
29
+ maxWidth?: SignalOrPrimitive<Size>;
30
+ maxHeight?: SignalOrPrimitive<Size>;
31
+ aspectRatio?: SignalOrPrimitive<number>;
32
+ flexGrow?: SignalOrPrimitive<number>;
33
+ flexShrink?: SignalOrPrimitive<number>;
34
+ flexBasis?: SignalOrPrimitive<Size>;
35
+ rowGap?: SignalOrPrimitive<number>;
36
+ columnGap?: SignalOrPrimitive<number>;
37
+ positionType?: PositionType;
38
+ top?: SignalOrPrimitive<Size>;
39
+ right?: SignalOrPrimitive<Size>;
40
+ bottom?: SignalOrPrimitive<Size>;
41
+ left?: SignalOrPrimitive<Size>;
42
+ objectFit?: ObjectFit;
43
+ objectPosition?: ObjectPosition;
44
+ transformOrigin?: TransformOrigin;
16
45
  children?: any[];
17
46
  flexDirection?: FlexDirection;
18
47
  justifyContent?: JustifyContent;
@@ -21,9 +50,9 @@ export interface DisplayObjectProps {
21
50
  padding?: EdgeSize;
22
51
  border?: EdgeSize;
23
52
  absolute?: SignalOrPrimitive<boolean>;
24
- scale?: SignalOrPrimitive<{ x: number, y: number } | number>;
25
- anchor?: SignalOrPrimitive<{ x: number, y: number }>;
26
- skew?: SignalOrPrimitive<{ x: number, y: number }>;
53
+ scale?: ObservablePointSignal | number;
54
+ anchor?: ObservablePointSignal;
55
+ skew?: ObservablePointSignal;
27
56
  tint?: SignalOrPrimitive<number>;
28
57
  rotation?: SignalOrPrimitive<number>;
29
58
  angle?: SignalOrPrimitive<number>;
@@ -31,11 +60,18 @@ export interface DisplayObjectProps {
31
60
  roundPixels?: SignalOrPrimitive<boolean>;
32
61
  cursor?: SignalOrPrimitive<string>;
33
62
  visible?: SignalOrPrimitive<boolean>;
34
- pivot?: SignalOrPrimitive<{ x: number, y: number }>;
63
+ pivot?: ObservablePointSignal;
35
64
  filters?: any[];
36
65
  blendMode?: SignalOrPrimitive<PIXI.BLEND_MODES>;
37
66
  blur?: SignalOrPrimitive<number>;
38
67
 
68
+ // Directives
69
+ drag?: DragProps;
70
+ viewportFollow?: ViewportFollowProps;
71
+ shake?: ShakeProps;
72
+ flash?: FlashProps;
73
+
74
+ // Events
39
75
  click?: PIXI.FederatedEventHandler;
40
76
  mousedown?: PIXI.FederatedEventHandler;
41
77
  mouseenter?: PIXI.FederatedEventHandler;
@@ -66,4 +102,5 @@ export interface DisplayObjectProps {
66
102
  touchmove?: PIXI.FederatedEventHandler;
67
103
  touchstart?: PIXI.FederatedEventHandler;
68
104
  wheel?: PIXI.FederatedEventHandler<PIXI.FederatedWheelEvent>;
105
+ tabindex?: SignalOrPrimitive<number>;
69
106
  }