canvasengine 2.0.0-beta.34 → 2.0.0-beta.36

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 (56) hide show
  1. package/dist/{DebugRenderer-BYa_lwD-.js → DebugRenderer-DDfZuvTR.js} +2 -2
  2. package/dist/{DebugRenderer-BYa_lwD-.js.map → DebugRenderer-DDfZuvTR.js.map} +1 -1
  3. package/dist/components/Container.d.ts +4 -0
  4. package/dist/components/Container.d.ts.map +1 -1
  5. package/dist/components/DOMContainer.d.ts +4 -0
  6. package/dist/components/DOMContainer.d.ts.map +1 -1
  7. package/dist/components/DisplayObject.d.ts +4 -0
  8. package/dist/components/DisplayObject.d.ts.map +1 -1
  9. package/dist/components/Mesh.d.ts +4 -0
  10. package/dist/components/Mesh.d.ts.map +1 -1
  11. package/dist/components/Sprite.d.ts +48 -0
  12. package/dist/components/Sprite.d.ts.map +1 -1
  13. package/dist/components/Viewport.d.ts +4 -0
  14. package/dist/components/Viewport.d.ts.map +1 -1
  15. package/dist/components/types/DisplayObject.d.ts +4 -0
  16. package/dist/components/types/DisplayObject.d.ts.map +1 -1
  17. package/dist/directives/Controls.d.ts +102 -0
  18. package/dist/directives/Controls.d.ts.map +1 -0
  19. package/dist/directives/ControlsBase.d.ts +198 -0
  20. package/dist/directives/ControlsBase.d.ts.map +1 -0
  21. package/dist/directives/Flash.d.ts +117 -0
  22. package/dist/directives/Flash.d.ts.map +1 -0
  23. package/dist/directives/GamepadControls.d.ts +223 -0
  24. package/dist/directives/GamepadControls.d.ts.map +1 -0
  25. package/dist/directives/KeyboardControls.d.ts +55 -366
  26. package/dist/directives/KeyboardControls.d.ts.map +1 -1
  27. package/dist/directives/Shake.d.ts +98 -0
  28. package/dist/directives/Shake.d.ts.map +1 -0
  29. package/dist/directives/index.d.ts +11 -1
  30. package/dist/directives/index.d.ts.map +1 -1
  31. package/dist/engine/trigger.d.ts +2 -3
  32. package/dist/engine/trigger.d.ts.map +1 -1
  33. package/dist/engine/utils.d.ts.map +1 -1
  34. package/dist/{index-BLbc2zG5.js → index--faZajmD.js} +4547 -3970
  35. package/dist/index--faZajmD.js.map +1 -0
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.global.js +6 -6
  39. package/dist/index.global.js.map +1 -1
  40. package/dist/index.js +70 -57
  41. package/package.json +2 -1
  42. package/src/components/Container.ts +17 -0
  43. package/src/components/DisplayObject.ts +45 -6
  44. package/src/components/Sprite.ts +87 -3
  45. package/src/components/types/DisplayObject.ts +4 -0
  46. package/src/directives/Controls.ts +182 -0
  47. package/src/directives/ControlsBase.ts +266 -0
  48. package/src/directives/Flash.ts +409 -0
  49. package/src/directives/GamepadControls.ts +515 -0
  50. package/src/directives/KeyboardControls.ts +66 -426
  51. package/src/directives/Shake.ts +282 -0
  52. package/src/directives/index.ts +11 -6
  53. package/src/engine/trigger.ts +2 -2
  54. package/src/engine/utils.ts +4 -0
  55. package/src/index.ts +1 -1
  56. package/dist/index-BLbc2zG5.js.map +0 -1
@@ -0,0 +1,282 @@
1
+ import { Container, Point } from 'pixi.js';
2
+ import { Directive, registerDirective } from '../engine/directive';
3
+ import { Element } from '../engine/reactive';
4
+ import { effect } from '@signe/reactive';
5
+ import { on, isTrigger, Trigger } from '../engine/trigger';
6
+ import { useProps } from '../hooks/useProps';
7
+ import { SignalOrPrimitive } from '../components/types';
8
+ import { animatedSignal, AnimatedSignal } from '../engine/animation';
9
+ import { Subscription } from 'rxjs';
10
+
11
+ export type ShakeProps = {
12
+ /**
13
+ * Trigger that activates the shake animation
14
+ * When the trigger is activated, the shake animation will start
15
+ */
16
+ trigger?: Trigger<any>;
17
+ /**
18
+ * Intensity of the shake effect (in pixels)
19
+ * @default 10
20
+ */
21
+ intensity?: SignalOrPrimitive<number>;
22
+ /**
23
+ * Duration of the shake animation in milliseconds
24
+ * @default 500
25
+ */
26
+ duration?: SignalOrPrimitive<number>;
27
+ /**
28
+ * Number of shake oscillations during the animation
29
+ * Higher values create more rapid shaking
30
+ * @default 10
31
+ */
32
+ frequency?: SignalOrPrimitive<number>;
33
+ /**
34
+ * Direction of the shake: 'x', 'y', or 'both'
35
+ * @default 'both'
36
+ */
37
+ direction?: SignalOrPrimitive<'x' | 'y' | 'both'>;
38
+ /**
39
+ * Callback function called when shake starts
40
+ */
41
+ onStart?: () => void;
42
+ /**
43
+ * Callback function called when shake completes
44
+ */
45
+ onComplete?: () => void;
46
+ }
47
+
48
+ /**
49
+ * Shake directive that animates a display object's position when a trigger is activated.
50
+ * Creates a shake effect by rapidly oscillating the x and/or y position.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Basic usage with trigger
55
+ * const shakeTrigger = trigger();
56
+ *
57
+ * onMount(element) {
58
+ * // Element will shake when trigger is activated
59
+ * element.props.shake = { trigger: shakeTrigger };
60
+ * }
61
+ *
62
+ * // Trigger the shake
63
+ * shakeTrigger.start();
64
+ * ```
65
+ */
66
+ export class Shake extends Directive {
67
+ private elementRef: Element<Container> | null = null;
68
+ private originalPosition: Point = new Point();
69
+ private progressSignal: AnimatedSignal<number> | null = null;
70
+ private shakeSubscription: any = null;
71
+ private positionEffect: Subscription | null = null;
72
+ private currentShakeConfig: {
73
+ intensity: number;
74
+ frequency: number;
75
+ direction: 'x' | 'y' | 'both';
76
+ randomSeed: number;
77
+ } | null = null;
78
+
79
+ /**
80
+ * Initializes the shake directive
81
+ * @param element - The element to attach the shake effect to
82
+ */
83
+ onInit(element: Element<Container>) {
84
+ this.elementRef = element;
85
+ }
86
+
87
+ /**
88
+ * Mounts the shake directive and sets up trigger listener
89
+ * @param element - The element being mounted
90
+ */
91
+ onMount(element: Element<Container>) {
92
+ const instance = element.componentInstance;
93
+ if (!instance) return;
94
+
95
+ const shakeProps = this.shakeProps;
96
+
97
+ // Check if trigger is provided
98
+ if (!shakeProps.trigger || !isTrigger(shakeProps.trigger)) {
99
+ return;
100
+ }
101
+
102
+ // Store original position
103
+ this.originalPosition.set(instance.position.x, instance.position.y);
104
+
105
+ // Listen to trigger activation
106
+ this.shakeSubscription = on(shakeProps.trigger, async (data) => {
107
+ await this.performShake(data);
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Gets the shake props with default values
113
+ * @returns ShakeProps with defaults applied
114
+ */
115
+ get shakeProps(): ShakeProps {
116
+ const shake = this.elementRef?.props.shake;
117
+ return useProps(shake?.value ?? shake, {
118
+ intensity: 10,
119
+ duration: 500,
120
+ frequency: 10,
121
+ direction: 'both',
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Performs the shake animation using animatedSignal
127
+ * @param data - Optional data passed from the trigger that can override default options
128
+ */
129
+ private async performShake(data?: any): Promise<void> {
130
+ if (!this.elementRef?.componentInstance) return;
131
+
132
+ const instance = this.elementRef.componentInstance;
133
+ const shakeProps = this.shakeProps;
134
+
135
+ // Use data from trigger to override defaults if provided
136
+ const intensity = data?.intensity ?? shakeProps.intensity();
137
+ const duration = data?.duration ?? shakeProps.duration();
138
+ const frequency = data?.frequency ?? shakeProps.frequency();
139
+ const direction = data?.direction ?? shakeProps.direction();
140
+
141
+ // Stop any existing animation and clean up
142
+ if (this.positionEffect) {
143
+ this.positionEffect.unsubscribe();
144
+ this.positionEffect = null;
145
+ }
146
+
147
+ // Reset position to original before starting new shake
148
+ this.originalPosition.set(instance.position.x, instance.position.y);
149
+ instance.position.x = this.originalPosition.x;
150
+ instance.position.y = this.originalPosition.y;
151
+
152
+ // Call onStart callback
153
+ shakeProps.onStart?.();
154
+
155
+ // Store current shake configuration for use in effect
156
+ this.currentShakeConfig = {
157
+ intensity,
158
+ frequency,
159
+ direction,
160
+ randomSeed: Math.random() * 1000, // Fixed random seed for this shake
161
+ };
162
+
163
+ // Create or recreate progress signal for shake animation
164
+ // We recreate it to ensure a fresh animation state
165
+ if (this.progressSignal) {
166
+ // Reset to 0 immediately without animation
167
+ this.progressSignal.set(0, { duration: 0 });
168
+ // Wait a bit to ensure the reset is complete
169
+ await new Promise(resolve => setTimeout(resolve, 0));
170
+ } else {
171
+ this.progressSignal = animatedSignal(0, {
172
+ duration: duration,
173
+ ease: (t) => t, // Linear ease, we'll handle oscillation in effect
174
+ });
175
+ }
176
+
177
+ const shakeX = direction === 'y' ? false : true;
178
+ const shakeY = direction === 'x' ? false : true;
179
+
180
+ // Create effect to update position based on progress
181
+ this.positionEffect = effect(() => {
182
+ if (!instance || !this.progressSignal || !this.currentShakeConfig) return;
183
+
184
+ const progress = this.progressSignal();
185
+ const config = this.currentShakeConfig;
186
+
187
+ // Calculate decay factor (shake intensity decreases over time)
188
+ const decay = 1 - progress;
189
+
190
+ // Generate oscillation based on progress and frequency
191
+ // progress goes from 0 to 1, so we multiply by frequency to get oscillations
192
+ const time = progress * config.frequency;
193
+ const oscillation = Math.sin(time * Math.PI * 2);
194
+
195
+ // Apply shake with decay and consistent random variation
196
+ // Use the stored random seed for consistency during this shake
197
+ const randomValue = (Math.sin(config.randomSeed + progress * 10) * 0.25 + 1); // Between 0.75 and 1.25
198
+ const offsetX = shakeX ? oscillation * config.intensity * decay * randomValue : 0;
199
+ const offsetY = shakeY ? oscillation * config.intensity * decay * randomValue : 0;
200
+
201
+ // Update position
202
+ instance.position.x = this.originalPosition.x + offsetX;
203
+ instance.position.y = this.originalPosition.y + offsetY;
204
+ }).subscription;
205
+
206
+ // Start animation and wait for completion
207
+ // Note: animatedSignal.set() replaces onComplete with resolve, so we call onComplete after await
208
+ await this.progressSignal.set(1, {
209
+ duration: duration,
210
+ });
211
+
212
+ // Animation completed - clean up and call callbacks
213
+ // Reset to original position
214
+ if (instance) {
215
+ instance.position.x = this.originalPosition.x;
216
+ instance.position.y = this.originalPosition.y;
217
+ }
218
+
219
+ // Clean up position effect
220
+ if (this.positionEffect) {
221
+ this.positionEffect.unsubscribe();
222
+ this.positionEffect = null;
223
+ }
224
+
225
+ // Clear shake config
226
+ this.currentShakeConfig = null;
227
+
228
+ // Call onComplete callback
229
+ shakeProps.onComplete?.();
230
+ }
231
+
232
+ /**
233
+ * Updates the shake directive when props change
234
+ * @param props - Updated props
235
+ */
236
+ onUpdate(props: any) {
237
+ // Re-mount if props change significantly
238
+ if (props.type && props.type === 'reset') {
239
+ this.onDestroy();
240
+ if (this.elementRef) {
241
+ this.onMount(this.elementRef);
242
+ }
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Cleans up the shake directive
248
+ */
249
+ onDestroy() {
250
+ // Stop any running animation by resetting progress
251
+ if (this.progressSignal) {
252
+ this.progressSignal.set(0, { duration: 0 });
253
+ this.progressSignal = null;
254
+ }
255
+
256
+ // Clean up position effect
257
+ if (this.positionEffect) {
258
+ this.positionEffect.unsubscribe();
259
+ this.positionEffect = null;
260
+ }
261
+
262
+ // Clear shake config
263
+ this.currentShakeConfig = null;
264
+
265
+ // Reset position to original
266
+ if (this.elementRef?.componentInstance) {
267
+ const instance = this.elementRef.componentInstance;
268
+ instance.position.x = this.originalPosition.x;
269
+ instance.position.y = this.originalPosition.y;
270
+ }
271
+
272
+ // Clean up subscription
273
+ if (this.shakeSubscription) {
274
+ this.shakeSubscription = null;
275
+ }
276
+
277
+ this.elementRef = null;
278
+ }
279
+ }
280
+
281
+ registerDirective('shake', Shake);
282
+
@@ -1,7 +1,12 @@
1
- import './KeyboardControls'
2
- import './Scheduler'
3
- import './ViewportFollow'
1
+ export * from './ControlsBase'
2
+ export * from './KeyboardControls'
3
+ export * from './GamepadControls'
4
+ export * from './Controls'
5
+ export * from './Scheduler'
6
+ export * from './ViewportFollow'
4
7
  //import './ViewportCull'
5
- import './Sound'
6
- import './Drag'
7
- import './Transition'
8
+ export * from './Sound'
9
+ export * from './Drag'
10
+ export * from './Transition'
11
+ export * from './Shake'
12
+ export * from './Flash'
@@ -1,6 +1,6 @@
1
1
  import { effect, signal } from "@signe/reactive";
2
2
 
3
- interface Listen<T = any> {
3
+ export interface Listen<T = any> {
4
4
  config: T | undefined;
5
5
  seed: {
6
6
  config: T | undefined;
@@ -9,7 +9,7 @@ interface Listen<T = any> {
9
9
  };
10
10
  }
11
11
 
12
- interface Trigger<T = any> {
12
+ export interface Trigger<T = any> {
13
13
  start: () => Promise<void>;
14
14
  listen: () => Listen<T> | undefined;
15
15
  }
@@ -1,3 +1,4 @@
1
+ import { isSignal } from "@signe/reactive"
1
2
  import { ObservablePoint } from "pixi.js"
2
3
  import { Observable } from "rxjs"
3
4
 
@@ -198,6 +199,9 @@ export function setObservablePoint(
198
199
  else if (Array.isArray(point)) {
199
200
  observablePoint.set(point[0], point[1]);
200
201
  }
202
+ else if (isObject(point) && 'value' in point) {
203
+ observablePoint.set((point as any).value.x, (point as any).value.y);
204
+ }
201
205
  else {
202
206
  observablePoint.set(point.x, point.y);
203
207
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import './directives'
2
+ export * from './directives'
3
3
  export * from '@signe/reactive'
4
4
  export { Howler } from 'howler'
5
5
  export * from './components'