like2d 2.11.1 → 2.12.1
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/README.md +33 -5
- package/assets/logo-banner-optimized.svg +15 -27
- package/assets/logo-banner.svg +76 -132
- package/assets/logo-icon.svg +33 -23
- package/assets/logo.svg +78 -123
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +34 -12
- package/dist/events.d.ts +0 -3
- package/dist/graphics/canvas.d.ts +15 -7
- package/dist/graphics/canvas.js +64 -68
- package/dist/graphics/graphics.d.ts +55 -40
- package/dist/graphics/graphics.js +100 -72
- package/dist/graphics/index.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -2
- package/dist/input/controllerdb.json +1 -1
- package/dist/input/gamepad-mapping.js +2 -1
- package/dist/input/gamepad.d.ts +1 -1
- package/dist/input/gamepad.js +1 -1
- package/dist/input/input.d.ts +2 -2
- package/dist/input/input.js +2 -2
- package/dist/input/mouse.d.ts +7 -7
- package/dist/input/mouse.js +7 -7
- package/dist/like.d.ts +48 -10
- package/dist/math/rect.d.ts +1 -0
- package/dist/math/rect.js +1 -0
- package/dist/math/vector2.d.ts +4 -1
- package/dist/math/vector2.js +3 -0
- package/dist/prefab-scenes/mapGamepad.d.ts +2 -3
- package/dist/prefab-scenes/mapGamepad.js +17 -23
- package/dist/prefab-scenes/startScreen.d.ts +2 -3
- package/dist/prefab-scenes/startScreen.js +41 -12
- package/dist/scene.d.ts +49 -7
- package/package.json +3 -2
- package/dist/__benchmarks__/vector2.bench.d.ts +0 -2
- package/dist/__benchmarks__/vector2.bench.d.ts.map +0 -1
- package/dist/__benchmarks__/vector2.bench.js +0 -74
- package/dist/audio/audio.d.ts.map +0 -1
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/engine.d.ts.map +0 -1
- package/dist/events.d.ts.map +0 -1
- package/dist/graphics/canvas.d.ts.map +0 -1
- package/dist/graphics/graphics.d.ts.map +0 -1
- package/dist/graphics/image.d.ts.map +0 -1
- package/dist/graphics/index.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/input/gamepad-mapping.d.ts.map +0 -1
- package/dist/input/gamepad.d.ts.map +0 -1
- package/dist/input/index.d.ts.map +0 -1
- package/dist/input/input.d.ts.map +0 -1
- package/dist/input/keyboard.d.ts.map +0 -1
- package/dist/input/mouse.d.ts.map +0 -1
- package/dist/like.d.ts.map +0 -1
- package/dist/math/index.d.ts.map +0 -1
- package/dist/math/rect.d.ts.map +0 -1
- package/dist/math/vector2.d.ts.map +0 -1
- package/dist/prefab-scenes/index.d.ts.map +0 -1
- package/dist/prefab-scenes/mapGamepad.d.ts.map +0 -1
- package/dist/prefab-scenes/startScreen.d.ts.map +0 -1
- package/dist/scene.d.ts.map +0 -1
- package/dist/timer/index.d.ts.map +0 -1
- package/dist/timer/timer.d.ts.map +0 -1
package/assets/logo.svg
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
<svg
|
|
5
5
|
width="100mm"
|
|
6
|
-
height="
|
|
7
|
-
viewBox="0 0 100
|
|
6
|
+
height="100mm"
|
|
7
|
+
viewBox="0 0 100 100"
|
|
8
8
|
version="1.1"
|
|
9
9
|
id="svg1"
|
|
10
10
|
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
|
11
11
|
sodipodi:docname="logo.svg"
|
|
12
|
+
inkscape:export-filename="logo.png"
|
|
13
|
+
inkscape:export-xdpi="203.2"
|
|
14
|
+
inkscape:export-ydpi="203.2"
|
|
12
15
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
13
16
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
14
17
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -24,15 +27,16 @@
|
|
|
24
27
|
inkscape:deskcolor="#d1d1d1"
|
|
25
28
|
inkscape:document-units="mm"
|
|
26
29
|
showgrid="false"
|
|
27
|
-
inkscape:zoom="1.
|
|
28
|
-
inkscape:cx="
|
|
29
|
-
inkscape:cy="
|
|
30
|
+
inkscape:zoom="1.7263173"
|
|
31
|
+
inkscape:cx="202.16446"
|
|
32
|
+
inkscape:cy="214.03945"
|
|
30
33
|
inkscape:window-width="1864"
|
|
31
34
|
inkscape:window-height="1163"
|
|
32
35
|
inkscape:window-x="0"
|
|
33
36
|
inkscape:window-y="0"
|
|
34
37
|
inkscape:window-maximized="1"
|
|
35
|
-
inkscape:current-layer="layer1"
|
|
38
|
+
inkscape:current-layer="layer1"
|
|
39
|
+
showguides="true">
|
|
36
40
|
<inkscape:grid
|
|
37
41
|
id="grid1"
|
|
38
42
|
units="mm"
|
|
@@ -55,134 +59,85 @@
|
|
|
55
59
|
inkscape:groupmode="layer"
|
|
56
60
|
id="layer1">
|
|
57
61
|
<rect
|
|
58
|
-
style="fill:#
|
|
59
|
-
id="
|
|
60
|
-
width="
|
|
61
|
-
height="
|
|
62
|
-
x="
|
|
63
|
-
y="
|
|
62
|
+
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
63
|
+
id="rect8"
|
|
64
|
+
width="67.87429"
|
|
65
|
+
height="22.082184"
|
|
66
|
+
x="16.43322"
|
|
67
|
+
y="69.388191"
|
|
68
|
+
ry="3.2184362" />
|
|
69
|
+
<circle
|
|
70
|
+
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
71
|
+
id="path9"
|
|
72
|
+
cx="49.649223"
|
|
73
|
+
cy="49.477631"
|
|
74
|
+
r="36.714138" />
|
|
64
75
|
<path
|
|
65
76
|
id="rect3"
|
|
66
|
-
style="fill:#
|
|
67
|
-
d="
|
|
77
|
+
style="fill:#ba2b2b;fill-opacity:1;stroke:#ffcf42;stroke-width:0.5;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
78
|
+
d="M 49.999942,7.2545716 28.60704,28.647471 l -0.01117,0.01116 a 15.134882,15.134882 0 0 0 0,21.404075 15.134882,15.134882 0 0 0 19.237387,1.796191 15.134882,15.134882 0 0 1 0.0045,0.365581 15.134882,15.134882 0 0 1 -15.135074,15.13507 h 34.594132 a 15.134882,15.134882 0 0 1 -15.134627,-15.13507 15.134882,15.134882 0 0 1 0.0045,-0.365581 15.134882,15.134882 0 0 0 19.237382,-1.79664 15.134882,15.134882 0 0 0 -4.42e-4,-21.404067 z" />
|
|
68
79
|
<circle
|
|
69
|
-
style="fill:none;stroke:#
|
|
80
|
+
style="fill:none;stroke:#ffcf42;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
|
|
70
81
|
id="path1"
|
|
71
|
-
cx="-
|
|
72
|
-
cy="-
|
|
73
|
-
r="
|
|
82
|
+
cx="-15.090682"
|
|
83
|
+
cy="-70.754875"
|
|
84
|
+
r="15.134882"
|
|
74
85
|
transform="rotate(135)" />
|
|
75
86
|
<circle
|
|
76
|
-
style="fill:none;stroke:#
|
|
87
|
+
style="fill:none;stroke:#ffcf42;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
|
|
77
88
|
id="path1-8"
|
|
78
|
-
cx="
|
|
79
|
-
cy="-55.
|
|
80
|
-
r="
|
|
89
|
+
cx="0.04419899"
|
|
90
|
+
cy="-55.619984"
|
|
91
|
+
r="15.134882"
|
|
81
92
|
transform="rotate(135)" />
|
|
82
93
|
<circle
|
|
83
|
-
style="fill:none;fill-opacity:1;stroke:#
|
|
94
|
+
style="fill:none;fill-opacity:1;stroke:#ffcf42;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
|
|
84
95
|
id="path1-8-2"
|
|
85
|
-
cx="
|
|
86
|
-
cy="
|
|
87
|
-
r="
|
|
96
|
+
cx="67.29686"
|
|
97
|
+
cy="52.224457"
|
|
98
|
+
r="15.134882" />
|
|
88
99
|
<circle
|
|
89
|
-
style="fill:none;fill-opacity:1;stroke:#
|
|
100
|
+
style="fill:none;fill-opacity:1;stroke:#ffcf42;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
|
|
90
101
|
id="path1-8-2-3"
|
|
91
|
-
cx="
|
|
92
|
-
cy="
|
|
93
|
-
r="
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
id="rect9"
|
|
132
|
-
width="3.6363642"
|
|
133
|
-
height="7.8007298"
|
|
134
|
-
x="31.818186"
|
|
135
|
-
y="86.378426" />
|
|
136
|
-
<rect
|
|
137
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
138
|
-
id="rect9-0"
|
|
139
|
-
width="3.6363642"
|
|
140
|
-
height="7.8007298"
|
|
141
|
-
x="42.727276"
|
|
142
|
-
y="86.378433" />
|
|
143
|
-
<rect
|
|
144
|
-
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
145
|
-
id="rect11"
|
|
146
|
-
width="7.272727"
|
|
147
|
-
height="3.6080317"
|
|
148
|
-
x="31.818186"
|
|
149
|
-
y="78.870033" />
|
|
150
|
-
<rect
|
|
151
|
-
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
152
|
-
id="rect11-4"
|
|
153
|
-
width="7.272727"
|
|
154
|
-
height="3.6080317"
|
|
155
|
-
x="39.090912"
|
|
156
|
-
y="78.870033" />
|
|
157
|
-
<path
|
|
158
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
159
|
-
d="m 68.181821,90.278798 -7.272727,-3.900366 v 0 l 7.272726,-7.508396 z"
|
|
160
|
-
id="path11"
|
|
161
|
-
sodipodi:nodetypes="ccccc" />
|
|
162
|
-
<rect
|
|
163
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
164
|
-
id="rect12-9"
|
|
165
|
-
width="6.9679103"
|
|
166
|
-
height="3.9003553"
|
|
167
|
-
x="53.788776"
|
|
168
|
-
y="94.179153" />
|
|
169
|
-
<rect
|
|
170
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
171
|
-
id="rect12-9-9"
|
|
172
|
-
width="6.9679103"
|
|
173
|
-
height="3.9003553"
|
|
174
|
-
x="82.727272"
|
|
175
|
-
y="82.624237" />
|
|
176
|
-
<rect
|
|
177
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
178
|
-
id="rect12-9-9-9"
|
|
179
|
-
width="6.9679103"
|
|
180
|
-
height="3.9003553"
|
|
181
|
-
x="82.727272"
|
|
182
|
-
y="90.278801" />
|
|
183
|
-
<path
|
|
184
|
-
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999997;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
185
|
-
d="m 53.636366,86.378432 0.15241,-7.508396 h 7.120316 z"
|
|
186
|
-
id="path12" />
|
|
102
|
+
cx="32.702847"
|
|
103
|
+
cy="52.224457"
|
|
104
|
+
r="15.134882" />
|
|
105
|
+
<g
|
|
106
|
+
id="g8"
|
|
107
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-linejoin:miter;stroke-opacity:1;stroke-width:0.5;stroke-dasharray:none"
|
|
108
|
+
transform="translate(0,1.0583333)">
|
|
109
|
+
<path
|
|
110
|
+
id="rect2"
|
|
111
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
112
|
+
d="M 15.406292,74.466773 V 91.080229 H 28.446323 V 84.333871 H 22.864751 V 74.466773 Z"
|
|
113
|
+
sodipodi:nodetypes="ccccccc" />
|
|
114
|
+
<path
|
|
115
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
116
|
+
d="m 33.048199,75.699528 v 3.500747 h 2.341044 v 5.04483 h -2.341044 v 6.83512 h 11.48336 v -6.83512 h -2.342303 v -5.04483 h 2.342303 v -3.500747 h -5.729088 -0.01385 z"
|
|
117
|
+
id="path7"
|
|
118
|
+
sodipodi:nodetypes="ccccccccccccccc" />
|
|
119
|
+
<path
|
|
120
|
+
id="rect7-9"
|
|
121
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
122
|
+
d="m 69.67956,74.516469 v 16.613457 h 15.65488 v -6.019272 h -5.061194 v -2.314587 h 5.061194 v -3.24683 h -5.061194 v -2.315104 h 5.061194 v -2.717664 z"
|
|
123
|
+
sodipodi:nodetypes="ccccccccccccc" />
|
|
124
|
+
<path
|
|
125
|
+
id="rect5"
|
|
126
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
127
|
+
d="m 49.204699,73.458136 v 17.671793 h 5.69836 v -2.315107 h 5.34696 v 2.315107 h 5.69836 c 0,0 0.486363,-6.903304 -1.021347,-8.571792 -1.610295,-1.782013 -2.956804,-2.064783 -2.956804,-2.064783 l 4.122579,-5.976885 h -6.317526 l -2.974663,5.947607 -2.014865,0.01689 -0.0035,-7.022831 z"
|
|
128
|
+
sodipodi:nodetypes="cccccccsccccccc" />
|
|
129
|
+
<circle
|
|
130
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
131
|
+
id="path8"
|
|
132
|
+
cx="35.389244"
|
|
133
|
+
cy="71.791946"
|
|
134
|
+
r="2.7943003" />
|
|
135
|
+
<circle
|
|
136
|
+
style="fill:#ffcf42;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
|
137
|
+
id="path8-6"
|
|
138
|
+
cx="42.119633"
|
|
139
|
+
cy="71.791946"
|
|
140
|
+
r="2.7943003" />
|
|
141
|
+
</g>
|
|
187
142
|
</g>
|
|
188
143
|
</svg>
|
package/dist/engine.d.ts
CHANGED
|
@@ -17,11 +17,13 @@ export declare class Engine {
|
|
|
17
17
|
private isRunning;
|
|
18
18
|
private lastTime;
|
|
19
19
|
private abort;
|
|
20
|
+
private sceneStack;
|
|
20
21
|
/**
|
|
21
22
|
* The Like interface providing access to all engine subsystems.
|
|
22
23
|
*/
|
|
23
24
|
readonly like: Like;
|
|
24
25
|
constructor(container: HTMLElement);
|
|
26
|
+
private refreshScene;
|
|
25
27
|
private dispatch;
|
|
26
28
|
/**
|
|
27
29
|
* Start the game loop.
|
package/dist/engine.js
CHANGED
|
@@ -42,6 +42,12 @@ export class Engine {
|
|
|
42
42
|
writable: true,
|
|
43
43
|
value: new AbortController()
|
|
44
44
|
});
|
|
45
|
+
Object.defineProperty(this, "sceneStack", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: []
|
|
50
|
+
});
|
|
45
51
|
/**
|
|
46
52
|
* The Like interface providing access to all engine subsystems.
|
|
47
53
|
*/
|
|
@@ -53,16 +59,13 @@ export class Engine {
|
|
|
53
59
|
});
|
|
54
60
|
this.canvas = document.createElement('canvas');
|
|
55
61
|
const canvas = new Canvas(this.canvas, this.dispatch.bind(this), this.abort.signal);
|
|
56
|
-
this.canvas.addEventListener("like:updateRenderTarget", (event) => {
|
|
57
|
-
this.like.gfx.setContext(event.detail.target.getContext("2d"));
|
|
58
|
-
});
|
|
59
62
|
this.container.appendChild(this.canvas);
|
|
60
63
|
const props = {
|
|
61
64
|
canvas: this.canvas,
|
|
62
65
|
dispatch: this.dispatch.bind(this),
|
|
63
66
|
abort: this.abort.signal,
|
|
64
67
|
};
|
|
65
|
-
const gfx = new Graphics(
|
|
68
|
+
const gfx = new Graphics(canvas.getContext());
|
|
66
69
|
const audio = new Audio();
|
|
67
70
|
const timer = new Timer(props);
|
|
68
71
|
const keyboard = new Keyboard(props);
|
|
@@ -80,15 +83,22 @@ export class Engine {
|
|
|
80
83
|
canvas,
|
|
81
84
|
start: this.start.bind(this),
|
|
82
85
|
dispose: this.dispose.bind(this),
|
|
86
|
+
getScene: (pos = -1) => {
|
|
87
|
+
return this.sceneStack.at(pos);
|
|
88
|
+
},
|
|
89
|
+
pushScene: (scene, _overlay) => {
|
|
90
|
+
this.sceneStack.push(scene);
|
|
91
|
+
this.refreshScene();
|
|
92
|
+
},
|
|
93
|
+
popScene: () => {
|
|
94
|
+
const s = this.sceneStack.pop();
|
|
95
|
+
this.refreshScene();
|
|
96
|
+
return s;
|
|
97
|
+
},
|
|
83
98
|
setScene: (scene) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.dispatch("load", []);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
this.like.handleEvent = undefined;
|
|
91
|
-
}
|
|
99
|
+
const idx = Math.max(0, this.sceneStack.length - 1);
|
|
100
|
+
this.sceneStack[idx] = scene;
|
|
101
|
+
this.refreshScene();
|
|
92
102
|
},
|
|
93
103
|
callOwnHandlers: (event) => {
|
|
94
104
|
if (event.type in this.like)
|
|
@@ -99,6 +109,18 @@ export class Engine {
|
|
|
99
109
|
window.addEventListener('blur', () => this.dispatch('blur', ['tab']));
|
|
100
110
|
this.canvas.addEventListener('focus', () => this.dispatch('focus', ['canvas']));
|
|
101
111
|
}
|
|
112
|
+
refreshScene() {
|
|
113
|
+
const topScene = this.sceneStack.at(-1);
|
|
114
|
+
if (topScene) {
|
|
115
|
+
this.like.handleEvent = (event) => sceneDispatch(topScene, this.like, event);
|
|
116
|
+
if (this.isRunning) {
|
|
117
|
+
this.dispatch("load", []);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.like.handleEvent = undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
102
124
|
dispatch(type, args) {
|
|
103
125
|
const event = { type, args, timestamp: this.like.timer.getTime() };
|
|
104
126
|
if (this.like.handleEvent) {
|
package/dist/events.d.ts
CHANGED
|
@@ -12,21 +12,28 @@ export type CanvasSize = Vector2 | 'native';
|
|
|
12
12
|
* A manager for the HTML canvas element, similar to `love.window`.
|
|
13
13
|
*
|
|
14
14
|
* Controls game size / scaling -- both native and pixelart mode via {@link Canvas.setMode}, as well as fullscreen functions.
|
|
15
|
+
*
|
|
16
|
+
* The canvas keeps two canvases: render and display. Each frame, it copies render to display before the canvas is presented.
|
|
17
|
+
* This allows for pixel-accurate scaling.
|
|
15
18
|
*/
|
|
16
19
|
export declare class Canvas {
|
|
17
20
|
/** The ultimately visible canvas in the browser */
|
|
18
21
|
private displayCanvas;
|
|
19
22
|
private dispatch;
|
|
20
23
|
private abort;
|
|
21
|
-
/** The canvas that we're drawing to with `like.gfx` functions.
|
|
22
|
-
* If it's the same as displayCanvas, we're in native mode.
|
|
23
|
-
* Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
|
|
24
|
-
*/
|
|
24
|
+
/** The canvas that we're drawing to with `like.gfx` functions. */
|
|
25
25
|
private renderCanvas;
|
|
26
26
|
private resizeTimeoutId;
|
|
27
|
+
private isNativeMode;
|
|
27
28
|
constructor(
|
|
28
29
|
/** The ultimately visible canvas in the browser */
|
|
29
30
|
displayCanvas: LikeCanvasElement, dispatch: Dispatcher<'resize'>, abort: AbortSignal);
|
|
31
|
+
/**
|
|
32
|
+
* Get the canvas that graphics functions render to.
|
|
33
|
+
* This is separate from the display canvas; it is
|
|
34
|
+
* not visibly exposed but rather copied each frame.
|
|
35
|
+
*/
|
|
36
|
+
getContext(): CanvasRenderingContext2D;
|
|
30
37
|
/** Get a unified canvas info object. */
|
|
31
38
|
getMode(): {
|
|
32
39
|
size: Vector2;
|
|
@@ -57,14 +64,15 @@ export declare class Canvas {
|
|
|
57
64
|
private getDisplayPixelSize;
|
|
58
65
|
/** Are we fullscreen? */
|
|
59
66
|
getFullscreen(): boolean;
|
|
67
|
+
/** Does the canvas have focus? */
|
|
68
|
+
hasFocus(): boolean;
|
|
60
69
|
/** Set fullscreen. */
|
|
61
70
|
setFullscreen(fullscreen: boolean): void;
|
|
62
71
|
/**
|
|
63
|
-
*
|
|
64
|
-
* rendering a frame.
|
|
72
|
+
* Trigered by `like:preDraw`
|
|
65
73
|
*/
|
|
66
74
|
private preDraw;
|
|
67
|
-
/**
|
|
75
|
+
/** Triggered by `like:postDraw` */
|
|
68
76
|
private postDraw;
|
|
69
77
|
/** @returns if size was changed. */
|
|
70
78
|
static setCanvasElemSize(canvas: LikeCanvasElement, newSize: Vector2): boolean;
|
package/dist/graphics/canvas.js
CHANGED
|
@@ -4,6 +4,9 @@ import { Vec2 } from "../math/vector2";
|
|
|
4
4
|
* A manager for the HTML canvas element, similar to `love.window`.
|
|
5
5
|
*
|
|
6
6
|
* Controls game size / scaling -- both native and pixelart mode via {@link Canvas.setMode}, as well as fullscreen functions.
|
|
7
|
+
*
|
|
8
|
+
* The canvas keeps two canvases: render and display. Each frame, it copies render to display before the canvas is presented.
|
|
9
|
+
* This allows for pixel-accurate scaling.
|
|
7
10
|
*/
|
|
8
11
|
export class Canvas {
|
|
9
12
|
constructor(
|
|
@@ -27,10 +30,7 @@ export class Canvas {
|
|
|
27
30
|
writable: true,
|
|
28
31
|
value: abort
|
|
29
32
|
});
|
|
30
|
-
/** The canvas that we're drawing to with `like.gfx` functions.
|
|
31
|
-
* If it's the same as displayCanvas, we're in native mode.
|
|
32
|
-
* Otherwise, we're in pixelart mode, consisting of nearest -> linear scaling.
|
|
33
|
-
*/
|
|
33
|
+
/** The canvas that we're drawing to with `like.gfx` functions. */
|
|
34
34
|
Object.defineProperty(this, "renderCanvas", {
|
|
35
35
|
enumerable: true,
|
|
36
36
|
configurable: true,
|
|
@@ -43,42 +43,39 @@ export class Canvas {
|
|
|
43
43
|
writable: true,
|
|
44
44
|
value: 0
|
|
45
45
|
});
|
|
46
|
+
Object.defineProperty(this, "isNativeMode", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: true
|
|
51
|
+
});
|
|
46
52
|
displayCanvas.tabIndex = 0;
|
|
47
53
|
displayCanvas.style.width = '100%';
|
|
48
54
|
displayCanvas.style.height = '100%';
|
|
49
|
-
|
|
55
|
+
displayCanvas.style.objectFit = 'contain';
|
|
56
|
+
// Always create a separate render canvas
|
|
57
|
+
this.renderCanvas = document.createElement('canvas');
|
|
50
58
|
this.setMode('native');
|
|
51
59
|
/** Only the canvas can really transform the mouse to the game size.
|
|
52
60
|
* This hack sends an event for the mouse module to listen to.
|
|
53
61
|
*/
|
|
54
62
|
this.displayCanvas.addEventListener('mousemove', (ev) => {
|
|
55
|
-
let pos;
|
|
56
|
-
let delta;
|
|
57
63
|
const rawPos = [ev.offsetX, ev.offsetY];
|
|
58
64
|
const rawDelta = [ev.movementX, ev.movementY];
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const scale = calcAspectFriendlyScale(this.getSize(), csize);
|
|
74
|
-
/* Upper-left corner */
|
|
75
|
-
const offset = Vec2.div(Vec2.sub(csize, Vec2.mul(this.getSize(), scale)), 2);
|
|
76
|
-
pos = Vec2.div(Vec2.sub(rawPos, offset), scale);
|
|
77
|
-
delta = Vec2.div(rawDelta, scale);
|
|
78
|
-
/* Only handle mousemove events that are in bounds. */
|
|
79
|
-
if (!Rect.containsPoint(this.getRect(), pos)) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
65
|
+
/* Recreation of object-fit */
|
|
66
|
+
const csize = [
|
|
67
|
+
this.displayCanvas.clientWidth,
|
|
68
|
+
this.displayCanvas.clientHeight
|
|
69
|
+
];
|
|
70
|
+
/* Scale of both dimensions */
|
|
71
|
+
const scale = calcAspectFriendlyScale(this.getSize(), csize);
|
|
72
|
+
/* Upper-left corner */
|
|
73
|
+
const offset = Vec2.div(Vec2.sub(csize, Vec2.mul(this.getSize(), scale)), 2);
|
|
74
|
+
const pos = Vec2.div(Vec2.sub(rawPos, offset), scale);
|
|
75
|
+
const delta = Vec2.div(rawDelta, scale);
|
|
76
|
+
/* Only handle mousemove events that are in bounds. */
|
|
77
|
+
if (!Rect.containsPoint(this.getRect(), pos)) {
|
|
78
|
+
return;
|
|
82
79
|
}
|
|
83
80
|
this.displayCanvas.dispatchEvent(new CustomEvent('like:mousemoved', {
|
|
84
81
|
detail: {
|
|
@@ -90,6 +87,14 @@ export class Canvas {
|
|
|
90
87
|
this.displayCanvas.addEventListener("like:preDraw", this.preDraw.bind(this), { signal: this.abort });
|
|
91
88
|
this.displayCanvas.addEventListener("like:postDraw", this.postDraw.bind(this), { signal: this.abort });
|
|
92
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the canvas that graphics functions render to.
|
|
92
|
+
* This is separate from the display canvas; it is
|
|
93
|
+
* not visibly exposed but rather copied each frame.
|
|
94
|
+
*/
|
|
95
|
+
getContext() {
|
|
96
|
+
return this.renderCanvas.getContext('2d');
|
|
97
|
+
}
|
|
93
98
|
/** Get a unified canvas info object. */
|
|
94
99
|
getMode() {
|
|
95
100
|
return {
|
|
@@ -115,27 +120,13 @@ export class Canvas {
|
|
|
115
120
|
* @param flags optional options.
|
|
116
121
|
*/
|
|
117
122
|
setMode(size, flags = {}) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (size == 'native') {
|
|
121
|
-
this.displayCanvas.style.objectFit = 'fill';
|
|
122
|
-
this.renderCanvas = this.displayCanvas;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
this.displayCanvas.style.objectFit = 'contain';
|
|
126
|
-
this.renderCanvas = document.createElement('canvas');
|
|
123
|
+
this.isNativeMode = size === 'native';
|
|
124
|
+
if (size !== 'native') {
|
|
127
125
|
const changed = Canvas.setCanvasElemSize(this.renderCanvas, size);
|
|
128
126
|
if (changed) {
|
|
129
127
|
this.dispatchResize(size);
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
|
-
if (prevRenderCanvas != this.renderCanvas) {
|
|
133
|
-
this.displayCanvas.dispatchEvent(new CustomEvent("like:updateRenderTarget", {
|
|
134
|
-
detail: {
|
|
135
|
-
target: this.renderCanvas,
|
|
136
|
-
},
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
139
130
|
if ('fullscreen' in flags) {
|
|
140
131
|
this.setFullscreen(flags.fullscreen);
|
|
141
132
|
}
|
|
@@ -164,6 +155,10 @@ export class Canvas {
|
|
|
164
155
|
getFullscreen() {
|
|
165
156
|
return this.displayCanvas === document.fullscreenElement;
|
|
166
157
|
}
|
|
158
|
+
/** Does the canvas have focus? */
|
|
159
|
+
hasFocus() {
|
|
160
|
+
return document.activeElement === this.displayCanvas;
|
|
161
|
+
}
|
|
167
162
|
/** Set fullscreen. */
|
|
168
163
|
setFullscreen(fullscreen) {
|
|
169
164
|
if (fullscreen) {
|
|
@@ -177,49 +172,50 @@ export class Canvas {
|
|
|
177
172
|
}
|
|
178
173
|
}
|
|
179
174
|
/**
|
|
180
|
-
*
|
|
181
|
-
* rendering a frame.
|
|
175
|
+
* Trigered by `like:preDraw`
|
|
182
176
|
*/
|
|
183
177
|
preDraw() {
|
|
184
|
-
if (this.
|
|
178
|
+
if (this.isNativeMode) {
|
|
179
|
+
// In native mode, renderCanvas tracks display size
|
|
185
180
|
const realSize = this.getDisplayPixelSize();
|
|
186
|
-
if ((realSize[0] != this.
|
|
187
|
-
realSize[1] != this.
|
|
181
|
+
if ((realSize[0] != this.renderCanvas.width ||
|
|
182
|
+
realSize[1] != this.renderCanvas.height) &&
|
|
188
183
|
!this.resizeTimeoutId) {
|
|
189
184
|
/** In native scaling mode, zooming and resizing the window cause us
|
|
190
185
|
* to set canvas width and height every frame, which could cause
|
|
191
186
|
* tons of canvas bitmap reallocations. So wait 1/4 second..
|
|
192
187
|
*/
|
|
193
|
-
Canvas.setCanvasElemSize(this.
|
|
188
|
+
Canvas.setCanvasElemSize(this.renderCanvas, realSize);
|
|
194
189
|
this.dispatchResize(realSize);
|
|
195
190
|
this.resizeTimeoutId = setTimeout(() => { this.resizeTimeoutId = 0; }, 250);
|
|
196
191
|
}
|
|
197
192
|
}
|
|
198
|
-
this.renderCanvas.getContext('2d')
|
|
193
|
+
const ctx = this.renderCanvas.getContext('2d');
|
|
194
|
+
ctx.resetTransform();
|
|
195
|
+
// Enable smoothing in native mode, disable in pixelart mode
|
|
196
|
+
ctx.imageSmoothingEnabled = this.isNativeMode;
|
|
199
197
|
}
|
|
200
|
-
/**
|
|
198
|
+
/** Triggered by `like:postDraw` */
|
|
201
199
|
postDraw() {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
// Always blit from render canvas to display canvas
|
|
201
|
+
if (this.isNativeMode) {
|
|
202
|
+
// In native mode, display canvas matches render canvas size
|
|
203
|
+
Canvas.setCanvasElemSize(this.displayCanvas, this.getSize());
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// In pixelart mode, set output canvas size to an ideal integer scale
|
|
207
207
|
Canvas.setCanvasElemSize(this.displayCanvas, Vec2.mul(this.getSize(), Math.round(calcAspectFriendlyScale(this.getSize(), this.getDisplayPixelSize()))));
|
|
208
|
-
// Copy the internal canvas to the visible one.
|
|
209
|
-
const ctx = this.displayCanvas.getContext('2d');
|
|
210
|
-
ctx.imageSmoothingEnabled = false;
|
|
211
|
-
ctx.drawImage(this.renderCanvas, 0, 0, this.renderCanvas.width, this.renderCanvas.height, 0, 0, this.displayCanvas.width, this.displayCanvas.height);
|
|
212
208
|
}
|
|
209
|
+
// Copy the render canvas to the visible one
|
|
210
|
+
const displayCtx = this.displayCanvas.getContext('2d');
|
|
211
|
+
displayCtx.imageSmoothingEnabled = false;
|
|
212
|
+
displayCtx.drawImage(this.renderCanvas, 0, 0, this.renderCanvas.width, this.renderCanvas.height, 0, 0, this.displayCanvas.width, this.displayCanvas.height);
|
|
213
213
|
}
|
|
214
214
|
/** @returns if size was changed. */
|
|
215
215
|
static setCanvasElemSize(canvas, newSize) {
|
|
216
|
-
const ctx = canvas.getContext('2d');
|
|
217
|
-
if (!ctx)
|
|
218
|
-
return false;
|
|
219
216
|
if (canvas.width === newSize[0] && canvas.height === newSize[1])
|
|
220
217
|
return false;
|
|
221
|
-
canvas.width = newSize
|
|
222
|
-
canvas.height = newSize[1];
|
|
218
|
+
[canvas.width, canvas.height] = newSize;
|
|
223
219
|
return true;
|
|
224
220
|
}
|
|
225
221
|
static getCanvasElemSize(canvas) {
|