canvasengine 2.0.0-beta.14 → 2.0.0-beta.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasengine",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,5 @@
1
- import { computed, effect, isSignal, Signal, WritableSignal } from "@signe/reactive";
1
+ import { Howl } from 'howler';
2
+ import { computed, effect, isSignal, Signal } from "@signe/reactive";
2
3
  import {
3
4
  Assets,
4
5
  Container,
@@ -338,7 +339,12 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
338
339
  const sound = this.currentAnimation.data.sound;
339
340
 
340
341
  if (sound) {
341
- //RpgSound.get(sound).play()
342
+ new Howl({
343
+ src: sound,
344
+ autoplay: true,
345
+ loop: false,
346
+ volume: 1,
347
+ })
342
348
  }
343
349
 
344
350
  // Updates immediately to avoid flickering
@@ -14,6 +14,7 @@ export type DragProps = {
14
14
  end?: () => void;
15
15
  snap?: SignalOrPrimitive<number>;
16
16
  direction?: SignalOrPrimitive<'x' | 'y' | 'all'>;
17
+ keyToPress?: SignalOrPrimitive<string[]>;
17
18
  viewport?: {
18
19
  edgeThreshold?: SignalOrPrimitive<number>;
19
20
  maxSpeed?: SignalOrPrimitive<number>;
@@ -46,10 +47,14 @@ export class Drag extends Directive {
46
47
  private viewport: any | null = null;
47
48
  private animationFrameId: number | null = null;
48
49
  private lastPointerPosition: Point = new Point();
50
+ private pressedKeys: Set<string> = new Set();
51
+ private pointerIsDown = false;
49
52
 
50
53
  private onDragMoveHandler: (event: FederatedPointerEvent) => void = () => {};
51
54
  private onDragEndHandler: () => void = () => {};
52
55
  private onDragStartHandler: (event: FederatedPointerEvent) => void = () => {};
56
+ private onKeyDownHandler: (event: KeyboardEvent) => void = () => {};
57
+ private onKeyUpHandler: (event: KeyboardEvent) => void = () => {};
53
58
 
54
59
  private subscriptions: Subscription[] = [];
55
60
 
@@ -58,6 +63,8 @@ export class Drag extends Directive {
58
63
  this.onDragMoveHandler = this.onDragMove.bind(this);
59
64
  this.onDragEndHandler = this.onDragEnd.bind(this);
60
65
  this.onDragStartHandler = this.onPointerDown.bind(this);
66
+ this.onKeyDownHandler = this.onKeyDown.bind(this);
67
+ this.onKeyUpHandler = this.onKeyUp.bind(this);
61
68
  }
62
69
 
63
70
  onMount(element: Element<Container>) {
@@ -89,6 +96,12 @@ export class Drag extends Directive {
89
96
  this.stageRef.on('pointerup', this.onDragEndHandler);
90
97
  this.stageRef.on('pointerupoutside', this.onDragEndHandler);
91
98
 
99
+ const keysToPress = dragProps.keyToPress ? dragProps.keyToPress : [];
100
+
101
+ // Always add keyboard event listeners to track pressed keys
102
+ window.addEventListener('keydown', this.onKeyDownHandler);
103
+ window.addEventListener('keyup', this.onKeyUpHandler);
104
+
92
105
  this.subscriptions = [
93
106
  tick.observable.subscribe(() => {
94
107
  if (this.isDragging && this.viewport) {
@@ -104,7 +117,8 @@ export class Drag extends Directive {
104
117
  const options = useProps(drag?.value ?? drag, {
105
118
  snap: 0,
106
119
  viewport: {},
107
- direction: 'all'
120
+ direction: 'all',
121
+ keyToPress: []
108
122
  });
109
123
  options.viewport = useProps(options.viewport, {
110
124
  edgeThreshold: 300,
@@ -243,10 +257,13 @@ export class Drag extends Directive {
243
257
  * Handles drag end event and stops viewport movement
244
258
  */
245
259
  private onDragEnd() {
246
- if (!this.isDragging || !this.elementRef) return;
260
+ this.pointerIsDown = false;
261
+
262
+ if (!this.isDragging) return;
247
263
 
248
264
  const dragProps = this.dragProps;
249
265
  this.isDragging = false;
266
+
250
267
  dragProps?.end?.();
251
268
 
252
269
  if (this.stageRef) {
@@ -254,23 +271,86 @@ export class Drag extends Directive {
254
271
  }
255
272
  }
256
273
 
274
+ onKeyDown(event: KeyboardEvent) {
275
+ this.pressedKeys.add(event.code);
276
+ this.pressedKeys.add(event.key.toLowerCase());
277
+
278
+ if (this.pointerIsDown && !this.isDragging && this.areRequiredKeysPressed()) {
279
+ this.startDrag();
280
+ }
281
+ }
282
+
283
+ onKeyUp(event: KeyboardEvent) {
284
+ this.pressedKeys.delete(event.code);
285
+ this.pressedKeys.delete(event.key.toLowerCase());
286
+ if (this.isDragging && !this.areRequiredKeysPressed()) {
287
+ this.onDragEnd();
288
+ }
289
+ }
290
+
291
+ private areRequiredKeysPressed(): boolean {
292
+ const keyToPress = this.dragProps.keyToPress ? this.dragProps.keyToPress : [];
293
+ if (!keyToPress || keyToPress.length === 0) {
294
+ return true; // No keys required, always return true
295
+ }
296
+
297
+ return keyToPress.some(key => {
298
+ // Check if the key is pressed directly
299
+ if (this.pressedKeys.has(key)) {
300
+ return true;
301
+ }
302
+
303
+ // Check common alternative formats
304
+ // Space key can be "Space", " ", or "space"
305
+ if (key.toLowerCase() === 'space') {
306
+ return this.pressedKeys.has('Space') || this.pressedKeys.has(' ');
307
+ }
308
+
309
+ // Shift key can be "ShiftLeft", "ShiftRight", or "shift"
310
+ if (key.toLowerCase() === 'shift') {
311
+ return this.pressedKeys.has('ShiftLeft') || this.pressedKeys.has('ShiftRight');
312
+ }
313
+
314
+ // Control key can be "ControlLeft", "ControlRight", or "control"
315
+ if (key.toLowerCase() === 'control' || key.toLowerCase() === 'ctrl') {
316
+ return this.pressedKeys.has('ControlLeft') || this.pressedKeys.has('ControlRight');
317
+ }
318
+
319
+ // Alt key can be "AltLeft", "AltRight", or "alt"
320
+ if (key.toLowerCase() === 'alt') {
321
+ return this.pressedKeys.has('AltLeft') || this.pressedKeys.has('AltRight');
322
+ }
323
+
324
+ return false;
325
+ });
326
+ }
327
+
257
328
  private onPointerDown(event: FederatedPointerEvent) {
258
329
  if (!this.elementRef?.componentInstance || !this.stageRef || !this.elementRef.componentInstance.parent) return;
330
+
331
+ this.pointerIsDown = true;
259
332
 
260
333
  const instance = this.elementRef.componentInstance;
261
334
  const parent = instance.parent;
262
- const dragProps = this.dragProps;
263
335
 
264
336
  const parentLocalPointer = parent.toLocal(event.global);
265
337
 
266
338
  this.offsetInParent.x = parentLocalPointer.x - instance.position.x;
267
339
  this.offsetInParent.y = parentLocalPointer.y - instance.position.y;
268
-
269
- this.isDragging = true;
270
340
 
271
341
  // Store initial pointer position
272
342
  this.lastPointerPosition.copyFrom(event.global);
273
343
 
344
+ if (this.areRequiredKeysPressed()) {
345
+ this.startDrag();
346
+ }
347
+ }
348
+
349
+ private startDrag() {
350
+ if (this.isDragging || !this.stageRef) return;
351
+
352
+ this.isDragging = true;
353
+ const dragProps = this.dragProps;
274
354
  dragProps?.start?.();
275
355
  this.stageRef.on('pointermove', this.onDragMoveHandler);
276
356
  }
@@ -293,8 +373,15 @@ export class Drag extends Directive {
293
373
  this.stageRef.off('pointerup', this.onDragEndHandler);
294
374
  this.stageRef.off('pointerupoutside', this.onDragEndHandler);
295
375
  }
376
+
377
+ // Remove keyboard event listeners
378
+ window.removeEventListener('keydown', this.onKeyDownHandler);
379
+ window.removeEventListener('keyup', this.onKeyUpHandler);
380
+
296
381
  this.stageRef = null;
297
382
  this.viewport = null;
383
+ this.pressedKeys.clear();
384
+ this.pointerIsDown = false;
298
385
  }
299
386
  }
300
387
 
@@ -20,7 +20,8 @@ export class Sound extends Directive {
20
20
  onMount(element: Element<Container>) {
21
21
  const { props } = element
22
22
  const tick = props.context.tick
23
- const { src, autoplay, loop, volume, spatial } = props.sound
23
+ const propsSound = props.sound.value ?? props.sound
24
+ const { src, autoplay, loop, volume, spatial } = propsSound
24
25
  this.sound = new Howl({
25
26
  src,
26
27
  autoplay,
@@ -28,8 +29,8 @@ export class Sound extends Directive {
28
29
  volume
29
30
  })
30
31
  for (let event of EVENTS) {
31
- if (!props.sound[event]) continue
32
- const fn = props.sound[event]
32
+ if (!propsSound[event]) continue
33
+ const fn = propsSound[event]
33
34
  this.eventsFn.push(fn)
34
35
  this.sound.on(event, fn);
35
36
  }
@@ -51,7 +52,7 @@ export class Sound extends Directive {
51
52
  }
52
53
 
53
54
  onUpdate(props: any) {
54
- const { volume, loop, mute, seek, playing, rate, spatial } = props
55
+ const { volume, loop, mute, seek, playing, rate, spatial } = props.value ?? props
55
56
  if (volume != undefined) this.sound.volume(volume)
56
57
  if (loop != undefined) this.sound.loop(loop)
57
58
  if (mute != undefined) this.sound.mute(mute)
@@ -365,6 +365,36 @@ export function loop<T>(
365
365
  el.destroy();
366
366
  elementMap.delete(change.index!);
367
367
  });
368
+ } else if (change.type === 'update' && change.index !== undefined && change.items.length === 1) {
369
+ const index = change.index;
370
+ const newItem = change.items[0];
371
+
372
+ // Check if the previous item at this index was effectively undefined or non-existent
373
+ if (index >= elements.length || elements[index] === undefined || !elementMap.has(index)) {
374
+ // Treat as add operation
375
+ const newElement = createElementFn(newItem as T, index);
376
+ if (newElement) {
377
+ elements.splice(index, 0, newElement); // Insert at the correct index
378
+ elementMap.set(index, newElement);
379
+ // Adjust indices in elementMap for subsequent elements might be needed if map relied on exact indices
380
+ // This simple implementation assumes keys are stable or createElementFn handles context correctly
381
+ } else {
382
+ console.warn(`Element creation returned null for index ${index} during add-like update.`);
383
+ }
384
+ } else {
385
+ // Treat as a standard update operation
386
+ const oldElement = elements[index];
387
+ oldElement.destroy();
388
+ const newElement = createElementFn(newItem as T, index);
389
+ if (newElement) {
390
+ elements[index] = newElement;
391
+ elementMap.set(index, newElement);
392
+ } else {
393
+ // Handle case where new element creation returns null
394
+ elements.splice(index, 1);
395
+ elementMap.delete(index);
396
+ }
397
+ }
368
398
  }
369
399
 
370
400
  subscriber.next({
package/src/index.ts CHANGED
@@ -12,4 +12,5 @@ export * from './utils/Ease'
12
12
  export * from './utils/RadialGradient'
13
13
  export * from './components/DisplayObject'
14
14
  export { isObservable } from 'rxjs'
15
- export * as Utils from './engine/utils'
15
+ export * as Utils from './engine/utils'
16
+ export * as Howl from 'howler'