litecanvas 0.76.0 → 0.78.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.
- package/dist/dist.dev.js +85 -75
- package/dist/dist.js +71 -56
- package/dist/dist.min.js +1 -1
- package/package.json +2 -2
- package/src/index.js +107 -97
- package/types/index.d.ts +2 -4
- package/types/types.d.ts +2 -4
package/dist/dist.dev.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
// src/index.js
|
|
32
32
|
function litecanvas(settings = {}) {
|
|
33
|
-
const root = globalThis, PI =
|
|
33
|
+
const root = globalThis, math = Math, PI = math.PI, TWO_PI = PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
34
34
|
elem.addEventListener(evt, callback, false);
|
|
35
35
|
_browserEventListeners.push(
|
|
36
36
|
() => elem.removeEventListener(evt, callback, false)
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
animate: true
|
|
51
51
|
};
|
|
52
52
|
settings = Object.assign(defaults, settings);
|
|
53
|
-
let _initialized = false, _plugins = [], _canvas = settings.canvas || document.createElement("canvas"),
|
|
53
|
+
let _initialized = false, _plugins = [], _canvas = settings.canvas || document.createElement("canvas"), _animated = settings.animate, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 18, _rng_seed = Date.now(), _global = settings.global, _events = {
|
|
54
54
|
init: null,
|
|
55
55
|
update: null,
|
|
56
56
|
draw: null,
|
|
@@ -135,6 +135,25 @@
|
|
|
135
135
|
DEV: assert(isFinite(rads), "rad2deg: 1st param must be a number");
|
|
136
136
|
return 180 / PI * rads;
|
|
137
137
|
},
|
|
138
|
+
/**
|
|
139
|
+
* Calculates the integer closest to a number and optional precision.
|
|
140
|
+
*
|
|
141
|
+
* @param {number} n number to round.
|
|
142
|
+
* @param {number} [precision] number of decimal digits to round to, default is 0.
|
|
143
|
+
* @returns {number} rounded number.
|
|
144
|
+
*/
|
|
145
|
+
round: (n, precision = 0) => {
|
|
146
|
+
DEV: assert(isFinite(n), "round: 1st param must be a number");
|
|
147
|
+
DEV: assert(
|
|
148
|
+
null === precision || isFinite(precision) && precision >= 0,
|
|
149
|
+
"round: 2nd param must be a positive number or zero"
|
|
150
|
+
);
|
|
151
|
+
if (!precision) {
|
|
152
|
+
return math.round(n);
|
|
153
|
+
}
|
|
154
|
+
const multiplier = 10 ** precision;
|
|
155
|
+
return math.round(n * multiplier) / multiplier;
|
|
156
|
+
},
|
|
138
157
|
/**
|
|
139
158
|
* Constrains a number between `min` and `max`.
|
|
140
159
|
*
|
|
@@ -175,7 +194,7 @@
|
|
|
175
194
|
max !== min,
|
|
176
195
|
"randi: the 2nd param must be not equal to the 3rd param"
|
|
177
196
|
);
|
|
178
|
-
return value - (max - min) *
|
|
197
|
+
return value - (max - min) * math.floor((value - min) / (max - min));
|
|
179
198
|
},
|
|
180
199
|
/**
|
|
181
200
|
* Re-maps a number from one range to another.
|
|
@@ -249,7 +268,7 @@
|
|
|
249
268
|
max > min,
|
|
250
269
|
"randi: the 1st param must be less than the 2nd param"
|
|
251
270
|
);
|
|
252
|
-
return
|
|
271
|
+
return math.floor(instance.rand(min, max + 1));
|
|
253
272
|
},
|
|
254
273
|
/**
|
|
255
274
|
* If a value is passed, initializes the random number generator with an explicit seed value.
|
|
@@ -269,12 +288,12 @@
|
|
|
269
288
|
/**
|
|
270
289
|
* Clear the game screen with an optional color
|
|
271
290
|
*
|
|
272
|
-
* @param {number?} color The background color (index) or null (for transparent)
|
|
291
|
+
* @param {number?} color The background color (index) or null/undefined (for transparent)
|
|
273
292
|
*/
|
|
274
293
|
cls(color) {
|
|
275
294
|
DEV: assert(
|
|
276
295
|
null == color || isFinite(color) && color >= 0,
|
|
277
|
-
"cls: 1st param must be a positive number or zero or
|
|
296
|
+
"cls: 1st param must be a positive number or zero or undefined"
|
|
278
297
|
);
|
|
279
298
|
if (null == color) {
|
|
280
299
|
_ctx.clearRect(0, 0, _ctx.canvas.width, _ctx.canvas.height);
|
|
@@ -797,51 +816,6 @@
|
|
|
797
816
|
DEV: assert(isFinite(value), "volume: 1st param must be a number");
|
|
798
817
|
root.zzfxV = value;
|
|
799
818
|
},
|
|
800
|
-
/** UTILS API */
|
|
801
|
-
/**
|
|
802
|
-
* Check a collision between two rectangles
|
|
803
|
-
*
|
|
804
|
-
* @param {number} x1 first rectangle position X
|
|
805
|
-
* @param {number} y1 first rectangle position Y
|
|
806
|
-
* @param {number} w1 first rectangle width
|
|
807
|
-
* @param {number} h1 first rectangle height
|
|
808
|
-
* @param {number} x2 second rectangle position X
|
|
809
|
-
* @param {number} y2 second rectangle position Y
|
|
810
|
-
* @param {number} w2 second rectangle width
|
|
811
|
-
* @param {number} h2 second rectangle height
|
|
812
|
-
* @returns {boolean}
|
|
813
|
-
*/
|
|
814
|
-
colrect: (x1, y1, w1, h1, x2, y2, w2, h2) => {
|
|
815
|
-
DEV: assert(isFinite(x1), "colrect: 1st param must be a number");
|
|
816
|
-
DEV: assert(isFinite(y1), "colrect: 2nd param must be a number");
|
|
817
|
-
DEV: assert(isFinite(w1), "colrect: 3rd param must be a number");
|
|
818
|
-
DEV: assert(isFinite(h1), "colrect: 4th param must be a number");
|
|
819
|
-
DEV: assert(isFinite(x2), "colrect: 5th param must be a number");
|
|
820
|
-
DEV: assert(isFinite(y2), "colrect: 6th param must be a number");
|
|
821
|
-
DEV: assert(isFinite(w2), "colrect: 7th param must be a number");
|
|
822
|
-
DEV: assert(isFinite(h2), "colrect: 8th param must be a number");
|
|
823
|
-
return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
|
|
824
|
-
},
|
|
825
|
-
/**
|
|
826
|
-
* Check a collision between two circles
|
|
827
|
-
*
|
|
828
|
-
* @param {number} x1 first circle position X
|
|
829
|
-
* @param {number} y1 first circle position Y
|
|
830
|
-
* @param {number} r1 first circle position radius
|
|
831
|
-
* @param {number} x2 second circle position X
|
|
832
|
-
* @param {number} y2 second circle position Y
|
|
833
|
-
* @param {number} r2 second circle position radius
|
|
834
|
-
* @returns {boolean}
|
|
835
|
-
*/
|
|
836
|
-
colcirc: (x1, y1, r1, x2, y2, r2) => {
|
|
837
|
-
DEV: assert(isFinite(x1), "colcirc: 1st param must be a number");
|
|
838
|
-
DEV: assert(isFinite(y1), "colcirc: 2nd param must be a number");
|
|
839
|
-
DEV: assert(isFinite(r1), "colcirc: 3rd param must be a number");
|
|
840
|
-
DEV: assert(isFinite(x2), "colcirc: 4th param must be a number");
|
|
841
|
-
DEV: assert(isFinite(y2), "colcirc: 5th param must be a number");
|
|
842
|
-
DEV: assert(isFinite(r2), "colcirc: 6th param must be a number");
|
|
843
|
-
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) <= (r1 + r2) * (r1 + r2);
|
|
844
|
-
},
|
|
845
819
|
/** PLUGINS API */
|
|
846
820
|
/**
|
|
847
821
|
* Prepares a plugin to be loaded
|
|
@@ -982,22 +956,22 @@
|
|
|
982
956
|
* Stops the litecanvas instance and remove all event listeners.
|
|
983
957
|
*/
|
|
984
958
|
quit() {
|
|
959
|
+
cancelAnimationFrame(_rafid);
|
|
985
960
|
instance.emit("quit");
|
|
961
|
+
_events = [];
|
|
986
962
|
for (const removeListener of _browserEventListeners) {
|
|
987
963
|
removeListener();
|
|
988
964
|
}
|
|
989
|
-
cancelAnimationFrame(_rafid);
|
|
990
|
-
_events = false;
|
|
991
965
|
if (_global) {
|
|
992
966
|
for (const key in instance) {
|
|
993
967
|
delete root[key];
|
|
994
968
|
}
|
|
995
|
-
delete root.
|
|
969
|
+
delete root.ENGINE;
|
|
996
970
|
}
|
|
997
971
|
}
|
|
998
972
|
};
|
|
999
|
-
for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,
|
|
1000
|
-
instance[k] =
|
|
973
|
+
for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
|
|
974
|
+
instance[k] = math[k];
|
|
1001
975
|
}
|
|
1002
976
|
function init() {
|
|
1003
977
|
_initialized = true;
|
|
@@ -1008,7 +982,7 @@
|
|
|
1008
982
|
for (const [callback, config] of _plugins) {
|
|
1009
983
|
loadPlugin(callback, config);
|
|
1010
984
|
}
|
|
1011
|
-
if (
|
|
985
|
+
if (settings.autoscale) {
|
|
1012
986
|
on(root, "resize", onResize);
|
|
1013
987
|
}
|
|
1014
988
|
if (settings.tapEvents) {
|
|
@@ -1030,7 +1004,7 @@
|
|
|
1030
1004
|
const tap = _taps.get(id) || _registerTap(id);
|
|
1031
1005
|
tap.x = x;
|
|
1032
1006
|
tap.y = y;
|
|
1033
|
-
}, _checkTapped = (tap) => tap && performance.now() - tap.ts <=
|
|
1007
|
+
}, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 300, preventDefault = (ev) => ev.preventDefault();
|
|
1034
1008
|
let _pressingMouse = false;
|
|
1035
1009
|
on(_canvas, "mousedown", (ev) => {
|
|
1036
1010
|
if (ev.button === 0) {
|
|
@@ -1109,22 +1083,59 @@
|
|
|
1109
1083
|
});
|
|
1110
1084
|
}
|
|
1111
1085
|
if (settings.keyboardEvents) {
|
|
1112
|
-
const
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1086
|
+
const toLowerCase = (s) => s.toLowerCase();
|
|
1087
|
+
const _keysDown = /* @__PURE__ */ new Set();
|
|
1088
|
+
const _keysPress = /* @__PURE__ */ new Set();
|
|
1089
|
+
const keyCheck = (keysSet, key) => {
|
|
1090
|
+
return !key ? keysSet.size > 0 : keysSet.has(
|
|
1091
|
+
"space" === toLowerCase(key) ? " " : toLowerCase(key)
|
|
1117
1092
|
);
|
|
1118
|
-
return "any" === key ? _keys.size > 0 : _keys.has(key.toLowerCase());
|
|
1119
1093
|
};
|
|
1120
|
-
instance.setvar("iskeydown", iskeydown);
|
|
1121
1094
|
on(root, "keydown", (event) => {
|
|
1122
|
-
|
|
1095
|
+
if (!_keysDown.has(toLowerCase(event.key))) {
|
|
1096
|
+
_keysDown.add(toLowerCase(event.key));
|
|
1097
|
+
_keysPress.add(toLowerCase(event.key));
|
|
1098
|
+
}
|
|
1123
1099
|
});
|
|
1124
1100
|
on(root, "keyup", (event) => {
|
|
1125
|
-
|
|
1101
|
+
_keysDown.delete(toLowerCase(event.key));
|
|
1126
1102
|
});
|
|
1127
|
-
on(root, "blur", () =>
|
|
1103
|
+
on(root, "blur", () => _keysDown.clear());
|
|
1104
|
+
instance.listen("after:draw", () => _keysPress.clear());
|
|
1105
|
+
instance.setvar(
|
|
1106
|
+
"iskeydown",
|
|
1107
|
+
/**
|
|
1108
|
+
* Checks if a which key is pressed (down) on the keyboard.
|
|
1109
|
+
* Note: use `iskeydown()` to check for any key.
|
|
1110
|
+
*
|
|
1111
|
+
* @param {string?} key
|
|
1112
|
+
* @returns {boolean}
|
|
1113
|
+
*/
|
|
1114
|
+
(key) => {
|
|
1115
|
+
DEV: assert(
|
|
1116
|
+
null == key || "string" === typeof key,
|
|
1117
|
+
"iskeydown: 1st param must be a string or undefined"
|
|
1118
|
+
);
|
|
1119
|
+
return keyCheck(_keysDown, key);
|
|
1120
|
+
}
|
|
1121
|
+
);
|
|
1122
|
+
instance.setvar(
|
|
1123
|
+
"iskeypressed",
|
|
1124
|
+
/**
|
|
1125
|
+
* Checks if a which key just got pressed on the keyboard.
|
|
1126
|
+
* Note: use `iskeypressed()` to check for any key.
|
|
1127
|
+
*
|
|
1128
|
+
* @param {string?} key
|
|
1129
|
+
* @returns {boolean}
|
|
1130
|
+
*/
|
|
1131
|
+
(key) => {
|
|
1132
|
+
DEV: assert(
|
|
1133
|
+
null == key || "string" === typeof key,
|
|
1134
|
+
"iskeypressed: 1st param must be a string or undefined"
|
|
1135
|
+
);
|
|
1136
|
+
return keyCheck(_keysPress, key);
|
|
1137
|
+
}
|
|
1138
|
+
);
|
|
1128
1139
|
}
|
|
1129
1140
|
if (settings.pauseOnBlur) {
|
|
1130
1141
|
on(root, "blur", () => {
|
|
@@ -1136,7 +1147,6 @@
|
|
|
1136
1147
|
}
|
|
1137
1148
|
});
|
|
1138
1149
|
}
|
|
1139
|
-
instance.setfps(60);
|
|
1140
1150
|
instance.emit("init", instance);
|
|
1141
1151
|
_lastFrameTime = performance.now();
|
|
1142
1152
|
_rafid = raf(drawFrame);
|
|
@@ -1148,7 +1158,7 @@
|
|
|
1148
1158
|
let updated = 0, frameTime = (now - _lastFrameTime) / 1e3;
|
|
1149
1159
|
_lastFrameTime = now;
|
|
1150
1160
|
if (frameTime > _deltaTime * 30) {
|
|
1151
|
-
console.
|
|
1161
|
+
console.warn("skipping too long frame");
|
|
1152
1162
|
} else {
|
|
1153
1163
|
_accumulated += frameTime;
|
|
1154
1164
|
if (!_animated) {
|
|
@@ -1199,12 +1209,12 @@
|
|
|
1199
1209
|
}
|
|
1200
1210
|
function onResize() {
|
|
1201
1211
|
const styles = _canvas.style;
|
|
1202
|
-
if (
|
|
1212
|
+
if (settings.autoscale) {
|
|
1203
1213
|
if (!styles.display) {
|
|
1204
1214
|
styles.display = "block";
|
|
1205
1215
|
styles.margin = "auto";
|
|
1206
1216
|
}
|
|
1207
|
-
_scale =
|
|
1217
|
+
_scale = math.min(
|
|
1208
1218
|
root.innerWidth / instance.WIDTH,
|
|
1209
1219
|
root.innerHeight / instance.HEIGHT
|
|
1210
1220
|
);
|
|
@@ -1238,11 +1248,11 @@
|
|
|
1238
1248
|
}
|
|
1239
1249
|
}
|
|
1240
1250
|
if (_global) {
|
|
1241
|
-
if (root.
|
|
1242
|
-
throw "global litecanvas
|
|
1251
|
+
if (root.ENGINE) {
|
|
1252
|
+
throw "two global litecanvas detected";
|
|
1243
1253
|
}
|
|
1244
1254
|
Object.assign(root, instance);
|
|
1245
|
-
root.
|
|
1255
|
+
root.ENGINE = instance;
|
|
1246
1256
|
}
|
|
1247
1257
|
setupCanvas();
|
|
1248
1258
|
if ("loading" === document.readyState) {
|
package/dist/dist.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
// src/index.js
|
|
27
27
|
function litecanvas(settings = {}) {
|
|
28
|
-
const root = globalThis, PI =
|
|
28
|
+
const root = globalThis, math = Math, PI = math.PI, TWO_PI = PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
29
29
|
elem.addEventListener(evt, callback, false);
|
|
30
30
|
_browserEventListeners.push(
|
|
31
31
|
() => elem.removeEventListener(evt, callback, false)
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
animate: true
|
|
46
46
|
};
|
|
47
47
|
settings = Object.assign(defaults, settings);
|
|
48
|
-
let _initialized = false, _plugins = [], _canvas = settings.canvas || document.createElement("canvas"),
|
|
48
|
+
let _initialized = false, _plugins = [], _canvas = settings.canvas || document.createElement("canvas"), _animated = settings.animate, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 18, _rng_seed = Date.now(), _global = settings.global, _events = {
|
|
49
49
|
init: null,
|
|
50
50
|
update: null,
|
|
51
51
|
draw: null,
|
|
@@ -125,6 +125,20 @@
|
|
|
125
125
|
rad2deg: (rads) => {
|
|
126
126
|
return 180 / PI * rads;
|
|
127
127
|
},
|
|
128
|
+
/**
|
|
129
|
+
* Calculates the integer closest to a number and optional precision.
|
|
130
|
+
*
|
|
131
|
+
* @param {number} n number to round.
|
|
132
|
+
* @param {number} [precision] number of decimal digits to round to, default is 0.
|
|
133
|
+
* @returns {number} rounded number.
|
|
134
|
+
*/
|
|
135
|
+
round: (n, precision = 0) => {
|
|
136
|
+
if (!precision) {
|
|
137
|
+
return math.round(n);
|
|
138
|
+
}
|
|
139
|
+
const multiplier = 10 ** precision;
|
|
140
|
+
return math.round(n * multiplier) / multiplier;
|
|
141
|
+
},
|
|
128
142
|
/**
|
|
129
143
|
* Constrains a number between `min` and `max`.
|
|
130
144
|
*
|
|
@@ -147,7 +161,7 @@
|
|
|
147
161
|
* @returns {number}
|
|
148
162
|
*/
|
|
149
163
|
wrap: (value, min, max) => {
|
|
150
|
-
return value - (max - min) *
|
|
164
|
+
return value - (max - min) * math.floor((value - min) / (max - min));
|
|
151
165
|
},
|
|
152
166
|
/**
|
|
153
167
|
* Re-maps a number from one range to another.
|
|
@@ -201,7 +215,7 @@
|
|
|
201
215
|
* @returns {number} the random number
|
|
202
216
|
*/
|
|
203
217
|
randi: (min = 0, max = 1) => {
|
|
204
|
-
return
|
|
218
|
+
return math.floor(instance.rand(min, max + 1));
|
|
205
219
|
},
|
|
206
220
|
/**
|
|
207
221
|
* If a value is passed, initializes the random number generator with an explicit seed value.
|
|
@@ -217,7 +231,7 @@
|
|
|
217
231
|
/**
|
|
218
232
|
* Clear the game screen with an optional color
|
|
219
233
|
*
|
|
220
|
-
* @param {number?} color The background color (index) or null (for transparent)
|
|
234
|
+
* @param {number?} color The background color (index) or null/undefined (for transparent)
|
|
221
235
|
*/
|
|
222
236
|
cls(color) {
|
|
223
237
|
if (null == color) {
|
|
@@ -574,37 +588,6 @@
|
|
|
574
588
|
volume(value) {
|
|
575
589
|
root.zzfxV = value;
|
|
576
590
|
},
|
|
577
|
-
/** UTILS API */
|
|
578
|
-
/**
|
|
579
|
-
* Check a collision between two rectangles
|
|
580
|
-
*
|
|
581
|
-
* @param {number} x1 first rectangle position X
|
|
582
|
-
* @param {number} y1 first rectangle position Y
|
|
583
|
-
* @param {number} w1 first rectangle width
|
|
584
|
-
* @param {number} h1 first rectangle height
|
|
585
|
-
* @param {number} x2 second rectangle position X
|
|
586
|
-
* @param {number} y2 second rectangle position Y
|
|
587
|
-
* @param {number} w2 second rectangle width
|
|
588
|
-
* @param {number} h2 second rectangle height
|
|
589
|
-
* @returns {boolean}
|
|
590
|
-
*/
|
|
591
|
-
colrect: (x1, y1, w1, h1, x2, y2, w2, h2) => {
|
|
592
|
-
return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
|
|
593
|
-
},
|
|
594
|
-
/**
|
|
595
|
-
* Check a collision between two circles
|
|
596
|
-
*
|
|
597
|
-
* @param {number} x1 first circle position X
|
|
598
|
-
* @param {number} y1 first circle position Y
|
|
599
|
-
* @param {number} r1 first circle position radius
|
|
600
|
-
* @param {number} x2 second circle position X
|
|
601
|
-
* @param {number} y2 second circle position Y
|
|
602
|
-
* @param {number} r2 second circle position radius
|
|
603
|
-
* @returns {boolean}
|
|
604
|
-
*/
|
|
605
|
-
colcirc: (x1, y1, r1, x2, y2, r2) => {
|
|
606
|
-
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) <= (r1 + r2) * (r1 + r2);
|
|
607
|
-
},
|
|
608
591
|
/** PLUGINS API */
|
|
609
592
|
/**
|
|
610
593
|
* Prepares a plugin to be loaded
|
|
@@ -700,22 +683,22 @@
|
|
|
700
683
|
* Stops the litecanvas instance and remove all event listeners.
|
|
701
684
|
*/
|
|
702
685
|
quit() {
|
|
686
|
+
cancelAnimationFrame(_rafid);
|
|
703
687
|
instance.emit("quit");
|
|
688
|
+
_events = [];
|
|
704
689
|
for (const removeListener of _browserEventListeners) {
|
|
705
690
|
removeListener();
|
|
706
691
|
}
|
|
707
|
-
cancelAnimationFrame(_rafid);
|
|
708
|
-
_events = false;
|
|
709
692
|
if (_global) {
|
|
710
693
|
for (const key in instance) {
|
|
711
694
|
delete root[key];
|
|
712
695
|
}
|
|
713
|
-
delete root.
|
|
696
|
+
delete root.ENGINE;
|
|
714
697
|
}
|
|
715
698
|
}
|
|
716
699
|
};
|
|
717
|
-
for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,
|
|
718
|
-
instance[k] =
|
|
700
|
+
for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
|
|
701
|
+
instance[k] = math[k];
|
|
719
702
|
}
|
|
720
703
|
function init() {
|
|
721
704
|
_initialized = true;
|
|
@@ -726,7 +709,7 @@
|
|
|
726
709
|
for (const [callback, config] of _plugins) {
|
|
727
710
|
loadPlugin(callback, config);
|
|
728
711
|
}
|
|
729
|
-
if (
|
|
712
|
+
if (settings.autoscale) {
|
|
730
713
|
on(root, "resize", onResize);
|
|
731
714
|
}
|
|
732
715
|
if (settings.tapEvents) {
|
|
@@ -748,7 +731,7 @@
|
|
|
748
731
|
const tap = _taps.get(id) || _registerTap(id);
|
|
749
732
|
tap.x = x;
|
|
750
733
|
tap.y = y;
|
|
751
|
-
}, _checkTapped = (tap) => tap && performance.now() - tap.ts <=
|
|
734
|
+
}, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 300, preventDefault = (ev) => ev.preventDefault();
|
|
752
735
|
let _pressingMouse = false;
|
|
753
736
|
on(_canvas, "mousedown", (ev) => {
|
|
754
737
|
if (ev.button === 0) {
|
|
@@ -827,18 +810,51 @@
|
|
|
827
810
|
});
|
|
828
811
|
}
|
|
829
812
|
if (settings.keyboardEvents) {
|
|
830
|
-
const
|
|
831
|
-
const
|
|
832
|
-
|
|
813
|
+
const toLowerCase = (s) => s.toLowerCase();
|
|
814
|
+
const _keysDown = /* @__PURE__ */ new Set();
|
|
815
|
+
const _keysPress = /* @__PURE__ */ new Set();
|
|
816
|
+
const keyCheck = (keysSet, key) => {
|
|
817
|
+
return !key ? keysSet.size > 0 : keysSet.has(
|
|
818
|
+
"space" === toLowerCase(key) ? " " : toLowerCase(key)
|
|
819
|
+
);
|
|
833
820
|
};
|
|
834
|
-
instance.setvar("iskeydown", iskeydown);
|
|
835
821
|
on(root, "keydown", (event) => {
|
|
836
|
-
|
|
822
|
+
if (!_keysDown.has(toLowerCase(event.key))) {
|
|
823
|
+
_keysDown.add(toLowerCase(event.key));
|
|
824
|
+
_keysPress.add(toLowerCase(event.key));
|
|
825
|
+
}
|
|
837
826
|
});
|
|
838
827
|
on(root, "keyup", (event) => {
|
|
839
|
-
|
|
828
|
+
_keysDown.delete(toLowerCase(event.key));
|
|
840
829
|
});
|
|
841
|
-
on(root, "blur", () =>
|
|
830
|
+
on(root, "blur", () => _keysDown.clear());
|
|
831
|
+
instance.listen("after:draw", () => _keysPress.clear());
|
|
832
|
+
instance.setvar(
|
|
833
|
+
"iskeydown",
|
|
834
|
+
/**
|
|
835
|
+
* Checks if a which key is pressed (down) on the keyboard.
|
|
836
|
+
* Note: use `iskeydown()` to check for any key.
|
|
837
|
+
*
|
|
838
|
+
* @param {string?} key
|
|
839
|
+
* @returns {boolean}
|
|
840
|
+
*/
|
|
841
|
+
(key) => {
|
|
842
|
+
return keyCheck(_keysDown, key);
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
instance.setvar(
|
|
846
|
+
"iskeypressed",
|
|
847
|
+
/**
|
|
848
|
+
* Checks if a which key just got pressed on the keyboard.
|
|
849
|
+
* Note: use `iskeypressed()` to check for any key.
|
|
850
|
+
*
|
|
851
|
+
* @param {string?} key
|
|
852
|
+
* @returns {boolean}
|
|
853
|
+
*/
|
|
854
|
+
(key) => {
|
|
855
|
+
return keyCheck(_keysPress, key);
|
|
856
|
+
}
|
|
857
|
+
);
|
|
842
858
|
}
|
|
843
859
|
if (settings.pauseOnBlur) {
|
|
844
860
|
on(root, "blur", () => {
|
|
@@ -850,7 +866,6 @@
|
|
|
850
866
|
}
|
|
851
867
|
});
|
|
852
868
|
}
|
|
853
|
-
instance.setfps(60);
|
|
854
869
|
instance.emit("init", instance);
|
|
855
870
|
_lastFrameTime = performance.now();
|
|
856
871
|
_rafid = raf(drawFrame);
|
|
@@ -896,12 +911,12 @@
|
|
|
896
911
|
}
|
|
897
912
|
function onResize() {
|
|
898
913
|
const styles = _canvas.style;
|
|
899
|
-
if (
|
|
914
|
+
if (settings.autoscale) {
|
|
900
915
|
if (!styles.display) {
|
|
901
916
|
styles.display = "block";
|
|
902
917
|
styles.margin = "auto";
|
|
903
918
|
}
|
|
904
|
-
_scale =
|
|
919
|
+
_scale = math.min(
|
|
905
920
|
root.innerWidth / instance.WIDTH,
|
|
906
921
|
root.innerHeight / instance.HEIGHT
|
|
907
922
|
);
|
|
@@ -931,11 +946,11 @@
|
|
|
931
946
|
}
|
|
932
947
|
}
|
|
933
948
|
if (_global) {
|
|
934
|
-
if (root.
|
|
935
|
-
throw "global litecanvas
|
|
949
|
+
if (root.ENGINE) {
|
|
950
|
+
throw "two global litecanvas detected";
|
|
936
951
|
}
|
|
937
952
|
Object.assign(root, instance);
|
|
938
|
-
root.
|
|
953
|
+
root.ENGINE = instance;
|
|
939
954
|
}
|
|
940
955
|
setupCanvas();
|
|
941
956
|
if ("loading" === document.readyState) {
|
package/dist/dist.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,
|
|
1
|
+
(()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,r=.1,o=0,s=1,c=0,f=0,d=0,u=0,p=0,g=0,h=0,m=0,E=0,v=1,b=0,w=0,x=0)=>{let T=Math,y=2*T.PI,H=c*=500*y/44100/44100,I=l*=(1-a+2*a*T.random(a=[]))*y/44100,S=0,D=0,k=0,A=1,C=0,X=0,z=0,N=x<0?-1:1,P=y*N*x*2/44100,L=T.cos(P),O=T.sin,Y=O(P)/4,F=1+Y,W=-2*L/F,G=(1-Y)/F,M=(1+N*L)/2/F,R=-(N+L)/F,B=0,U=0,q=0,V=0;for(n=44100*n+9,b*=44100,i*=44100,r*=44100,E*=44100,f*=500*y/85766121e6,h*=y/44100,d*=y/44100,u*=44100,p=44100*p|0,t*=.3*(globalThis.zzfxV||1),N=n+b+i+r+E|0;k<N;a[k++]=z*t)++X%(100*m|0)||(z=o?1<o?2<o?3<o?O(S*S):T.max(T.min(T.tan(S),1),-1):1-(2*S/y%2+2)%2:1-4*T.abs(T.round(S/y)-S/y):O(S),z=(p?1-w+w*O(y*k/p):1)*(z<0?-1:1)*T.abs(z)**s*(k<n?k/n:k<n+b?1-(k-n)/b*(1-v):k<n+b+i?v:k<N-E?(N-k-E)/r*v:0),z=E?z/2+(E>k?0:(k<N-E?1:(N-k)/E)*a[k-E|0]/2/t):z,x&&(z=V=M*B+R*(B=U)+M*(U=z)-G*q-W*(q=V))),S+=(P=(l+=c+=f)*T.cos(h*D++))+P*g*O(k**5),A&&++A>u&&(l+=d,I+=d,A=0),!p||++C%p||(l=I,c=H,A=A||1);(t=e.createBuffer(1,N,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math,i=n.PI,r=2*i,o=requestAnimationFrame,s=[],c=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let f=!1,d=[],u=e.canvas||document.createElement("canvas"),p=e.animate,g=1,h,m=.5,E=1,v,b=1/60,w=0,x,T="sans-serif",y=18,H=Date.now(),I=e.global,S={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},D={settings:Object.assign({},e),colors:a},k={WIDTH:e.width,HEIGHT:e.height||e.width,CANVAS:null,ELAPSED:0,CENTERX:0,CENTERY:0,MOUSEX:-1,MOUSEY:-1,DEFAULT_SFX:[.5,,1675,,.06,.2,1,1.8,,,637,.06],TWO_PI:r,HALF_PI:i/2,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>i/180*e,rad2deg:e=>180/i*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let r=(e-t)/(a-t)*(n-l)+l;return i?k.clamp(r,l,n):r},norm:(e,t,a)=>k.map(e,t,a,0,1),rand:(e=0,t=1)=>(H=(1664525*H+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(k.rand(e,t+1)),seed:e=>null==e?H:H=~~e,cls(e){null==e?h.clearRect(0,0,h.canvas.width,h.canvas.height):k.rectfill(0,0,h.canvas.width,h.canvas.height,e)},rect(e,t,a,l,n,i=null){h.beginPath(),h[i?"roundRect":"rect"](~~e-m,~~t-m,~~a+2*m,~~l+2*m,i),k.stroke(n)},rectfill(e,t,a,l,n,i=null){h.beginPath(),h[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),k.fill(n)},circ(e,t,a,l){h.beginPath(),h.arc(~~e,~~t,~~a,0,r),k.stroke(l)},circfill(e,t,a,l){h.beginPath(),h.arc(~~e,~~t,~~a,0,r),k.fill(l)},line(e,t,a,l,n){h.beginPath();let i=.5*(0!==m&&~~e==~~a),r=.5*(0!==m&&~~t==~~l);h.moveTo(~~e+i,~~t+r),h.lineTo(~~a+i,~~l+r),k.stroke(n)},linewidth(e){h.lineWidth=~~e,m=.5*(~~e%2!=0)},linedash(e,t=0){h.setLineDash(e),h.lineDashOffset=t},text(e,t,a,l=3,n="normal"){h.font=`${n} ${y}px ${T}`,h.fillStyle=k.getcolor(l),h.fillText(a,~~e,~~t)},textfont(e){T=e},textsize(e){y=e},textalign(e,t){e&&(h.textAlign=e),t&&(h.textBaseline=t)},image(e,t,a){h.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,r=h;if(n.width=e*i,n.height=t*i,(h=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(h.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&k.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(h);return h=r,n},ctx:e=>(e&&(h=e),h),push:()=>h.save(),pop:()=>h.restore(),translate:(e,t)=>h.translate(~~e,~~t),scale:(e,t)=>h.scale(e,t||e),rotate:e=>h.rotate(e),alpha(e){h.globalAlpha=k.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){h.fillStyle=k.getcolor(e),t?h.fill(t):h.fill()},stroke(e,t){h.strokeStyle=k.getcolor(e),t?h.stroke(t):h.stroke()},clip(e){h.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||k.DEFAULT_SFX,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},use(e,t={}){f?N(e,t):d.push([e,t])},listen:(e,t)=>(S[e]=S[e]||new Set,S[e].add(t),()=>S[e].delete(t)),emit(e,t,a,l,n){f&&(z("before:"+e,t,a,l,n),z(e,t,a,l,n),z("after:"+e,t,a,l,n))},getcolor:e=>a[~~e%a.length],setvar(e,t){k[e]=t,I&&(l[e]=t)},resize(e,t){k.setvar("WIDTH",u.width=e),k.setvar("HEIGHT",u.height=t),k.setvar("CENTERX",k.WIDTH/2),k.setvar("CENTERY",k.HEIGHT/2),X()},timescale(e){E=e},setfps(e){b=1/~~e},quit(){for(let e of(cancelAnimationFrame(x),k.emit("quit"),S=[],s))e();if(I){for(let e in k)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))k[e]=n[e];function A(){f=!0;let t=e.loop?e.loop:l;for(let e in S)t[e]&&k.listen(e,t[e]);for(let[e,t]of d)N(e,t);if(e.autoscale&&c(l,"resize",X),e.tapEvents){let e=(e,t)=>[(e-u.offsetLeft)/g,(t-u.offsetTop)/g],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),o=!1;c(u,"mousedown",t=>{if(0===t.button){r(t);let[l,n]=e(t.pageX,t.pageY);k.emit("tap",l,n,0),a(0,l,n),o=!0}}),c(u,"mouseup",a=>{if(0===a.button){r(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&k.emit("tapped",l.startX,l.startY,0),k.emit("untap",n,s,0),t.delete(0),o=!1}}),c(u,"mousemove",t=>{r(t);let[a,l]=e(t.pageX,t.pageY);k.setvar("MOUSEX",a),k.setvar("MOUSEY",l),o&&(k.emit("tapping",a,l,0),n(0,a,l))}),c(u,"touchstart",t=>{for(let l of(r(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);k.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),c(u,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);k.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&k.emit("tapped",l.startX,l.startY,e),k.emit("untap",l.x,l.y,e),t.delete(e))};c(u,"touchend",s),c(u,"touchcancel",s),c(l,"blur",()=>{for(let[e,a]of(o=!1,t))k.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=e=>e.toLowerCase(),t=new Set,a=new Set,n=(t,a)=>a?t.has("space"===e(a)?" ":e(a)):t.size>0;c(l,"keydown",l=>{t.has(e(l.key))||(t.add(e(l.key)),a.add(e(l.key)))}),c(l,"keyup",a=>{t.delete(e(a.key))}),c(l,"blur",()=>t.clear()),k.listen("after:draw",()=>a.clear()),k.setvar("iskeydown",e=>n(t,e)),k.setvar("iskeypressed",e=>n(a,e))}e.pauseOnBlur&&(c(l,"blur",()=>{x=cancelAnimationFrame(x)}),c(l,"focus",()=>{x||(x=o(C))})),k.emit("init",k),v=performance.now(),x=o(C)}function C(e){p&&(x=o(C));let t=0,a=(e-v)/1e3;if(v=e,a>30*b);else for(w+=a,p||(w=b);w>=b;w-=b)k.emit("update",b*E),k.setvar("ELAPSED",k.ELAPSED+b*E),t++;(t||!p)&&(k.textalign("start","top"),k.emit("draw"))}function X(){let t=u.style;e.autoscale&&(t.display||(t.display="block",t.margin="auto"),g=n.min(l.innerWidth/k.WIDTH,l.innerHeight/k.HEIGHT),g=(e.pixelart?~~g:g)||1,t.width=k.WIDTH*g+"px",t.height=k.HEIGHT*g+"px"),(!e.antialias||e.pixelart)&&(h.imageSmoothingEnabled=!1,t.imageRendering="pixelated"),k.emit("resized",g),p||o(C)}function z(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function N(e,t){let a=e(k,D,t);for(let e in a)k.setvar(e,a[e])}if(I){if(l.ENGINE)throw"two global litecanvas detected";Object.assign(l,k),l.ENGINE=k}return u="string"==typeof u?document.querySelector(u):u,k.setvar("CANVAS",u),h=u.getContext("2d"),c(u,"click",()=>l.focus()),u.style="",k.WIDTH||(k.WIDTH=l.innerWidth,k.HEIGHT=l.innerHeight),k.resize(k.WIDTH,k.HEIGHT,!1),u.parentNode||document.body.appendChild(u),"loading"===document.readyState?c(l,"DOMContentLoaded",()=>o(A)):o(A),k}})();
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litecanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.0",
|
|
4
4
|
"description": "Lightweight HTML5 canvas game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Luiz Bills <luizbills@pm.me>",
|
|
7
7
|
"contributors": [],
|
|
8
8
|
"devDependencies": {
|
|
9
|
-
"@swc/core": "^1.11.
|
|
9
|
+
"@swc/core": "^1.11.9",
|
|
10
10
|
"ava": "^6.2.0",
|
|
11
11
|
"esbuild": "^0.25.1",
|
|
12
12
|
"gzip-size": "^7.0.0",
|
package/src/index.js
CHANGED
|
@@ -11,7 +11,8 @@ import './types.js'
|
|
|
11
11
|
*/
|
|
12
12
|
export default function litecanvas(settings = {}) {
|
|
13
13
|
const root = globalThis,
|
|
14
|
-
|
|
14
|
+
math = Math,
|
|
15
|
+
PI = math.PI,
|
|
15
16
|
TWO_PI = PI * 2,
|
|
16
17
|
raf = requestAnimationFrame,
|
|
17
18
|
/** @type {Function[]} */
|
|
@@ -50,8 +51,6 @@ export default function litecanvas(settings = {}) {
|
|
|
50
51
|
/** @type {HTMLCanvasElement|string} _canvas */
|
|
51
52
|
_canvas = settings.canvas || document.createElement('canvas'),
|
|
52
53
|
/** @type {boolean} */
|
|
53
|
-
_autoscale = settings.autoscale,
|
|
54
|
-
/** @type {boolean} */
|
|
55
54
|
_animated = settings.animate,
|
|
56
55
|
/** @type {number} */
|
|
57
56
|
_scale = 1,
|
|
@@ -64,7 +63,7 @@ export default function litecanvas(settings = {}) {
|
|
|
64
63
|
/** @type {number} */
|
|
65
64
|
_lastFrameTime,
|
|
66
65
|
/** @type {number} */
|
|
67
|
-
_deltaTime,
|
|
66
|
+
_deltaTime = 1 / 60,
|
|
68
67
|
/** @type {number} */
|
|
69
68
|
_accumulated = 0,
|
|
70
69
|
/** @type {number} */
|
|
@@ -72,7 +71,7 @@ export default function litecanvas(settings = {}) {
|
|
|
72
71
|
/** @type {string} */
|
|
73
72
|
_fontFamily = 'sans-serif',
|
|
74
73
|
/** @type {number} */
|
|
75
|
-
_fontSize =
|
|
74
|
+
_fontSize = 18,
|
|
76
75
|
/** @type {number} */
|
|
77
76
|
_rng_seed = Date.now(),
|
|
78
77
|
/** @type {boolean} */
|
|
@@ -191,6 +190,26 @@ export default function litecanvas(settings = {}) {
|
|
|
191
190
|
return (180 / PI) * rads
|
|
192
191
|
},
|
|
193
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Calculates the integer closest to a number and optional precision.
|
|
195
|
+
*
|
|
196
|
+
* @param {number} n number to round.
|
|
197
|
+
* @param {number} [precision] number of decimal digits to round to, default is 0.
|
|
198
|
+
* @returns {number} rounded number.
|
|
199
|
+
*/
|
|
200
|
+
round: (n, precision = 0) => {
|
|
201
|
+
DEV: assert(isFinite(n), 'round: 1st param must be a number')
|
|
202
|
+
DEV: assert(
|
|
203
|
+
null === precision || (isFinite(precision) && precision >= 0),
|
|
204
|
+
'round: 2nd param must be a positive number or zero'
|
|
205
|
+
)
|
|
206
|
+
if (!precision) {
|
|
207
|
+
return math.round(n)
|
|
208
|
+
}
|
|
209
|
+
const multiplier = 10 ** precision
|
|
210
|
+
return math.round(n * multiplier) / multiplier
|
|
211
|
+
},
|
|
212
|
+
|
|
194
213
|
/**
|
|
195
214
|
* Constrains a number between `min` and `max`.
|
|
196
215
|
*
|
|
@@ -234,7 +253,7 @@ export default function litecanvas(settings = {}) {
|
|
|
234
253
|
'randi: the 2nd param must be not equal to the 3rd param'
|
|
235
254
|
)
|
|
236
255
|
|
|
237
|
-
return value - (max - min) *
|
|
256
|
+
return value - (max - min) * math.floor((value - min) / (max - min))
|
|
238
257
|
},
|
|
239
258
|
|
|
240
259
|
/**
|
|
@@ -319,7 +338,7 @@ export default function litecanvas(settings = {}) {
|
|
|
319
338
|
'randi: the 1st param must be less than the 2nd param'
|
|
320
339
|
)
|
|
321
340
|
|
|
322
|
-
return
|
|
341
|
+
return math.floor(instance.rand(min, max + 1))
|
|
323
342
|
},
|
|
324
343
|
|
|
325
344
|
/**
|
|
@@ -342,12 +361,12 @@ export default function litecanvas(settings = {}) {
|
|
|
342
361
|
/**
|
|
343
362
|
* Clear the game screen with an optional color
|
|
344
363
|
*
|
|
345
|
-
* @param {number?} color The background color (index) or null (for transparent)
|
|
364
|
+
* @param {number?} color The background color (index) or null/undefined (for transparent)
|
|
346
365
|
*/
|
|
347
366
|
cls(color) {
|
|
348
367
|
DEV: assert(
|
|
349
368
|
null == color || (isFinite(color) && color >= 0),
|
|
350
|
-
'cls: 1st param must be a positive number or zero or
|
|
369
|
+
'cls: 1st param must be a positive number or zero or undefined'
|
|
351
370
|
)
|
|
352
371
|
|
|
353
372
|
if (null == color) {
|
|
@@ -955,58 +974,6 @@ export default function litecanvas(settings = {}) {
|
|
|
955
974
|
root.zzfxV = value
|
|
956
975
|
},
|
|
957
976
|
|
|
958
|
-
/** UTILS API */
|
|
959
|
-
/**
|
|
960
|
-
* Check a collision between two rectangles
|
|
961
|
-
*
|
|
962
|
-
* @param {number} x1 first rectangle position X
|
|
963
|
-
* @param {number} y1 first rectangle position Y
|
|
964
|
-
* @param {number} w1 first rectangle width
|
|
965
|
-
* @param {number} h1 first rectangle height
|
|
966
|
-
* @param {number} x2 second rectangle position X
|
|
967
|
-
* @param {number} y2 second rectangle position Y
|
|
968
|
-
* @param {number} w2 second rectangle width
|
|
969
|
-
* @param {number} h2 second rectangle height
|
|
970
|
-
* @returns {boolean}
|
|
971
|
-
*/
|
|
972
|
-
colrect: (x1, y1, w1, h1, x2, y2, w2, h2) => {
|
|
973
|
-
DEV: assert(isFinite(x1), 'colrect: 1st param must be a number')
|
|
974
|
-
DEV: assert(isFinite(y1), 'colrect: 2nd param must be a number')
|
|
975
|
-
DEV: assert(isFinite(w1), 'colrect: 3rd param must be a number')
|
|
976
|
-
DEV: assert(isFinite(h1), 'colrect: 4th param must be a number')
|
|
977
|
-
DEV: assert(isFinite(x2), 'colrect: 5th param must be a number')
|
|
978
|
-
DEV: assert(isFinite(y2), 'colrect: 6th param must be a number')
|
|
979
|
-
DEV: assert(isFinite(w2), 'colrect: 7th param must be a number')
|
|
980
|
-
DEV: assert(isFinite(h2), 'colrect: 8th param must be a number')
|
|
981
|
-
|
|
982
|
-
return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2
|
|
983
|
-
},
|
|
984
|
-
|
|
985
|
-
/**
|
|
986
|
-
* Check a collision between two circles
|
|
987
|
-
*
|
|
988
|
-
* @param {number} x1 first circle position X
|
|
989
|
-
* @param {number} y1 first circle position Y
|
|
990
|
-
* @param {number} r1 first circle position radius
|
|
991
|
-
* @param {number} x2 second circle position X
|
|
992
|
-
* @param {number} y2 second circle position Y
|
|
993
|
-
* @param {number} r2 second circle position radius
|
|
994
|
-
* @returns {boolean}
|
|
995
|
-
*/
|
|
996
|
-
colcirc: (x1, y1, r1, x2, y2, r2) => {
|
|
997
|
-
DEV: assert(isFinite(x1), 'colcirc: 1st param must be a number')
|
|
998
|
-
DEV: assert(isFinite(y1), 'colcirc: 2nd param must be a number')
|
|
999
|
-
DEV: assert(isFinite(r1), 'colcirc: 3rd param must be a number')
|
|
1000
|
-
DEV: assert(isFinite(x2), 'colcirc: 4th param must be a number')
|
|
1001
|
-
DEV: assert(isFinite(y2), 'colcirc: 5th param must be a number')
|
|
1002
|
-
DEV: assert(isFinite(r2), 'colcirc: 6th param must be a number')
|
|
1003
|
-
|
|
1004
|
-
return (
|
|
1005
|
-
(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) <=
|
|
1006
|
-
(r1 + r2) * (r1 + r2)
|
|
1007
|
-
)
|
|
1008
|
-
},
|
|
1009
|
-
|
|
1010
977
|
/** PLUGINS API */
|
|
1011
978
|
/**
|
|
1012
979
|
* Prepares a plugin to be loaded
|
|
@@ -1168,25 +1135,34 @@ export default function litecanvas(settings = {}) {
|
|
|
1168
1135
|
* Stops the litecanvas instance and remove all event listeners.
|
|
1169
1136
|
*/
|
|
1170
1137
|
quit() {
|
|
1138
|
+
// stop the renderer
|
|
1139
|
+
cancelAnimationFrame(_rafid)
|
|
1140
|
+
|
|
1141
|
+
// emit "quit" event to manual clean ups
|
|
1171
1142
|
instance.emit('quit')
|
|
1143
|
+
|
|
1144
|
+
// clear all engine events
|
|
1145
|
+
_events = []
|
|
1146
|
+
|
|
1147
|
+
// clear all browser events
|
|
1172
1148
|
for (const removeListener of _browserEventListeners) {
|
|
1173
1149
|
removeListener()
|
|
1174
1150
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1151
|
+
|
|
1152
|
+
// maybe clear global context
|
|
1177
1153
|
if (_global) {
|
|
1178
1154
|
for (const key in instance) {
|
|
1179
1155
|
delete root[key]
|
|
1180
1156
|
}
|
|
1181
|
-
delete root.
|
|
1157
|
+
delete root.ENGINE
|
|
1182
1158
|
}
|
|
1183
1159
|
},
|
|
1184
1160
|
}
|
|
1185
1161
|
|
|
1186
1162
|
// prettier-ignore
|
|
1187
|
-
for (const k of 'PI,sin,cos,atan2,hypot,tan,abs,ceil,
|
|
1163
|
+
for (const k of 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp'.split(',')) {
|
|
1188
1164
|
// import native Math functions
|
|
1189
|
-
instance[k] =
|
|
1165
|
+
instance[k] = math[k]
|
|
1190
1166
|
}
|
|
1191
1167
|
|
|
1192
1168
|
function init() {
|
|
@@ -1204,7 +1180,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1204
1180
|
}
|
|
1205
1181
|
|
|
1206
1182
|
// listen window resize event when "autoscale" is enabled
|
|
1207
|
-
if (
|
|
1183
|
+
if (settings.autoscale) {
|
|
1208
1184
|
on(root, 'resize', onResize)
|
|
1209
1185
|
}
|
|
1210
1186
|
|
|
@@ -1233,7 +1209,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1233
1209
|
tap.y = y
|
|
1234
1210
|
},
|
|
1235
1211
|
_checkTapped = (tap) =>
|
|
1236
|
-
tap && performance.now() - tap.ts <=
|
|
1212
|
+
tap && performance.now() - tap.ts <= 300,
|
|
1237
1213
|
preventDefault = (ev) => ev.preventDefault()
|
|
1238
1214
|
|
|
1239
1215
|
let _pressingMouse = false
|
|
@@ -1330,40 +1306,76 @@ export default function litecanvas(settings = {}) {
|
|
|
1330
1306
|
}
|
|
1331
1307
|
|
|
1332
1308
|
if (settings.keyboardEvents) {
|
|
1309
|
+
const toLowerCase = (s) => s.toLowerCase()
|
|
1310
|
+
|
|
1333
1311
|
/** @type {Set<string>} */
|
|
1334
|
-
const
|
|
1312
|
+
const _keysDown = new Set()
|
|
1313
|
+
|
|
1314
|
+
/** @type {Set<string>} */
|
|
1315
|
+
const _keysPress = new Set()
|
|
1335
1316
|
|
|
1336
1317
|
/**
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
* - to check the space key use `iskeydown(" ")`.
|
|
1340
|
-
* - you can check if any key is pressed using `iskeydown("any")`.
|
|
1341
|
-
*
|
|
1342
|
-
* @param {string} key
|
|
1318
|
+
* @param {Set<string>} keysSet
|
|
1319
|
+
* @param {string?} key
|
|
1343
1320
|
* @returns {boolean}
|
|
1344
1321
|
*/
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
return 'any' === key
|
|
1352
|
-
? _keys.size > 0
|
|
1353
|
-
: _keys.has(key.toLowerCase())
|
|
1322
|
+
const keyCheck = (keysSet, key) => {
|
|
1323
|
+
return !key
|
|
1324
|
+
? keysSet.size > 0
|
|
1325
|
+
: keysSet.has(
|
|
1326
|
+
'space' === toLowerCase(key) ? ' ' : toLowerCase(key)
|
|
1327
|
+
)
|
|
1354
1328
|
}
|
|
1355
1329
|
|
|
1356
|
-
instance.setvar('iskeydown', iskeydown)
|
|
1357
|
-
|
|
1358
1330
|
on(root, 'keydown', (/** @type {KeyboardEvent} */ event) => {
|
|
1359
|
-
|
|
1331
|
+
if (!_keysDown.has(toLowerCase(event.key))) {
|
|
1332
|
+
_keysDown.add(toLowerCase(event.key))
|
|
1333
|
+
_keysPress.add(toLowerCase(event.key))
|
|
1334
|
+
}
|
|
1360
1335
|
})
|
|
1361
1336
|
|
|
1362
1337
|
on(root, 'keyup', (/** @type {KeyboardEvent} */ event) => {
|
|
1363
|
-
|
|
1338
|
+
_keysDown.delete(toLowerCase(event.key))
|
|
1364
1339
|
})
|
|
1365
1340
|
|
|
1366
|
-
on(root, 'blur', () =>
|
|
1341
|
+
on(root, 'blur', () => _keysDown.clear())
|
|
1342
|
+
instance.listen('after:draw', () => _keysPress.clear())
|
|
1343
|
+
|
|
1344
|
+
instance.setvar(
|
|
1345
|
+
'iskeydown',
|
|
1346
|
+
/**
|
|
1347
|
+
* Checks if a which key is pressed (down) on the keyboard.
|
|
1348
|
+
* Note: use `iskeydown()` to check for any key.
|
|
1349
|
+
*
|
|
1350
|
+
* @param {string?} key
|
|
1351
|
+
* @returns {boolean}
|
|
1352
|
+
*/
|
|
1353
|
+
(key) => {
|
|
1354
|
+
DEV: assert(
|
|
1355
|
+
null == key || 'string' === typeof key,
|
|
1356
|
+
'iskeydown: 1st param must be a string or undefined'
|
|
1357
|
+
)
|
|
1358
|
+
return keyCheck(_keysDown, key)
|
|
1359
|
+
}
|
|
1360
|
+
)
|
|
1361
|
+
|
|
1362
|
+
instance.setvar(
|
|
1363
|
+
'iskeypressed',
|
|
1364
|
+
/**
|
|
1365
|
+
* Checks if a which key just got pressed on the keyboard.
|
|
1366
|
+
* Note: use `iskeypressed()` to check for any key.
|
|
1367
|
+
*
|
|
1368
|
+
* @param {string?} key
|
|
1369
|
+
* @returns {boolean}
|
|
1370
|
+
*/
|
|
1371
|
+
(key) => {
|
|
1372
|
+
DEV: assert(
|
|
1373
|
+
null == key || 'string' === typeof key,
|
|
1374
|
+
'iskeypressed: 1st param must be a string or undefined'
|
|
1375
|
+
)
|
|
1376
|
+
return keyCheck(_keysPress, key)
|
|
1377
|
+
}
|
|
1378
|
+
)
|
|
1367
1379
|
}
|
|
1368
1380
|
|
|
1369
1381
|
// listen browser focus/blur events and pause the update/draw loop
|
|
@@ -1379,8 +1391,6 @@ export default function litecanvas(settings = {}) {
|
|
|
1379
1391
|
})
|
|
1380
1392
|
}
|
|
1381
1393
|
|
|
1382
|
-
instance.setfps(60)
|
|
1383
|
-
|
|
1384
1394
|
// start the game loop
|
|
1385
1395
|
instance.emit('init', instance)
|
|
1386
1396
|
|
|
@@ -1402,7 +1412,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1402
1412
|
_lastFrameTime = now
|
|
1403
1413
|
|
|
1404
1414
|
if (frameTime > _deltaTime * 30) {
|
|
1405
|
-
console.
|
|
1415
|
+
console.warn('skipping too long frame')
|
|
1406
1416
|
} else {
|
|
1407
1417
|
_accumulated += frameTime
|
|
1408
1418
|
|
|
@@ -1472,13 +1482,13 @@ export default function litecanvas(settings = {}) {
|
|
|
1472
1482
|
function onResize() {
|
|
1473
1483
|
const styles = _canvas.style
|
|
1474
1484
|
|
|
1475
|
-
if (
|
|
1485
|
+
if (settings.autoscale) {
|
|
1476
1486
|
if (!styles.display) {
|
|
1477
1487
|
styles.display = 'block'
|
|
1478
1488
|
styles.margin = 'auto'
|
|
1479
1489
|
}
|
|
1480
1490
|
|
|
1481
|
-
_scale =
|
|
1491
|
+
_scale = math.min(
|
|
1482
1492
|
root.innerWidth / instance.WIDTH,
|
|
1483
1493
|
root.innerHeight / instance.HEIGHT
|
|
1484
1494
|
)
|
|
@@ -1526,11 +1536,11 @@ export default function litecanvas(settings = {}) {
|
|
|
1526
1536
|
}
|
|
1527
1537
|
|
|
1528
1538
|
if (_global) {
|
|
1529
|
-
if (root.
|
|
1530
|
-
throw 'global litecanvas
|
|
1539
|
+
if (root.ENGINE) {
|
|
1540
|
+
throw 'two global litecanvas detected'
|
|
1531
1541
|
}
|
|
1532
1542
|
Object.assign(root, instance)
|
|
1533
|
-
root.
|
|
1543
|
+
root.ENGINE = instance
|
|
1534
1544
|
}
|
|
1535
1545
|
|
|
1536
1546
|
setupCanvas()
|
package/types/index.d.ts
CHANGED
|
@@ -487,10 +487,8 @@ declare global {
|
|
|
487
487
|
|
|
488
488
|
/** UTILS API */
|
|
489
489
|
/**
|
|
490
|
-
* Checks if a key is
|
|
491
|
-
*
|
|
492
|
-
* - to check the space key use `iskeydown(" ")`.
|
|
493
|
-
* - you can check if any key is pressed using `iskeydown("any")`.
|
|
490
|
+
* Checks if a which key is pressed on the keyboard.
|
|
491
|
+
* Note: use `iskeydown("any")` to check for any key pressed.
|
|
494
492
|
*
|
|
495
493
|
* @param key
|
|
496
494
|
* @returns `true` if the which key is down
|
package/types/types.d.ts
CHANGED
|
@@ -465,10 +465,8 @@ type LitecanvasInstance = {
|
|
|
465
465
|
|
|
466
466
|
/** UTILS API */
|
|
467
467
|
/**
|
|
468
|
-
* Checks if a key is
|
|
469
|
-
*
|
|
470
|
-
* - to check the space key use `iskeydown(" ")`.
|
|
471
|
-
* - you can check if any key is pressed using `iskeydown("any")`.
|
|
468
|
+
* Checks if a which key is pressed on the keyboard.
|
|
469
|
+
* Note: use `iskeydown("any")` to check for any key pressed.
|
|
472
470
|
*
|
|
473
471
|
* @param key
|
|
474
472
|
* @returns `true` if the which key is down
|