canvasengine 2.0.0-beta.34 → 2.0.0-beta.35

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/index.js CHANGED
@@ -1,64 +1,75 @@
1
- import { h as a } from "./index-BLbc2zG5.js";
2
- import { A as r, B as n, w as o, C as c, m as l, l as p, D as S, v as m, a5 as u, a4 as g, a2 as b, n as d, G as C, c as j, M as T, N as E, O, P as f, a3 as v, R as w, o as h, p as D, S as P, q as k, r as x, T as y, b as A, V as H, t as M, a1 as V, a0 as B, _ as G, d as N, J as R, H as q, K as U, e as z, W as I, $ as J, f as K, g as L, x as Q, j as W, i as X, y as Y, k as Z, X as _, I as $, Q as F, L as aa, Z as sa, z as ea, s as ta, U as ia, Y as ra, a as na, u as oa } from "./index-BLbc2zG5.js";
1
+ import { h as a } from "./index-B9ATEmLe.js";
2
+ import { A as r, U as o, W as n, o as l, r as c, p, C as S, d as u, L as m, Q as g, ag as d, f as b, D as C, af as T, ad as j, t as w, G as D, q as f, c as v, I as E, K as O, M as h, N as y, O as P, P as V, ae as k, R as x, x as A, S as B, e as G, y as H, w as M, B as N, H as R, T as q, v as I, b as K, z as U, J as z, V as F, ac as J, ab as L, a9 as Q, g as W, a0 as X, _ as Y, a1 as Z, j as _, a5 as $, aa, k as sa, l as ea, X as ta, m as ia, i as ra, Y as oa, n as na, a6 as la, $ as ca, a3 as pa, a2 as Sa, a8 as ua, Z as ma, s as ga, a4 as da, a7 as ba, a as Ca, u as Ta } from "./index-B9ATEmLe.js";
3
3
  const e = a.Howler;
4
4
  export {
5
5
  r as ArraySubject,
6
- n as Button,
7
- o as ButtonState,
8
- c as Canvas,
9
- l as Circle,
6
+ o as Button,
7
+ n as ButtonState,
8
+ l as Canvas,
9
+ c as Circle,
10
10
  p as Container,
11
- S as DOMContainer,
12
- m as DOMElement,
13
- u as DisplayObject,
14
- g as EVENTS,
15
- b as Easing,
16
- d as Ellipse,
17
- C as Graphics,
18
- j as Howl,
11
+ S as ControlsBase,
12
+ u as ControlsDirective,
13
+ m as DOMContainer,
14
+ g as DOMElement,
15
+ d as DisplayObject,
16
+ b as Drag,
17
+ C as Drop,
18
+ T as EVENTS,
19
+ j as Easing,
20
+ w as Ellipse,
21
+ D as GamepadControls,
22
+ f as Graphics,
23
+ v as Howl,
19
24
  e as Howler,
20
- T as Mesh,
21
- E as NineSliceSprite,
22
- O as ObjectSubject,
23
- f as ParticlesEmitter,
24
- v as RadialGradient,
25
- w as Rect,
26
- h as Scene,
27
- D as Sprite,
28
- P as Svg,
29
- k as Text,
30
- x as TilingSprite,
31
- y as Triangle,
32
- A as Utils,
33
- H as Video,
34
- M as Viewport,
35
- V as animatedSequence,
36
- B as animatedSignal,
37
- G as bootstrapCanvas,
38
- N as computed,
39
- R as cond,
40
- q as createComponent,
41
- U as currentSubscriptionsTracker,
42
- z as effect,
43
- I as h,
44
- J as isAnimatedSignal,
45
- K as isArraySubject,
46
- L as isComputed,
47
- Q as isElement,
48
- W as isObjectSubject,
49
- X as isObservable,
50
- Y as isPrimitive,
51
- Z as isSignal,
52
- _ as isTrigger,
53
- $ as loop,
54
- F as mount,
55
- aa as mountTracker,
56
- sa as on,
57
- ea as registerComponent,
58
- ta as signal,
59
- ia as tick,
60
- ra as trigger,
61
- na as useDefineProps,
62
- oa as useProps
25
+ E as Input,
26
+ O as KeyboardControls,
27
+ h as Mesh,
28
+ y as NineSliceSprite,
29
+ P as ObjectSubject,
30
+ V as ParticlesEmitter,
31
+ k as RadialGradient,
32
+ x as Rect,
33
+ A as Scene,
34
+ B as Scheduler,
35
+ G as Sound,
36
+ H as Sprite,
37
+ M as Svg,
38
+ N as Text,
39
+ R as TilingSprite,
40
+ q as Transition,
41
+ I as Triangle,
42
+ K as Utils,
43
+ U as Video,
44
+ z as Viewport,
45
+ F as ViewportFollow,
46
+ J as animatedSequence,
47
+ L as animatedSignal,
48
+ Q as bootstrapCanvas,
49
+ W as computed,
50
+ X as cond,
51
+ Y as createComponent,
52
+ Z as currentSubscriptionsTracker,
53
+ _ as effect,
54
+ $ as h,
55
+ aa as isAnimatedSignal,
56
+ sa as isArraySubject,
57
+ ea as isComputed,
58
+ ta as isElement,
59
+ ia as isObjectSubject,
60
+ ra as isObservable,
61
+ oa as isPrimitive,
62
+ na as isSignal,
63
+ la as isTrigger,
64
+ ca as loop,
65
+ pa as mount,
66
+ Sa as mountTracker,
67
+ ua as on,
68
+ ma as registerComponent,
69
+ ga as signal,
70
+ da as tick,
71
+ ba as trigger,
72
+ Ca as useDefineProps,
73
+ Ta as useProps
63
74
  };
64
75
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasengine",
3
- "version": "2.0.0-beta.34",
3
+ "version": "2.0.0-beta.35",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,6 +12,7 @@
12
12
  "@pixi/layout": "^3.0.2",
13
13
  "@signe/reactive": "^2.3.3",
14
14
  "howler": "^2.2.4",
15
+ "joypad.js": "^2.3.5",
15
16
  "pixi-filters": "^6.0.5",
16
17
  "pixi-viewport": "^6.0.3",
17
18
  "popmotion": "^11.0.5",
@@ -86,10 +86,65 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
86
86
 
87
87
  private currentAnimationContainer: Container | null = null;
88
88
 
89
+ /**
90
+ * Auto-detects image dimensions by loading the image and reading its natural size
91
+ * This is used when width/height are not explicitly provided in the spritesheet definition
92
+ *
93
+ * @param imagePath - Path to the image file
94
+ * @returns Object containing the detected width and height of the image
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const { width, height } = await sprite.detectImageDimensions('path/to/image.png');
99
+ * // width: 256, height: 128
100
+ * ```
101
+ */
102
+ private async detectImageDimensions(imagePath: string): Promise<{ width: number; height: number }> {
103
+ if (!imagePath || typeof imagePath !== 'string' || imagePath.trim() === '') {
104
+ throw new Error(`Invalid image path provided to detectImageDimensions: ${imagePath}`);
105
+ }
106
+
107
+ const texture = await Assets.load(imagePath);
108
+ return {
109
+ width: texture.width,
110
+ height: texture.height,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Creates textures from a spritesheet image by cutting it into frames
116
+ * Automatically detects image dimensions if width/height are not provided
117
+ *
118
+ * @param options - Texture options containing image path, dimensions, and frame configuration
119
+ * @returns A 2D array of textures organized by rows and columns
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // With explicit dimensions
124
+ * const textures = await sprite.createTextures({
125
+ * image: 'path/to/image.png',
126
+ * width: 256,
127
+ * height: 128,
128
+ * framesWidth: 4,
129
+ * framesHeight: 2,
130
+ * spriteWidth: 64,
131
+ * spriteHeight: 64
132
+ * });
133
+ *
134
+ * // Without dimensions (automatically detected)
135
+ * const textures = await sprite.createTextures({
136
+ * image: 'path/to/image.png',
137
+ * framesWidth: 4,
138
+ * framesHeight: 2,
139
+ * spriteWidth: 64,
140
+ * spriteHeight: 64
141
+ * });
142
+ * ```
143
+ */
89
144
  private async createTextures(
90
145
  options: Required<TextureOptionsMerging>
91
146
  ): Promise<Texture[][]> {
92
- const { width, height, framesHeight, framesWidth, image, offset } = options;
147
+ let { width, height, framesHeight, framesWidth, image, offset } = options;
93
148
 
94
149
  if (!image || typeof image !== 'string' || image.trim() === '') {
95
150
  console.warn('Invalid image path provided to createTextures:', image);
@@ -97,6 +152,17 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
97
152
  }
98
153
 
99
154
  const texture = await Assets.load(image);
155
+
156
+ // Auto-detect width and height from the image if not provided
157
+ if (!width || width <= 0) {
158
+ width = texture.width;
159
+ options.width = width;
160
+ }
161
+ if (!height || height <= 0) {
162
+ height = texture.height;
163
+ options.height = height;
164
+ }
165
+
100
166
  const spriteWidth = options.spriteWidth;
101
167
  const spriteHeight = options.spriteHeight;
102
168
  const frames: Texture[][] = [];
@@ -155,12 +221,30 @@ export class CanvasSprite extends DisplayObject(PixiSprite) {
155
221
  } as any;
156
222
  const {
157
223
  rectWidth,
158
- width = 0,
224
+ width: widthOption = 0,
159
225
  framesWidth = 1,
160
226
  rectHeight,
161
- height = 0,
227
+ height: heightOption = 0,
162
228
  framesHeight = 1,
229
+ image,
163
230
  } = optionsTextures;
231
+
232
+ // Auto-detect width and height from the image if not provided
233
+ let width = widthOption;
234
+ let height = heightOption;
235
+
236
+ if (image && ((!width || width <= 0) || (!height || height <= 0))) {
237
+ const dimensions = await this.detectImageDimensions(image);
238
+ if (!width || width <= 0) {
239
+ width = dimensions.width;
240
+ optionsTextures.width = width;
241
+ }
242
+ if (!height || height <= 0) {
243
+ height = dimensions.height;
244
+ optionsTextures.height = height;
245
+ }
246
+ }
247
+
164
248
  optionsTextures.spriteWidth = rectWidth ? rectWidth : width / framesWidth;
165
249
  optionsTextures.spriteHeight = rectHeight
166
250
  ? rectHeight
@@ -0,0 +1,182 @@
1
+ import { Directive, registerDirective } from "../engine/directive";
2
+ import { Element } from "../engine/reactive";
3
+ import { ControlsBase, Controls } from "./ControlsBase";
4
+ import { KeyboardControls } from "./KeyboardControls";
5
+ import { GamepadControls, GamepadConfig } from "./GamepadControls";
6
+
7
+ /**
8
+ * Controls directive that coordinates keyboard and gamepad input systems
9
+ *
10
+ * This directive automatically activates both keyboard and gamepad controls when available.
11
+ * The gamepad is automatically enabled if joypad.js is detected in the environment.
12
+ *
13
+ * Both systems share the same control configuration and can work simultaneously.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <Sprite
18
+ * image="path/to/image.png"
19
+ * controls={controlsConfig}
20
+ * x={x}
21
+ * y={y}
22
+ * />
23
+ * ```
24
+ */
25
+ export class ControlsDirective extends Directive {
26
+ private keyboardControls: KeyboardControls | null = null;
27
+ private gamepadControls: GamepadControls | null = null;
28
+
29
+ /**
30
+ * Initialize the controls directive
31
+ * Sets up keyboard and gamepad controls if available
32
+ */
33
+ onInit(element: Element) {
34
+ const value = element.props.controls?.value ?? element.props.controls;
35
+ if (!value) return;
36
+
37
+ // Initialize keyboard controls (always available)
38
+ this.keyboardControls = new KeyboardControls();
39
+ this.keyboardControls.setInputs(value as Controls);
40
+ this.keyboardControls.start();
41
+
42
+ // Initialize gamepad controls if gamepad config is present
43
+ // GamepadControls will handle joypad.js availability internally
44
+ const gamepadConfig = (value as Controls & { gamepad?: GamepadConfig }).gamepad;
45
+ if (gamepadConfig !== undefined && gamepadConfig.enabled !== false) {
46
+ this.gamepadControls = new GamepadControls();
47
+ this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
48
+ this.gamepadControls.start();
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Mount hook (no specific action needed)
54
+ */
55
+ onMount(element: Element) {}
56
+
57
+ /**
58
+ * Update controls configuration
59
+ * Updates both keyboard and gamepad controls
60
+ */
61
+ onUpdate(props: any) {
62
+ const value = props.controls?.value ?? props.controls;
63
+ if (!value) return;
64
+
65
+ if (this.keyboardControls) {
66
+ this.keyboardControls.setInputs(value as Controls);
67
+ }
68
+
69
+ if (this.gamepadControls) {
70
+ this.gamepadControls.setInputs(value as Controls & { gamepad?: GamepadConfig });
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Cleanup and destroy all control systems
76
+ */
77
+ onDestroy(element: Element) {
78
+ if (this.keyboardControls) {
79
+ this.keyboardControls.destroy();
80
+ this.keyboardControls = null;
81
+ }
82
+
83
+ if (this.gamepadControls) {
84
+ this.gamepadControls.destroy();
85
+ this.gamepadControls = null;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get a control by input name
91
+ * Delegates to keyboard controls (primary system)
92
+ *
93
+ * @param inputName - Name of the input/key
94
+ * @returns BoundKey if found, undefined otherwise
95
+ */
96
+ getControl(inputName: string) {
97
+ return this.keyboardControls?.getControl(inputName);
98
+ }
99
+
100
+ /**
101
+ * Get all bound controls
102
+ * Delegates to keyboard controls (primary system)
103
+ *
104
+ * @returns Object mapping input names to BoundKey objects
105
+ */
106
+ getControls() {
107
+ return this.keyboardControls?.getControls() || {};
108
+ }
109
+
110
+ /**
111
+ * Apply a control action programmatically
112
+ * Applies to both keyboard and gamepad if available
113
+ *
114
+ * @param controlName - Name of the control
115
+ * @param isDown - Whether the control is pressed (true) or released (false)
116
+ * @returns Promise that resolves when the action is complete
117
+ */
118
+ async applyControl(controlName: string | number, isDown?: boolean): Promise<void> {
119
+ if (this.keyboardControls) {
120
+ await this.keyboardControls.applyControl(controlName, isDown);
121
+ }
122
+ if (this.gamepadControls) {
123
+ await this.gamepadControls.applyControl(controlName, isDown);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Stop listening to inputs
129
+ * Stops both keyboard and gamepad input processing
130
+ */
131
+ stopInputs() {
132
+ if (this.keyboardControls) {
133
+ this.keyboardControls.stopInputs();
134
+ }
135
+ if (this.gamepadControls) {
136
+ this.gamepadControls.stopInputs();
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Resume listening to inputs
142
+ * Resumes both keyboard and gamepad input processing
143
+ */
144
+ listenInputs() {
145
+ if (this.keyboardControls) {
146
+ this.keyboardControls.listenInputs();
147
+ }
148
+ if (this.gamepadControls) {
149
+ this.gamepadControls.listenInputs();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Get the current controls configuration
155
+ * Returns keyboard controls options (both systems share the same config)
156
+ *
157
+ * @returns The controls options object
158
+ */
159
+ get options(): Controls {
160
+ return this.keyboardControls?.options || {};
161
+ }
162
+
163
+ /**
164
+ * Get the keyboard controls instance
165
+ *
166
+ * @returns KeyboardControls instance or null
167
+ */
168
+ get keyboard(): KeyboardControls | null {
169
+ return this.keyboardControls;
170
+ }
171
+
172
+ /**
173
+ * Get the gamepad controls instance
174
+ *
175
+ * @returns GamepadControls instance or null
176
+ */
177
+ get gamepad(): GamepadControls | null {
178
+ return this.gamepadControls;
179
+ }
180
+ }
181
+
182
+ registerDirective('controls', ControlsDirective);