like2d 2.9.0 → 2.10.0

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 (103) hide show
  1. package/README.md +21 -17
  2. package/dist/__benchmarks__/vector2.bench.d.ts +2 -0
  3. package/dist/__benchmarks__/vector2.bench.d.ts.map +1 -0
  4. package/dist/__benchmarks__/vector2.bench.js +74 -0
  5. package/dist/{core → audio}/audio.d.ts +12 -3
  6. package/dist/audio/audio.d.ts.map +1 -0
  7. package/dist/{core → audio}/audio.js +10 -2
  8. package/dist/audio/index.d.ts +2 -0
  9. package/dist/audio/index.d.ts.map +1 -0
  10. package/dist/audio/index.js +1 -0
  11. package/dist/engine.d.ts +12 -42
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +34 -76
  14. package/dist/{core/events.d.ts → events.d.ts} +27 -50
  15. package/dist/events.d.ts.map +1 -0
  16. package/dist/events.js +5 -0
  17. package/dist/gamecontrollerdb.txt +2222 -0
  18. package/dist/{core → graphics}/canvas.d.ts +18 -11
  19. package/dist/graphics/canvas.d.ts.map +1 -0
  20. package/dist/{core → graphics}/canvas.js +73 -58
  21. package/dist/{core/graphics.d.ts → graphics/drawing.d.ts} +25 -25
  22. package/dist/graphics/drawing.d.ts.map +1 -0
  23. package/dist/{core/graphics.js → graphics/drawing.js} +59 -52
  24. package/dist/graphics/index.d.ts +19 -0
  25. package/dist/graphics/index.d.ts.map +1 -0
  26. package/dist/graphics/index.js +13 -0
  27. package/dist/index.d.ts +3 -30
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +0 -21
  30. package/dist/input/gamepad-mapping.d.ts +134 -0
  31. package/dist/input/gamepad-mapping.d.ts.map +1 -0
  32. package/dist/input/gamepad-mapping.js +146 -0
  33. package/dist/input/gamepad.d.ts +74 -0
  34. package/dist/input/gamepad.d.ts.map +1 -0
  35. package/dist/input/gamepad.js +288 -0
  36. package/dist/input/index.d.ts +6 -0
  37. package/dist/input/index.d.ts.map +1 -0
  38. package/dist/input/index.js +1 -0
  39. package/dist/input/input.d.ts +76 -0
  40. package/dist/input/input.d.ts.map +1 -0
  41. package/dist/input/input.js +164 -0
  42. package/dist/input/keyboard.d.ts +15 -0
  43. package/dist/input/keyboard.d.ts.map +1 -0
  44. package/dist/{core → input}/keyboard.js +11 -21
  45. package/dist/input/mouse.d.ts +108 -0
  46. package/dist/input/mouse.d.ts.map +1 -0
  47. package/dist/input/mouse.js +241 -0
  48. package/dist/like.d.ts +80 -0
  49. package/dist/like.d.ts.map +1 -0
  50. package/dist/like.js +5 -0
  51. package/dist/math/index.d.ts +16 -0
  52. package/dist/math/index.d.ts.map +1 -1
  53. package/dist/math/index.js +16 -0
  54. package/dist/math/rect.d.ts +24 -27
  55. package/dist/math/rect.d.ts.map +1 -1
  56. package/dist/math/rect.js +47 -73
  57. package/dist/math/vector2.d.ts +87 -32
  58. package/dist/math/vector2.d.ts.map +1 -1
  59. package/dist/math/vector2.js +92 -110
  60. package/dist/prefab-scenes/index.d.ts +1 -0
  61. package/dist/prefab-scenes/index.d.ts.map +1 -1
  62. package/dist/prefab-scenes/index.js +1 -0
  63. package/dist/prefab-scenes/mapGamepad.d.ts +28 -0
  64. package/dist/prefab-scenes/mapGamepad.d.ts.map +1 -0
  65. package/dist/prefab-scenes/mapGamepad.js +181 -0
  66. package/dist/prefab-scenes/startScreen.d.ts +2 -2
  67. package/dist/prefab-scenes/startScreen.js +2 -2
  68. package/dist/scene.d.ts +2 -2
  69. package/dist/scene.d.ts.map +1 -1
  70. package/dist/timer/index.d.ts +2 -0
  71. package/dist/timer/index.d.ts.map +1 -0
  72. package/dist/timer/index.js +1 -0
  73. package/dist/timer/timer.d.ts +32 -0
  74. package/dist/timer/timer.d.ts.map +1 -0
  75. package/dist/{core → timer}/timer.js +20 -3
  76. package/package.json +22 -13
  77. package/dist/core/audio.d.ts.map +0 -1
  78. package/dist/core/canvas.d.ts.map +0 -1
  79. package/dist/core/events.d.ts.map +0 -1
  80. package/dist/core/events.js +0 -21
  81. package/dist/core/gamepad-mapping.d.ts +0 -58
  82. package/dist/core/gamepad-mapping.d.ts.map +0 -1
  83. package/dist/core/gamepad-mapping.js +0 -23
  84. package/dist/core/gamepad.d.ts +0 -37
  85. package/dist/core/gamepad.d.ts.map +0 -1
  86. package/dist/core/gamepad.js +0 -103
  87. package/dist/core/graphics.d.ts.map +0 -1
  88. package/dist/core/input-state.d.ts +0 -14
  89. package/dist/core/input-state.d.ts.map +0 -1
  90. package/dist/core/input-state.js +0 -50
  91. package/dist/core/input.d.ts +0 -40
  92. package/dist/core/input.d.ts.map +0 -1
  93. package/dist/core/input.js +0 -105
  94. package/dist/core/keyboard.d.ts +0 -15
  95. package/dist/core/keyboard.d.ts.map +0 -1
  96. package/dist/core/like.d.ts +0 -113
  97. package/dist/core/like.d.ts.map +0 -1
  98. package/dist/core/like.js +0 -9
  99. package/dist/core/mouse.d.ts +0 -52
  100. package/dist/core/mouse.d.ts.map +0 -1
  101. package/dist/core/mouse.js +0 -142
  102. package/dist/core/timer.d.ts +0 -15
  103. package/dist/core/timer.d.ts.map +0 -1
@@ -1,22 +1,24 @@
1
- import { EngineDispatch } from "../engine";
1
+ import { type Dispatcher } from "../events";
2
2
  import { Rectangle } from "../math/rect";
3
3
  import { type Vector2 } from "../math/vector2";
4
4
  export type CanvasModeOptions = {
5
5
  fullscreen: boolean;
6
6
  };
7
7
  export type CanvasSize = Vector2 | 'native';
8
- export declare class CanvasInternal {
9
- dispatch: EngineDispatch;
8
+ export declare class Canvas {
10
9
  /** The ultimately visible canvas in the browser */
11
- _displayCanvas: HTMLCanvasElement;
10
+ private displayCanvas;
11
+ private dispatch;
12
+ private abort;
12
13
  /** The canvas that we're drawing to with `like.gfx` functions.
13
- * If it's the same as _displayCanvas, we're in native mode.
14
+ * If it's the same as displayCanvas, we're in native mode.
14
15
  * Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
15
16
  */
16
- _renderCanvas: HTMLCanvasElement;
17
+ private renderCanvas;
17
18
  private resizeTimeoutId;
18
- private abort;
19
- constructor(dispatch: EngineDispatch);
19
+ constructor(
20
+ /** The ultimately visible canvas in the browser */
21
+ displayCanvas: HTMLCanvasElement, dispatch: Dispatcher<'resize'>, abort: AbortSignal);
20
22
  /** Get a unified canvas info object. */
21
23
  getMode(): {
22
24
  size: Vector2;
@@ -40,19 +42,24 @@ export declare class CanvasInternal {
40
42
  setMode(size: CanvasSize, flags?: Partial<CanvasModeOptions>): void;
41
43
  /** Get the apparent (in-game) canvas size. */
42
44
  getSize(): Vector2;
45
+ private dispatchResize;
43
46
  /** Sometimes you want a screen rect! */
44
47
  getRect(): Rectangle;
45
48
  /** Get the actual (physical) canvas size on screen. */
46
- _getDisplayPixelSize(): Vector2;
49
+ private getDisplayPixelSize;
47
50
  /** Are we fullscreen? */
48
51
  getFullscreen(): boolean;
49
52
  /** Set fullscreen. */
50
53
  setFullscreen(fullscreen: boolean): void;
54
+ /**
55
+ * Called internally by the engine before
56
+ * rendering a frame.
57
+ */
58
+ private preDraw;
51
59
  /** Called every frame by the engine after drawing */
52
- _present(): void;
60
+ private postDraw;
53
61
  /** @returns if size was changed. */
54
62
  static setCanvasElemSize(canvas: HTMLCanvasElement, newSize: Vector2): boolean;
55
63
  static getCanvasElemSize(canvas: HTMLCanvasElement): Vector2;
56
- _dispose(): void;
57
64
  }
58
65
  //# sourceMappingURL=canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/graphics/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAQ,SAAS,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C,qBAAa,MAAM;IAUX,mDAAmD;IACnD,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,KAAK;IAZjB;;;MAGE;IACF,OAAO,CAAC,YAAY,CAAoB;IAExC,OAAO,CAAC,eAAe,CAAU;;IAG7B,mDAAmD;IAC3C,aAAa,EAAE,iBAAiB,EAChC,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,EAC9B,KAAK,EAAE,WAAW;IAoE9B,wCAAwC;IACxC,OAAO,IAAI;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,iBAAiB,CAAA;KAAE;IAStD;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,GAAE,OAAO,CAAC,iBAAiB,CAAM;IA6BhE,8CAA8C;IAC9C,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,cAAc;IAWtB,wCAAwC;IACxC,OAAO,IAAI,SAAS;IAIpB,uDAAuD;IACvD,OAAO,CAAC,mBAAmB;IAO3B,yBAAyB;IACzB,aAAa,IAAI,OAAO;IAIxB,sBAAsB;IACtB,aAAa,CAAC,UAAU,EAAE,OAAO;IAWjC;;;OAGG;IACH,OAAO,CAAC,OAAO;IAmBf,qDAAqD;IACrD,OAAO,CAAC,QAAQ;IA2BhB,qCAAqC;IACrC,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;IAO9E,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO;CAG/D"}
@@ -1,25 +1,32 @@
1
1
  import { Rect } from "../math/rect";
2
2
  import { Vec2 } from "../math/vector2";
3
- export class CanvasInternal {
4
- constructor(dispatch) {
3
+ export class Canvas {
4
+ constructor(
5
+ /** The ultimately visible canvas in the browser */
6
+ displayCanvas, dispatch, abort) {
7
+ Object.defineProperty(this, "displayCanvas", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: displayCanvas
12
+ });
5
13
  Object.defineProperty(this, "dispatch", {
6
14
  enumerable: true,
7
15
  configurable: true,
8
16
  writable: true,
9
17
  value: dispatch
10
18
  });
11
- /** The ultimately visible canvas in the browser */
12
- Object.defineProperty(this, "_displayCanvas", {
19
+ Object.defineProperty(this, "abort", {
13
20
  enumerable: true,
14
21
  configurable: true,
15
22
  writable: true,
16
- value: void 0
23
+ value: abort
17
24
  });
18
25
  /** The canvas that we're drawing to with `like.gfx` functions.
19
- * If it's the same as _displayCanvas, we're in native mode.
26
+ * If it's the same as displayCanvas, we're in native mode.
20
27
  * Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
21
28
  */
22
- Object.defineProperty(this, "_renderCanvas", {
29
+ Object.defineProperty(this, "renderCanvas", {
23
30
  enumerable: true,
24
31
  configurable: true,
25
32
  writable: true,
@@ -31,27 +38,20 @@ export class CanvasInternal {
31
38
  writable: true,
32
39
  value: 0
33
40
  });
34
- Object.defineProperty(this, "abort", {
35
- enumerable: true,
36
- configurable: true,
37
- writable: true,
38
- value: new AbortController()
39
- });
40
- this._displayCanvas = document.createElement('canvas');
41
- this._displayCanvas.tabIndex = 0;
42
- this._displayCanvas.style.width = '100%';
43
- this._displayCanvas.style.height = '100%';
44
- this._renderCanvas = this._displayCanvas;
41
+ displayCanvas.tabIndex = 0;
42
+ displayCanvas.style.width = '100%';
43
+ displayCanvas.style.height = '100%';
44
+ this.renderCanvas = this.displayCanvas;
45
45
  this.setMode('native');
46
46
  /** Only the canvas can really transform the mouse to the game size.
47
47
  * This hack sends an event for the mouse module to listen to.
48
48
  */
49
- this._displayCanvas.addEventListener('mousemove', (ev) => {
49
+ this.displayCanvas.addEventListener('mousemove', (ev) => {
50
50
  let pos;
51
51
  let delta;
52
52
  const rawPos = [ev.offsetX, ev.offsetY];
53
53
  const rawDelta = [ev.movementX, ev.movementY];
54
- if (this._renderCanvas == this._displayCanvas) {
54
+ if (this.renderCanvas == this.displayCanvas) {
55
55
  /* Native mode. */
56
56
  pos = Vec2.mul(rawPos, window.devicePixelRatio ?? 1);
57
57
  delta = Vec2.mul(rawDelta, window.devicePixelRatio ?? 1);
@@ -61,8 +61,8 @@ export class CanvasInternal {
61
61
  * which preserves aspect ratio.
62
62
  */
63
63
  const csize = [
64
- this._displayCanvas.clientWidth,
65
- this._displayCanvas.clientHeight
64
+ this.displayCanvas.clientWidth,
65
+ this.displayCanvas.clientHeight
66
66
  ];
67
67
  /* Scale of both dimensions */
68
68
  const scale = calcAspectFriendlyScale(this.getSize(), csize);
@@ -75,14 +75,15 @@ export class CanvasInternal {
75
75
  return;
76
76
  }
77
77
  }
78
- this._displayCanvas.dispatchEvent(new CustomEvent('like:mousemoved', {
78
+ this.displayCanvas.dispatchEvent(new CustomEvent('like:mousemoved', {
79
79
  detail: {
80
80
  pos,
81
81
  delta,
82
- renderSize: this.getSize(),
83
82
  }
84
83
  }));
85
- }, { signal: this.abort.signal });
84
+ }, { signal: this.abort });
85
+ this.displayCanvas.addEventListener("like:preDraw", this.preDraw.bind(this), { signal: this.abort });
86
+ this.displayCanvas.addEventListener("like:postDraw", this.postDraw.bind(this), { signal: this.abort });
86
87
  }
87
88
  /** Get a unified canvas info object. */
88
89
  getMode() {
@@ -110,24 +111,24 @@ export class CanvasInternal {
110
111
  */
111
112
  setMode(size, flags = {}) {
112
113
  // set up sizing / render target
113
- const prevRenderCanvas = this._renderCanvas;
114
+ const prevRenderCanvas = this.renderCanvas;
114
115
  if (size == 'native') {
115
- this._displayCanvas.style.objectFit = 'fill';
116
- this._renderCanvas = this._displayCanvas;
116
+ this.displayCanvas.style.objectFit = 'fill';
117
+ this.renderCanvas = this.displayCanvas;
117
118
  }
118
119
  else {
119
- this._displayCanvas.style.objectFit = 'contain';
120
- this._renderCanvas = document.createElement('canvas');
121
- const changed = CanvasInternal.setCanvasElemSize(this._renderCanvas, size);
120
+ this.displayCanvas.style.objectFit = 'contain';
121
+ this.renderCanvas = document.createElement('canvas');
122
+ const changed = Canvas.setCanvasElemSize(this.renderCanvas, size);
122
123
  if (changed) {
123
- this.dispatch('resize', [size]);
124
+ this.dispatchResize(size);
124
125
  }
125
126
  }
126
- if (prevRenderCanvas != this._renderCanvas) {
127
- this._displayCanvas.dispatchEvent(new CustomEvent('like:updateRenderTarget', {
127
+ if (prevRenderCanvas != this.renderCanvas) {
128
+ this.displayCanvas.dispatchEvent(new CustomEvent("like:updateRenderTarget", {
128
129
  detail: {
129
- target: this._renderCanvas,
130
- }
130
+ target: this.renderCanvas,
131
+ },
131
132
  }));
132
133
  }
133
134
  if ('fullscreen' in flags) {
@@ -136,56 +137,73 @@ export class CanvasInternal {
136
137
  }
137
138
  /** Get the apparent (in-game) canvas size. */
138
139
  getSize() {
139
- return [this._renderCanvas.width, this._renderCanvas.height];
140
+ return [this.renderCanvas.width, this.renderCanvas.height];
141
+ }
142
+ dispatchResize(size) {
143
+ this.displayCanvas.dispatchEvent(new CustomEvent("like:resizeCanvas", {
144
+ detail: {
145
+ size,
146
+ },
147
+ }));
148
+ this.dispatch("resize", [size]);
140
149
  }
141
150
  /** Sometimes you want a screen rect! */
142
151
  getRect() {
143
152
  return [0, 0, ...this.getSize()];
144
153
  }
145
154
  /** Get the actual (physical) canvas size on screen. */
146
- _getDisplayPixelSize() {
147
- return Vec2.round(Vec2.mul([this._displayCanvas.clientWidth, this._displayCanvas.clientHeight], window.devicePixelRatio ?? 1));
155
+ getDisplayPixelSize() {
156
+ return Vec2.round(Vec2.mul([this.displayCanvas.clientWidth, this.displayCanvas.clientHeight], window.devicePixelRatio ?? 1));
148
157
  }
149
158
  /** Are we fullscreen? */
150
159
  getFullscreen() {
151
- return this._displayCanvas === document.fullscreenElement;
160
+ return this.displayCanvas === document.fullscreenElement;
152
161
  }
153
162
  /** Set fullscreen. */
154
163
  setFullscreen(fullscreen) {
155
164
  if (fullscreen) {
156
- this._displayCanvas.requestFullscreen();
157
- this._displayCanvas.focus();
165
+ this.displayCanvas.requestFullscreen();
166
+ this.displayCanvas.focus();
158
167
  }
159
168
  else {
160
- document.exitFullscreen();
169
+ if (this.getFullscreen()) {
170
+ document.exitFullscreen();
171
+ }
161
172
  }
162
173
  }
163
- /** Called every frame by the engine after drawing */
164
- _present() {
165
- if (this._renderCanvas == this._displayCanvas) {
166
- const realSize = this._getDisplayPixelSize();
167
- if ((realSize[0] != this._displayCanvas.width ||
168
- realSize[1] != this._displayCanvas.height) &&
174
+ /**
175
+ * Called internally by the engine before
176
+ * rendering a frame.
177
+ */
178
+ preDraw() {
179
+ if (this.renderCanvas == this.displayCanvas) {
180
+ const realSize = this.getDisplayPixelSize();
181
+ if ((realSize[0] != this.displayCanvas.width ||
182
+ realSize[1] != this.displayCanvas.height) &&
169
183
  !this.resizeTimeoutId) {
170
184
  /** In native scaling mode, zooming and resizing the window cause us
171
185
  * to set canvas width and height every frame, which could cause
172
186
  * tons of canvas bitmap reallocations. So wait 1/4 second..
173
187
  */
174
- CanvasInternal.setCanvasElemSize(this._displayCanvas, realSize);
175
- this.dispatch('resize', [realSize]);
188
+ Canvas.setCanvasElemSize(this.displayCanvas, realSize);
189
+ this.dispatchResize(realSize);
176
190
  this.resizeTimeoutId = setTimeout(() => { this.resizeTimeoutId = 0; }, 250);
177
191
  }
178
192
  }
179
- else if (this._renderCanvas != this._displayCanvas) {
193
+ this.renderCanvas.getContext('2d').resetTransform();
194
+ }
195
+ /** Called every frame by the engine after drawing */
196
+ postDraw() {
197
+ if (this.renderCanvas != this.displayCanvas) {
180
198
  /* We're in pixelart mode,
181
199
  * so set output canvas size to an ideal integer scale.
182
200
  * No debounce: changes to integer ratio are infrequent.
183
201
  */
184
- CanvasInternal.setCanvasElemSize(this._displayCanvas, Vec2.mul(this.getSize(), Math.round(calcAspectFriendlyScale(this.getSize(), this._getDisplayPixelSize()))));
202
+ Canvas.setCanvasElemSize(this.displayCanvas, Vec2.mul(this.getSize(), Math.round(calcAspectFriendlyScale(this.getSize(), this.getDisplayPixelSize()))));
185
203
  // Copy the internal canvas to the visible one.
186
- const ctx = this._displayCanvas.getContext('2d');
204
+ const ctx = this.displayCanvas.getContext('2d');
187
205
  ctx.imageSmoothingEnabled = false;
188
- ctx.drawImage(this._renderCanvas, 0, 0, this._renderCanvas.width, this._renderCanvas.height, 0, 0, this._displayCanvas.width, this._displayCanvas.height);
206
+ ctx.drawImage(this.renderCanvas, 0, 0, this.renderCanvas.width, this.renderCanvas.height, 0, 0, this.displayCanvas.width, this.displayCanvas.height);
189
207
  }
190
208
  }
191
209
  /** @returns if size was changed. */
@@ -199,9 +217,6 @@ export class CanvasInternal {
199
217
  static getCanvasElemSize(canvas) {
200
218
  return [canvas.width, canvas.height];
201
219
  }
202
- _dispose() {
203
- this.abort.abort();
204
- }
205
220
  }
206
221
  /** How much could this image be scaled, preserving aspect? */
207
222
  function calcAspectFriendlyScale(imageSize, containerSize) {
@@ -26,9 +26,9 @@
26
26
  * - Y increases down
27
27
  * - Angles in radians, 0 is right, positive is clockwise
28
28
  */
29
- import type { Vector2 } from "../math/vector2";
29
+ import { type Vector2 } from "../math/vector2";
30
30
  import type { Rectangle } from "../math/rect";
31
- type DrawMode = "fill" | "line";
31
+ export type DrawMode = "fill" | "line";
32
32
  /**
33
33
  * - RGBA array with values 0-1: `[r, g, b, a]`
34
34
  * - Alpha defaults to 1 if omitted
@@ -49,7 +49,7 @@ export type DrawProps = ShapeProps & {
49
49
  };
50
50
  export type PrintProps = {
51
51
  font?: string;
52
- limit?: number;
52
+ width?: number;
53
53
  align?: CanvasTextAlign;
54
54
  };
55
55
  export declare class ImageHandle {
@@ -64,14 +64,18 @@ export declare class ImageHandle {
64
64
  getElement(): HTMLImageElement | null;
65
65
  }
66
66
  /**
67
- * A ready-made pure module for drawing to non-LIKE canvases.
67
+ * All of these methods exist on `like.gfx`, but with `ctx`
68
+ * bound to the first arg.
68
69
  *
69
- * Acts as the core of the graphics system.
70
+ * Acts as the core of the graphics system, but can be used separately.
71
+ *
72
+ * ```ts
73
+ * import { draw } from "like/graphics"
74
+ * draw.clear(my2dContext, "red");
75
+ * ```
70
76
  *
71
- * import { pure as gfx } from "like/internal/graphics"
72
- * gfx.clear(my2dContext, "red");
73
77
  */
74
- export declare const pure: {
78
+ export declare const draw: {
75
79
  /**
76
80
  * Clears the canvas with a solid color.
77
81
  * @param ctx Canvas context.
@@ -94,12 +98,11 @@ export declare const pure: {
94
98
  * @param color Fill or stroke color.
95
99
  * @param position Center position.
96
100
  * @param radii Radius (number) or [rx, ry] for ellipse.
97
- * @param props Optional angle, arc, or stroke properties.
101
+ * @param props Optional arc, center, and stroke properties. Center is true by default.
98
102
  */
99
103
  circle(ctx: CanvasRenderingContext2D, mode: DrawMode, color: Color, position: Vector2, radii: number | Vector2, props?: ShapeProps & {
100
- angle?: number;
101
104
  arc?: [number, number];
102
- center: boolean;
105
+ center?: boolean;
103
106
  }): void;
104
107
  /**
105
108
  * Draws connected line segments.
@@ -110,12 +113,20 @@ export declare const pure: {
110
113
  */
111
114
  line(ctx: CanvasRenderingContext2D, color: Color, points: Vector2[], props?: ShapeProps): void;
112
115
  /**
113
- * Draws text at position.
116
+ * Draws text at a position.
117
+ *
118
+ * Keep in mind: if you set `align` without `width` in your props,
119
+ * nothing will happen -- you'll get left-aligned text.
120
+ *
121
+ * Align works browser-style: if you align center, your text draws
122
+ * to the left and right of its position. If you align right, your position
123
+ * becomes the upper-right corner of the text.
124
+ *
114
125
  * @param ctx Canvas context.
115
126
  * @param color Fill color.
116
127
  * @param text Text string.
117
128
  * @param position Top-left position.
118
- * @param props Optional font, text limit, or alignment.
129
+ * @param props {@link PrintProps} Optional font, text limit, or alignment.
119
130
  */
120
131
  print(ctx: CanvasRenderingContext2D, color: Color, text: string, position: Vector2, props?: PrintProps): void;
121
132
  /**
@@ -189,15 +200,4 @@ export declare const pure: {
189
200
  */
190
201
  scale(ctx: CanvasRenderingContext2D, factor: number | Vector2): void;
191
202
  };
192
- type Bind<F> = F extends (ctx: CanvasRenderingContext2D, ...args: infer A) => infer R ? (...args: A) => R : never;
193
- /**
194
- * A graphics object with a canvas already attatched to it.
195
- * Calling its methods will draw to the render canvas.
196
- * See {@link graphics} for more info.
197
- */
198
- export type BoundGraphics = {
199
- [K in keyof typeof pure]: Bind<(typeof pure)[K]>;
200
- };
201
- export declare function bindGraphics(ctx: CanvasRenderingContext2D): BoundGraphics;
202
- export {};
203
- //# sourceMappingURL=graphics.d.ts.map
203
+ //# sourceMappingURL=drawing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawing.d.ts","sourceRoot":"","sources":["../../src/graphics/drawing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAQ,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC;;;;GAIG;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG;IACnC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,MAAM;IAiBxB,OAAO,IAAI,OAAO;IAIlB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,UAAU,IAAI,gBAAgB,GAAG,IAAI;CAGtC;AAgDD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI;IACf;;;;OAIG;eACQ,wBAAwB,UAAS,KAAK,GAAkB,IAAI;IAKvE;;;;;;;OAOG;mBAEI,wBAAwB,QACvB,QAAQ,SACP,KAAK,QACN,SAAS,UACP,UAAU,GACjB,IAAI;IAYP;;;;;;;;OAQG;gBAEI,wBAAwB,QACvB,QAAQ,SACP,KAAK,YACF,OAAO,SACV,MAAM,GAAG,OAAO,UACf,UAAU,GAAG;QACnB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACvB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,GACA,IAAI;IA4BP;;;;;;OAMG;cAEI,wBAAwB,SACtB,KAAK,UACJ,OAAO,EAAE,UACT,UAAU,GACjB,IAAI;IAWP;;;;;;;;;;;;;;;OAeG;eAEI,wBAAwB,SACtB,KAAK,QACN,MAAM,YACF,OAAO,UACT,UAAU,GACjB,IAAI;IAoBP;;;;;;;;;;OAUG;cAEI,wBAAwB,UACrB,WAAW,YACT,OAAO,UACT,SAAS,GAChB,IAAI;IAuBP;;;;;;OAMG;mBACY,wBAAwB,QAAQ,MAAM,GAAG,WAAW;IAInE;;;;OAIG;cACO,wBAAwB,SAAS,SAAS,GAAG,IAAI;IAW3D;;;;;;;OAOG;iBAEI,wBAAwB,QACvB,QAAQ,SACP,KAAK,UACJ,OAAO,EAAE,UACT,UAAU,GACjB,IAAI;IAkBP;;;;;OAKG;gBACS,wBAAwB,SAAS,KAAK,OAAO,OAAO,EAAE,GAAG,IAAI;IAKzE;;;OAGG;cACO,wBAAwB,GAAG,IAAI;IAIzC;;;OAGG;aACM,wBAAwB,GAAG,IAAI;IAIxC;;;;OAIG;mBACY,wBAAwB,UAAU,OAAO,GAAG,IAAI;IAK/D;;;;OAIG;gBACS,wBAAwB,SAAS,MAAM,GAAG,IAAI;IAI1D;;;;OAIG;eACQ,wBAAwB,UAAU,MAAM,GAAG,OAAO,GAAG,IAAI;CAIrE,CAAC"}
@@ -26,6 +26,7 @@
26
26
  * - Y increases down
27
27
  * - Angles in radians, 0 is right, positive is clockwise
28
28
  */
29
+ import { Vec2 } from "../math/vector2";
29
30
  export class ImageHandle {
30
31
  constructor(path) {
31
32
  Object.defineProperty(this, "path", {
@@ -94,15 +95,40 @@ function setStrokeProps(ctx, props) {
94
95
  ctx.lineJoin = props?.lineJoin ?? "miter";
95
96
  ctx.miterLimit = props?.miterLimit ?? 10;
96
97
  }
98
+ function wrapText(ctx, text, maxWidth) {
99
+ const words = text.split(" ");
100
+ const [first, ...rest] = words;
101
+ const lines = [];
102
+ let current = first ?? "";
103
+ rest.forEach((word) => {
104
+ if (ctx.measureText(current + " " + word).width < maxWidth) {
105
+ current += " " + word;
106
+ }
107
+ else {
108
+ lines.push(current);
109
+ current = word;
110
+ }
111
+ });
112
+ lines.push(current);
113
+ return lines;
114
+ }
115
+ function getFontHeight(ctx) {
116
+ const match = ctx.font.match(/(\d+)px/);
117
+ return match ? parseInt(match[1]) : 16;
118
+ }
97
119
  /**
98
- * A ready-made pure module for drawing to non-LIKE canvases.
120
+ * All of these methods exist on `like.gfx`, but with `ctx`
121
+ * bound to the first arg.
122
+ *
123
+ * Acts as the core of the graphics system, but can be used separately.
99
124
  *
100
- * Acts as the core of the graphics system.
125
+ * ```ts
126
+ * import { draw } from "like/graphics"
127
+ * draw.clear(my2dContext, "red");
128
+ * ```
101
129
  *
102
- * import { pure as gfx } from "like/internal/graphics"
103
- * gfx.clear(my2dContext, "red");
104
130
  */
105
- export const pure = {
131
+ export const draw = {
106
132
  /**
107
133
  * Clears the canvas with a solid color.
108
134
  * @param ctx Canvas context.
@@ -139,20 +165,23 @@ export const pure = {
139
165
  * @param color Fill or stroke color.
140
166
  * @param position Center position.
141
167
  * @param radii Radius (number) or [rx, ry] for ellipse.
142
- * @param props Optional angle, arc, or stroke properties.
168
+ * @param props Optional arc, center, and stroke properties. Center is true by default.
143
169
  */
144
170
  circle(ctx, mode, color, position, radii, props) {
145
- const [x, y] = position;
171
+ const center = (props && 'center' in props) ? props.center : true;
146
172
  const c = applyColor(color);
147
- const [rx, ry] = typeof radii === "number" ? [radii, radii] : radii;
173
+ const size = typeof radii === "number" ? [radii, radii] : radii;
148
174
  const [startAngle, endAngle] = props?.arc ?? [0, Math.PI * 2];
149
- const rotation = props?.angle ?? 0;
175
+ if (!center) {
176
+ position = Vec2.add(position, size);
177
+ }
150
178
  ctx.save();
151
- ctx.translate(x, y);
152
- ctx.scale(rx, ry);
153
- ctx.rotate(rotation);
179
+ ctx.translate(...position);
180
+ ctx.scale(...size);
154
181
  ctx.beginPath();
155
182
  ctx.arc(0, 0, 1, startAngle, endAngle);
183
+ if (mode == 'fill')
184
+ ctx.lineTo(0, 0);
156
185
  ctx.closePath();
157
186
  ctx.restore();
158
187
  if (mode === "fill") {
@@ -184,30 +213,36 @@ export const pure = {
184
213
  ctx.stroke();
185
214
  },
186
215
  /**
187
- * Draws text at position.
216
+ * Draws text at a position.
217
+ *
218
+ * Keep in mind: if you set `align` without `width` in your props,
219
+ * nothing will happen -- you'll get left-aligned text.
220
+ *
221
+ * Align works browser-style: if you align center, your text draws
222
+ * to the left and right of its position. If you align right, your position
223
+ * becomes the upper-right corner of the text.
224
+ *
188
225
  * @param ctx Canvas context.
189
226
  * @param color Fill color.
190
227
  * @param text Text string.
191
228
  * @param position Top-left position.
192
- * @param props Optional font, text limit, or alignment.
229
+ * @param props {@link PrintProps} Optional font, text limit, or alignment.
193
230
  */
194
231
  print(ctx, color, text, position, props) {
195
232
  const [x, y] = position;
196
- const { font = "16px sans-serif", limit, align = "left" } = props ?? {};
233
+ const { font = "16px sans-serif" } = props ?? {};
197
234
  ctx.fillStyle = parseColor(color);
198
235
  ctx.font = font;
199
- if (limit !== undefined) {
200
- const lines = wrapText(ctx, text, limit);
236
+ ctx.textAlign = props?.align ?? "left";
237
+ if (props && 'width' in props) {
238
+ const { width } = props;
239
+ const lines = wrapText(ctx, text, width);
201
240
  const lineHeight = getFontHeight(ctx);
241
+ ctx.textBaseline = "top";
202
242
  lines.forEach((line, i) => {
203
- const lineWidth = ctx.measureText(line).width;
204
- const drawX = align === "center"
205
- ? x + (limit - lineWidth) / 2
206
- : align === "right"
207
- ? x + limit - lineWidth
208
- : x;
209
- ctx.fillText(line, drawX, y + i * lineHeight);
243
+ ctx.fillText(line, x, y + i * lineHeight, width);
210
244
  });
245
+ ctx.textBaseline = "alphabetic";
211
246
  }
212
247
  else {
213
248
  ctx.fillText(text, x, y);
@@ -351,31 +386,3 @@ export const pure = {
351
386
  ctx.scale(sx, sy);
352
387
  },
353
388
  };
354
- function wrapText(ctx, text, maxWidth) {
355
- const words = text.split(" ");
356
- const [first, ...rest] = words;
357
- const lines = [];
358
- let current = first ?? "";
359
- rest.forEach((word) => {
360
- if (ctx.measureText(current + " " + word).width < maxWidth) {
361
- current += " " + word;
362
- }
363
- else {
364
- lines.push(current);
365
- current = word;
366
- }
367
- });
368
- lines.push(current);
369
- return lines;
370
- }
371
- function getFontHeight(ctx) {
372
- const match = ctx.font.match(/(\d+)px/);
373
- return match ? parseInt(match[1]) : 16;
374
- }
375
- export function bindGraphics(ctx) {
376
- const bound = {};
377
- for (const [name, fn] of Object.entries(pure)) {
378
- bound[name] = (...args) => fn(ctx, ...args);
379
- }
380
- return bound;
381
- }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @module graphics
3
+ * @description a reduced-state, Love2D-like wrapper around browser canvas
4
+ */
5
+ import { draw } from "./drawing";
6
+ export type { Color, DrawMode, ShapeProps, DrawProps, PrintProps, ImageHandle, } from "./drawing";
7
+ export { draw } from "./drawing";
8
+ export type { CanvasModeOptions, CanvasSize, Canvas, } from "./canvas";
9
+ type Bind<F> = F extends (ctx: CanvasRenderingContext2D, ...args: infer A) => infer R ? (...args: A) => R : never;
10
+ /**
11
+ * A graphics object with a canvas already attatched to it.
12
+ * Calling its methods will draw to the render canvas.
13
+ * See {@link graphics} for more info.
14
+ */
15
+ export type BoundGraphics = {
16
+ [K in keyof typeof draw]: Bind<(typeof draw)[K]>;
17
+ };
18
+ export declare function bindGraphics(ctx: CanvasRenderingContext2D): BoundGraphics;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/graphics/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,YAAY,EACV,KAAK,EACL,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,YAAY,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,GACP,MAAM,UAAU,CAAC;AAElB,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CACvB,GAAG,EAAE,wBAAwB,EAC7B,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,MAAM,CAAC,GACR,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,GACjB,KAAK,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;KACzB,CAAC,IAAI,MAAM,OAAO,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;CACjD,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,wBAAwB,GAAG,aAAa,CAMzE"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module graphics
3
+ * @description a reduced-state, Love2D-like wrapper around browser canvas
4
+ */
5
+ import { draw } from "./drawing";
6
+ export { draw } from "./drawing";
7
+ export function bindGraphics(ctx) {
8
+ const bound = {};
9
+ for (const [name, fn] of Object.entries(draw)) {
10
+ bound[name] = (...args) => fn(ctx, ...args);
11
+ }
12
+ return bound;
13
+ }
package/dist/index.d.ts CHANGED
@@ -1,19 +1,10 @@
1
1
  /**
2
2
  * @module like2d
3
3
  * @description A cozy web-native 2D game framework.
4
- *
5
- * See main like/README.md file an for overview of Like2D.
6
4
  */
7
- import type { LikeEvent } from './core/events';
8
- import type { Like } from './core/like';
9
- export type { Like } from './core/like';
10
- export type { LikeEvent, EventType, EventMap } from './core/events';
11
- export type { CanvasSize, CanvasModeOptions as CanvasModeFlags } from './core/canvas';
12
- export type { Color, ShapeProps, DrawProps, PrintProps } from './core/graphics';
13
- export { ImageHandle } from './core/graphics';
14
- export type { AudioSource, AudioSourceOptions } from './core/audio';
15
- export { type LikeButton } from './core/gamepad';
16
- export type TopLevelEventHandler = (event: LikeEvent) => void;
5
+ import type { Like } from './like';
6
+ export type { Like, TopLevelEventHandler } from './like';
7
+ export type { LikeEvent, EventType, EventMap } from './events';
17
8
  /**
18
9
  * Create a new Like2D game instance attached to a DOM container.
19
10
  *
@@ -21,26 +12,8 @@ export type TopLevelEventHandler = (event: LikeEvent) => void;
21
12
  * initializes all subsystems (graphics, audio, input), and returns an object
22
13
  * where you can assign game callbacks.
23
14
  *
24
- * ### How to bind callbacks
25
- *
26
- * ```ts
27
- * export const like = createLike();
28
- *
29
- * like.start();
30
- *
31
- * like.draw = () => { like.gfx.clear('yellow') }
32
- *
33
- * like.update = function (dt: number) {
34
- * if (dt === Math.random()) {
35
- * console.log("You just won the Powerball!")
36
- * }
37
- * }
38
- * ```
39
- *
40
15
  * @param container - The HTML element to attach the game canvas to.
41
- * Must be in the DOM.
42
16
  * @returns A {@link Like} instance ready for callback assignment
43
- *
44
17
  */
45
18
  export declare function createLike(container: HTMLElement): Like;
46
19
  //# sourceMappingURL=index.d.ts.map