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.
- package/dist/{DebugRenderer-BYa_lwD-.js → DebugRenderer-DDfZuvTR.js} +2 -2
- package/dist/{DebugRenderer-BYa_lwD-.js.map → DebugRenderer-DDfZuvTR.js.map} +1 -1
- package/dist/components/Container.d.ts +4 -0
- package/dist/components/Container.d.ts.map +1 -1
- package/dist/components/DOMContainer.d.ts +4 -0
- package/dist/components/DOMContainer.d.ts.map +1 -1
- package/dist/components/DisplayObject.d.ts +4 -0
- package/dist/components/DisplayObject.d.ts.map +1 -1
- package/dist/components/Mesh.d.ts +4 -0
- package/dist/components/Mesh.d.ts.map +1 -1
- package/dist/components/Sprite.d.ts +48 -0
- package/dist/components/Sprite.d.ts.map +1 -1
- package/dist/components/Viewport.d.ts +4 -0
- package/dist/components/Viewport.d.ts.map +1 -1
- package/dist/components/types/DisplayObject.d.ts +4 -0
- package/dist/components/types/DisplayObject.d.ts.map +1 -1
- package/dist/directives/Controls.d.ts +102 -0
- package/dist/directives/Controls.d.ts.map +1 -0
- package/dist/directives/ControlsBase.d.ts +198 -0
- package/dist/directives/ControlsBase.d.ts.map +1 -0
- package/dist/directives/Flash.d.ts +117 -0
- package/dist/directives/Flash.d.ts.map +1 -0
- package/dist/directives/GamepadControls.d.ts +223 -0
- package/dist/directives/GamepadControls.d.ts.map +1 -0
- package/dist/directives/KeyboardControls.d.ts +55 -366
- package/dist/directives/KeyboardControls.d.ts.map +1 -1
- package/dist/directives/Shake.d.ts +98 -0
- package/dist/directives/Shake.d.ts.map +1 -0
- package/dist/directives/index.d.ts +11 -1
- package/dist/directives/index.d.ts.map +1 -1
- package/dist/engine/trigger.d.ts +2 -3
- package/dist/engine/trigger.d.ts.map +1 -1
- package/dist/engine/utils.d.ts.map +1 -1
- package/dist/{index-BLbc2zG5.js → index--faZajmD.js} +4547 -3970
- package/dist/index--faZajmD.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.js +6 -6
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +70 -57
- package/package.json +2 -1
- package/src/components/Container.ts +17 -0
- package/src/components/DisplayObject.ts +45 -6
- package/src/components/Sprite.ts +87 -3
- package/src/components/types/DisplayObject.ts +4 -0
- package/src/directives/Controls.ts +182 -0
- package/src/directives/ControlsBase.ts +266 -0
- package/src/directives/Flash.ts +409 -0
- package/src/directives/GamepadControls.ts +515 -0
- package/src/directives/KeyboardControls.ts +66 -426
- package/src/directives/Shake.ts +282 -0
- package/src/directives/index.ts +11 -6
- package/src/engine/trigger.ts +2 -2
- package/src/engine/utils.ts +4 -0
- package/src/index.ts +1 -1
- 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
|
+
|
package/src/directives/index.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
export * from './Sound'
|
|
9
|
+
export * from './Drag'
|
|
10
|
+
export * from './Transition'
|
|
11
|
+
export * from './Shake'
|
|
12
|
+
export * from './Flash'
|
package/src/engine/trigger.ts
CHANGED
|
@@ -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
|
}
|
package/src/engine/utils.ts
CHANGED
|
@@ -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