litecanvas 0.96.0 → 0.97.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/README.md +3 -7
- package/dist/dist.dev.js +70 -45
- package/dist/dist.js +55 -43
- package/dist/dist.min.js +1 -1
- package/package.json +4 -4
- package/src/index.js +76 -54
- package/src/version.js +1 -1
- package/types/global.d.ts +29 -4
- package/types/types.d.ts +29 -17
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Litecanvas
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/litecanvas/)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
10
|
Litecanvas is a lightweight HTML5 canvas 2D engine suitable for small web games, prototypes, game jams, animations, creative coding, learning game programming and game design, etc.
|
|
@@ -32,14 +32,10 @@ Litecanvas is a lightweight HTML5 canvas 2D engine suitable for small web games,
|
|
|
32
32
|
|
|
33
33
|
## Getting Started
|
|
34
34
|
|
|
35
|
-
You can try our [online playground](https://litecanvas.github.io) or install the
|
|
35
|
+
You can try our [online playground](https://litecanvas.github.io) or install the NPM package:
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
|
|
39
|
-
npx tiged litecanvas/template my-game
|
|
40
|
-
cd my-game
|
|
41
|
-
npm install
|
|
42
|
-
npm run dev
|
|
38
|
+
npm i litecanvas
|
|
43
39
|
```
|
|
44
40
|
|
|
45
41
|
or just use add a `<script>` tag with our CDN link:
|
package/dist/dist.dev.js
CHANGED
|
@@ -32,27 +32,25 @@
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// src/version.js
|
|
35
|
-
var version = "0.
|
|
35
|
+
var version = "0.97.0";
|
|
36
36
|
|
|
37
37
|
// src/index.js
|
|
38
38
|
function litecanvas(settings = {}) {
|
|
39
39
|
const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
40
40
|
elem.addEventListener(evt, callback, false);
|
|
41
41
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
|
|
42
|
-
}, preventDefault = (ev) => ev.preventDefault(), beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
|
|
42
|
+
}, lowerCase = (str) => str.toLowerCase(), preventDefault = (ev) => ev.preventDefault(), beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
|
|
43
43
|
width: null,
|
|
44
44
|
height: null,
|
|
45
45
|
autoscale: true,
|
|
46
|
-
pixelart: true,
|
|
47
46
|
canvas: null,
|
|
48
47
|
global: true,
|
|
49
48
|
loop: null,
|
|
50
49
|
tapEvents: true,
|
|
51
|
-
keyboardEvents: true
|
|
52
|
-
animate: true
|
|
50
|
+
keyboardEvents: true
|
|
53
51
|
};
|
|
54
52
|
settings = Object.assign(defaults, settings);
|
|
55
|
-
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors
|
|
53
|
+
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
|
|
56
54
|
const instance = {
|
|
57
55
|
/** @type {number} */
|
|
58
56
|
W: 0,
|
|
@@ -626,19 +624,44 @@
|
|
|
626
624
|
DEV: assert(isNumber(y), "[litecanvas] image() 2nd param must be a number");
|
|
627
625
|
_ctx.drawImage(source, ~~x, ~~y);
|
|
628
626
|
},
|
|
627
|
+
/**
|
|
628
|
+
* Draw a sprite pxiel by pixel represented by a string. Each pixel must be a base 36 number (0-9 or a-z) or a dot.
|
|
629
|
+
*
|
|
630
|
+
* @param {number} x
|
|
631
|
+
* @param {number} y
|
|
632
|
+
* @param {number} width
|
|
633
|
+
* @param {number} height
|
|
634
|
+
* @param {string} pixels
|
|
635
|
+
*/
|
|
636
|
+
spr(x, y, width, height, pixels) {
|
|
637
|
+
DEV: assert(isNumber(x), "[litecanvas] spr() 1st param must be a number");
|
|
638
|
+
DEV: assert(isNumber(y), "[litecanvas] spr() 2nd param must be a number");
|
|
639
|
+
DEV: assert(isNumber(width), "[litecanvas] spr() 3rd param must be a number");
|
|
640
|
+
DEV: assert(isNumber(height), "[litecanvas] spr() 4th param must be a number");
|
|
641
|
+
DEV: assert("string" === typeof pixels, "[litecanvas] spr() 5th param must be a string");
|
|
642
|
+
const chars = pixels.replace(/\s/g, "");
|
|
643
|
+
for (let gridx = 0; gridx < width; gridx++) {
|
|
644
|
+
for (let gridy = 0; gridy < height; gridy++) {
|
|
645
|
+
const char = chars[height * gridy + gridx] || ".";
|
|
646
|
+
if (char !== ".") {
|
|
647
|
+
instance.rectfill(x + gridx, y + gridy, 1, 1, parseInt(char, 16) || 0);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
},
|
|
629
652
|
/**
|
|
630
653
|
* Draw in an OffscreenCanvas and returns its image.
|
|
631
654
|
*
|
|
632
655
|
* @param {number} width
|
|
633
656
|
* @param {number} height
|
|
634
|
-
* @param {
|
|
657
|
+
* @param {drawCallback} callback
|
|
635
658
|
* @param {object} [options]
|
|
636
659
|
* @param {number} [options.scale=1]
|
|
637
660
|
* @param {OffscreenCanvas} [options.canvas]
|
|
638
661
|
* @returns {ImageBitmap}
|
|
639
662
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
|
|
640
663
|
*/
|
|
641
|
-
paint(width, height,
|
|
664
|
+
paint(width, height, callback, options = {}) {
|
|
642
665
|
DEV: assert(
|
|
643
666
|
isNumber(width) && width >= 1,
|
|
644
667
|
"[litecanvas] paint() 1st param must be a positive number"
|
|
@@ -648,7 +671,7 @@
|
|
|
648
671
|
"[litecanvas] paint() 2nd param must be a positive number"
|
|
649
672
|
);
|
|
650
673
|
DEV: assert(
|
|
651
|
-
"function" === typeof
|
|
674
|
+
"function" === typeof callback,
|
|
652
675
|
"[litecanvas] paint() 3rd param must be a function or array"
|
|
653
676
|
);
|
|
654
677
|
DEV: assert(
|
|
@@ -659,28 +682,13 @@
|
|
|
659
682
|
options && null == options.canvas || options.canvas instanceof OffscreenCanvas,
|
|
660
683
|
"[litecanvas] paint() 4th param (options.canvas) must be an OffscreenCanvas"
|
|
661
684
|
);
|
|
662
|
-
const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1,
|
|
685
|
+
const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1, currentContext = _ctx;
|
|
663
686
|
canvas.width = width * scale;
|
|
664
687
|
canvas.height = height * scale;
|
|
665
688
|
_ctx = canvas.getContext("2d");
|
|
666
689
|
_ctx.scale(scale, scale);
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
_ctx.imageSmoothingEnabled = false;
|
|
670
|
-
for (const str of drawing) {
|
|
671
|
-
for (const color of str) {
|
|
672
|
-
if (" " !== color && "." !== color) {
|
|
673
|
-
instance.rectfill(x, y, 1, 1, parseInt(color, 16));
|
|
674
|
-
}
|
|
675
|
-
x++;
|
|
676
|
-
}
|
|
677
|
-
y++;
|
|
678
|
-
x = 0;
|
|
679
|
-
}
|
|
680
|
-
} else {
|
|
681
|
-
drawing(_ctx);
|
|
682
|
-
}
|
|
683
|
-
_ctx = contextOriginal;
|
|
690
|
+
callback(_ctx);
|
|
691
|
+
_ctx = currentContext;
|
|
684
692
|
return canvas.transferToImageBitmap();
|
|
685
693
|
},
|
|
686
694
|
/** ADVANCED GRAPHICS API */
|
|
@@ -882,7 +890,7 @@
|
|
|
882
890
|
"function" === typeof callback,
|
|
883
891
|
"[litecanvas] listen() 2nd param must be a function"
|
|
884
892
|
);
|
|
885
|
-
eventName = eventName
|
|
893
|
+
eventName = lowerCase(eventName);
|
|
886
894
|
_eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
|
|
887
895
|
_eventListeners[eventName].add(callback);
|
|
888
896
|
return () => _eventListeners && _eventListeners[eventName].delete(callback);
|
|
@@ -902,14 +910,14 @@
|
|
|
902
910
|
"[litecanvas] emit() 1st param must be a string"
|
|
903
911
|
);
|
|
904
912
|
if (_initialized) {
|
|
905
|
-
eventName = eventName
|
|
913
|
+
eventName = lowerCase(eventName);
|
|
906
914
|
triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
|
|
907
915
|
triggerEvent(eventName, arg1, arg2, arg3, arg4);
|
|
908
916
|
triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
|
|
909
917
|
}
|
|
910
918
|
},
|
|
911
919
|
/**
|
|
912
|
-
* Set or reset the color palette
|
|
920
|
+
* Set or reset the color palette.
|
|
913
921
|
*
|
|
914
922
|
* @param {string[]} [colors]
|
|
915
923
|
*/
|
|
@@ -919,6 +927,31 @@
|
|
|
919
927
|
"[litecanvas] pal() 1st param must be a array of strings"
|
|
920
928
|
);
|
|
921
929
|
_colors = colors;
|
|
930
|
+
_currentPalette = [...colors];
|
|
931
|
+
},
|
|
932
|
+
/**
|
|
933
|
+
* Swap two colors of the current palette.
|
|
934
|
+
*
|
|
935
|
+
* If called without arguments, reset the current palette.
|
|
936
|
+
*
|
|
937
|
+
* @param {number?} a
|
|
938
|
+
* @param {number?} b
|
|
939
|
+
*/
|
|
940
|
+
palc(a, b) {
|
|
941
|
+
DEV: assert(
|
|
942
|
+
null == a || isNumber(a) && a >= 0,
|
|
943
|
+
"[litecanvas] palc() 1st param must be a positive number"
|
|
944
|
+
);
|
|
945
|
+
DEV: assert(
|
|
946
|
+
isNumber(a) ? isNumber(b) && b >= 0 : null == b,
|
|
947
|
+
"[litecanvas] palc() 2nd param must be a positive number"
|
|
948
|
+
);
|
|
949
|
+
if (a == null) {
|
|
950
|
+
_colors = [..._currentPalette];
|
|
951
|
+
} else {
|
|
952
|
+
;
|
|
953
|
+
[_colors[a], _colors[b]] = [_colors[b], _colors[a]];
|
|
954
|
+
}
|
|
922
955
|
},
|
|
923
956
|
/**
|
|
924
957
|
* Define or update a instance property.
|
|
@@ -1230,12 +1263,12 @@
|
|
|
1230
1263
|
const _keysDown = /* @__PURE__ */ new Set();
|
|
1231
1264
|
const _keysPress = /* @__PURE__ */ new Set();
|
|
1232
1265
|
const keyCheck = (keySet, key = "") => {
|
|
1233
|
-
key = key
|
|
1266
|
+
key = lowerCase(key);
|
|
1234
1267
|
return !key ? keySet.size > 0 : keySet.has("space" === key ? " " : key);
|
|
1235
1268
|
};
|
|
1236
1269
|
let _lastKey = "";
|
|
1237
1270
|
on(root, "keydown", (event) => {
|
|
1238
|
-
const key = event.key
|
|
1271
|
+
const key = lowerCase(event.key);
|
|
1239
1272
|
if (!_keysDown.has(key)) {
|
|
1240
1273
|
_keysDown.add(key);
|
|
1241
1274
|
_keysPress.add(key);
|
|
@@ -1243,7 +1276,7 @@
|
|
|
1243
1276
|
}
|
|
1244
1277
|
});
|
|
1245
1278
|
on(root, "keyup", (event) => {
|
|
1246
|
-
_keysDown.delete(event.key
|
|
1279
|
+
_keysDown.delete(lowerCase(event.key));
|
|
1247
1280
|
});
|
|
1248
1281
|
on(root, "blur", () => _keysDown.clear());
|
|
1249
1282
|
instance.listen("after:update", () => _keysPress.clear());
|
|
@@ -1288,9 +1321,7 @@
|
|
|
1288
1321
|
instance.resume();
|
|
1289
1322
|
}
|
|
1290
1323
|
function drawFrame() {
|
|
1291
|
-
|
|
1292
|
-
return instance.emit("draw", _ctx);
|
|
1293
|
-
}
|
|
1324
|
+
_rafid = raf(drawFrame);
|
|
1294
1325
|
let now = Date.now();
|
|
1295
1326
|
let updated = 0;
|
|
1296
1327
|
let frameTime = now - _lastFrameTime;
|
|
@@ -1312,7 +1343,6 @@
|
|
|
1312
1343
|
);
|
|
1313
1344
|
}
|
|
1314
1345
|
}
|
|
1315
|
-
_rafid = raf(drawFrame);
|
|
1316
1346
|
}
|
|
1317
1347
|
function setupCanvas() {
|
|
1318
1348
|
if ("string" === typeof settings.canvas) {
|
|
@@ -1331,11 +1361,11 @@
|
|
|
1331
1361
|
);
|
|
1332
1362
|
_ctx = _canvas.getContext("2d");
|
|
1333
1363
|
on(_canvas, "click", () => focus());
|
|
1334
|
-
_canvas.style = "";
|
|
1335
1364
|
resizeCanvas();
|
|
1336
1365
|
if (!_canvas.parentNode) {
|
|
1337
1366
|
document.body.appendChild(_canvas);
|
|
1338
1367
|
}
|
|
1368
|
+
_canvas.style.imageRendering = "pixelated";
|
|
1339
1369
|
_canvas.oncontextmenu = () => false;
|
|
1340
1370
|
}
|
|
1341
1371
|
function resizeCanvas() {
|
|
@@ -1367,15 +1397,9 @@
|
|
|
1367
1397
|
_canvas.style.width = width * _scale + "px";
|
|
1368
1398
|
_canvas.style.height = height * _scale + "px";
|
|
1369
1399
|
}
|
|
1370
|
-
|
|
1371
|
-
_ctx.imageSmoothingEnabled = false;
|
|
1372
|
-
_canvas.style.imageRendering = "pixelated";
|
|
1373
|
-
}
|
|
1400
|
+
_ctx.imageSmoothingEnabled = false;
|
|
1374
1401
|
instance.textalign("start", "top");
|
|
1375
1402
|
instance.emit("resized", _scale);
|
|
1376
|
-
if (!settings.animate) {
|
|
1377
|
-
raf(drawFrame);
|
|
1378
|
-
}
|
|
1379
1403
|
}
|
|
1380
1404
|
function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
|
|
1381
1405
|
if (!_eventListeners[eventName]) return;
|
|
@@ -1403,6 +1427,7 @@
|
|
|
1403
1427
|
DEV: console.info(`[litecanvas] version ${version} started`);
|
|
1404
1428
|
DEV: console.debug(`[litecanvas] litecanvas() options =`, settings);
|
|
1405
1429
|
setupCanvas();
|
|
1430
|
+
instance.pal();
|
|
1406
1431
|
if ("loading" === document.readyState) {
|
|
1407
1432
|
on(root, "DOMContentLoaded", () => raf(init));
|
|
1408
1433
|
} else {
|
package/dist/dist.js
CHANGED
|
@@ -31,20 +31,18 @@
|
|
|
31
31
|
const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
|
|
32
32
|
elem.addEventListener(evt, callback, false);
|
|
33
33
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
|
|
34
|
-
}, preventDefault = (ev) => ev.preventDefault(), beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
|
|
34
|
+
}, lowerCase = (str) => str.toLowerCase(), preventDefault = (ev) => ev.preventDefault(), beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
|
|
35
35
|
width: null,
|
|
36
36
|
height: null,
|
|
37
37
|
autoscale: true,
|
|
38
|
-
pixelart: true,
|
|
39
38
|
canvas: null,
|
|
40
39
|
global: true,
|
|
41
40
|
loop: null,
|
|
42
41
|
tapEvents: true,
|
|
43
|
-
keyboardEvents: true
|
|
44
|
-
animate: true
|
|
42
|
+
keyboardEvents: true
|
|
45
43
|
};
|
|
46
44
|
settings = Object.assign(defaults, settings);
|
|
47
|
-
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors
|
|
45
|
+
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
|
|
48
46
|
const instance = {
|
|
49
47
|
/** @type {number} */
|
|
50
48
|
W: 0,
|
|
@@ -417,41 +415,46 @@
|
|
|
417
415
|
image(x, y, source) {
|
|
418
416
|
_ctx.drawImage(source, ~~x, ~~y);
|
|
419
417
|
},
|
|
418
|
+
/**
|
|
419
|
+
* Draw a sprite pxiel by pixel represented by a string. Each pixel must be a base 36 number (0-9 or a-z) or a dot.
|
|
420
|
+
*
|
|
421
|
+
* @param {number} x
|
|
422
|
+
* @param {number} y
|
|
423
|
+
* @param {number} width
|
|
424
|
+
* @param {number} height
|
|
425
|
+
* @param {string} pixels
|
|
426
|
+
*/
|
|
427
|
+
spr(x, y, width, height, pixels) {
|
|
428
|
+
const chars = pixels.replace(/\s/g, "");
|
|
429
|
+
for (let gridx = 0; gridx < width; gridx++) {
|
|
430
|
+
for (let gridy = 0; gridy < height; gridy++) {
|
|
431
|
+
const char = chars[height * gridy + gridx] || ".";
|
|
432
|
+
if (char !== ".") {
|
|
433
|
+
instance.rectfill(x + gridx, y + gridy, 1, 1, parseInt(char, 16) || 0);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
},
|
|
420
438
|
/**
|
|
421
439
|
* Draw in an OffscreenCanvas and returns its image.
|
|
422
440
|
*
|
|
423
441
|
* @param {number} width
|
|
424
442
|
* @param {number} height
|
|
425
|
-
* @param {
|
|
443
|
+
* @param {drawCallback} callback
|
|
426
444
|
* @param {object} [options]
|
|
427
445
|
* @param {number} [options.scale=1]
|
|
428
446
|
* @param {OffscreenCanvas} [options.canvas]
|
|
429
447
|
* @returns {ImageBitmap}
|
|
430
448
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
|
|
431
449
|
*/
|
|
432
|
-
paint(width, height,
|
|
433
|
-
const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1,
|
|
450
|
+
paint(width, height, callback, options = {}) {
|
|
451
|
+
const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1, currentContext = _ctx;
|
|
434
452
|
canvas.width = width * scale;
|
|
435
453
|
canvas.height = height * scale;
|
|
436
454
|
_ctx = canvas.getContext("2d");
|
|
437
455
|
_ctx.scale(scale, scale);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
_ctx.imageSmoothingEnabled = false;
|
|
441
|
-
for (const str of drawing) {
|
|
442
|
-
for (const color of str) {
|
|
443
|
-
if (" " !== color && "." !== color) {
|
|
444
|
-
instance.rectfill(x, y, 1, 1, parseInt(color, 16));
|
|
445
|
-
}
|
|
446
|
-
x++;
|
|
447
|
-
}
|
|
448
|
-
y++;
|
|
449
|
-
x = 0;
|
|
450
|
-
}
|
|
451
|
-
} else {
|
|
452
|
-
drawing(_ctx);
|
|
453
|
-
}
|
|
454
|
-
_ctx = contextOriginal;
|
|
456
|
+
callback(_ctx);
|
|
457
|
+
_ctx = currentContext;
|
|
455
458
|
return canvas.transferToImageBitmap();
|
|
456
459
|
},
|
|
457
460
|
/** ADVANCED GRAPHICS API */
|
|
@@ -609,7 +612,7 @@
|
|
|
609
612
|
* @returns {Function} a function to remove the listener
|
|
610
613
|
*/
|
|
611
614
|
listen(eventName, callback) {
|
|
612
|
-
eventName = eventName
|
|
615
|
+
eventName = lowerCase(eventName);
|
|
613
616
|
_eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
|
|
614
617
|
_eventListeners[eventName].add(callback);
|
|
615
618
|
return () => _eventListeners && _eventListeners[eventName].delete(callback);
|
|
@@ -625,19 +628,36 @@
|
|
|
625
628
|
*/
|
|
626
629
|
emit(eventName, arg1, arg2, arg3, arg4) {
|
|
627
630
|
if (_initialized) {
|
|
628
|
-
eventName = eventName
|
|
631
|
+
eventName = lowerCase(eventName);
|
|
629
632
|
triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
|
|
630
633
|
triggerEvent(eventName, arg1, arg2, arg3, arg4);
|
|
631
634
|
triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
|
|
632
635
|
}
|
|
633
636
|
},
|
|
634
637
|
/**
|
|
635
|
-
* Set or reset the color palette
|
|
638
|
+
* Set or reset the color palette.
|
|
636
639
|
*
|
|
637
640
|
* @param {string[]} [colors]
|
|
638
641
|
*/
|
|
639
642
|
pal(colors = defaultPalette) {
|
|
640
643
|
_colors = colors;
|
|
644
|
+
_currentPalette = [...colors];
|
|
645
|
+
},
|
|
646
|
+
/**
|
|
647
|
+
* Swap two colors of the current palette.
|
|
648
|
+
*
|
|
649
|
+
* If called without arguments, reset the current palette.
|
|
650
|
+
*
|
|
651
|
+
* @param {number?} a
|
|
652
|
+
* @param {number?} b
|
|
653
|
+
*/
|
|
654
|
+
palc(a, b) {
|
|
655
|
+
if (a == null) {
|
|
656
|
+
_colors = [..._currentPalette];
|
|
657
|
+
} else {
|
|
658
|
+
;
|
|
659
|
+
[_colors[a], _colors[b]] = [_colors[b], _colors[a]];
|
|
660
|
+
}
|
|
641
661
|
},
|
|
642
662
|
/**
|
|
643
663
|
* Define or update a instance property.
|
|
@@ -928,12 +948,12 @@
|
|
|
928
948
|
const _keysDown = /* @__PURE__ */ new Set();
|
|
929
949
|
const _keysPress = /* @__PURE__ */ new Set();
|
|
930
950
|
const keyCheck = (keySet, key = "") => {
|
|
931
|
-
key = key
|
|
951
|
+
key = lowerCase(key);
|
|
932
952
|
return !key ? keySet.size > 0 : keySet.has("space" === key ? " " : key);
|
|
933
953
|
};
|
|
934
954
|
let _lastKey = "";
|
|
935
955
|
on(root, "keydown", (event) => {
|
|
936
|
-
const key = event.key
|
|
956
|
+
const key = lowerCase(event.key);
|
|
937
957
|
if (!_keysDown.has(key)) {
|
|
938
958
|
_keysDown.add(key);
|
|
939
959
|
_keysPress.add(key);
|
|
@@ -941,7 +961,7 @@
|
|
|
941
961
|
}
|
|
942
962
|
});
|
|
943
963
|
on(root, "keyup", (event) => {
|
|
944
|
-
_keysDown.delete(event.key
|
|
964
|
+
_keysDown.delete(lowerCase(event.key));
|
|
945
965
|
});
|
|
946
966
|
on(root, "blur", () => _keysDown.clear());
|
|
947
967
|
instance.listen("after:update", () => _keysPress.clear());
|
|
@@ -978,9 +998,7 @@
|
|
|
978
998
|
instance.resume();
|
|
979
999
|
}
|
|
980
1000
|
function drawFrame() {
|
|
981
|
-
|
|
982
|
-
return instance.emit("draw", _ctx);
|
|
983
|
-
}
|
|
1001
|
+
_rafid = raf(drawFrame);
|
|
984
1002
|
let now = Date.now();
|
|
985
1003
|
let updated = 0;
|
|
986
1004
|
let frameTime = now - _lastFrameTime;
|
|
@@ -999,7 +1017,6 @@
|
|
|
999
1017
|
_accumulated = 0;
|
|
1000
1018
|
}
|
|
1001
1019
|
}
|
|
1002
|
-
_rafid = raf(drawFrame);
|
|
1003
1020
|
}
|
|
1004
1021
|
function setupCanvas() {
|
|
1005
1022
|
if ("string" === typeof settings.canvas) {
|
|
@@ -1010,11 +1027,11 @@
|
|
|
1010
1027
|
_canvas = _canvas || document.createElement("canvas");
|
|
1011
1028
|
_ctx = _canvas.getContext("2d");
|
|
1012
1029
|
on(_canvas, "click", () => focus());
|
|
1013
|
-
_canvas.style = "";
|
|
1014
1030
|
resizeCanvas();
|
|
1015
1031
|
if (!_canvas.parentNode) {
|
|
1016
1032
|
document.body.appendChild(_canvas);
|
|
1017
1033
|
}
|
|
1034
|
+
_canvas.style.imageRendering = "pixelated";
|
|
1018
1035
|
_canvas.oncontextmenu = () => false;
|
|
1019
1036
|
}
|
|
1020
1037
|
function resizeCanvas() {
|
|
@@ -1034,15 +1051,9 @@
|
|
|
1034
1051
|
_canvas.style.width = width * _scale + "px";
|
|
1035
1052
|
_canvas.style.height = height * _scale + "px";
|
|
1036
1053
|
}
|
|
1037
|
-
|
|
1038
|
-
_ctx.imageSmoothingEnabled = false;
|
|
1039
|
-
_canvas.style.imageRendering = "pixelated";
|
|
1040
|
-
}
|
|
1054
|
+
_ctx.imageSmoothingEnabled = false;
|
|
1041
1055
|
instance.textalign("start", "top");
|
|
1042
1056
|
instance.emit("resized", _scale);
|
|
1043
|
-
if (!settings.animate) {
|
|
1044
|
-
raf(drawFrame);
|
|
1045
|
-
}
|
|
1046
1057
|
}
|
|
1047
1058
|
function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
|
|
1048
1059
|
if (!_eventListeners[eventName]) return;
|
|
@@ -1064,6 +1075,7 @@
|
|
|
1064
1075
|
root.ENGINE = instance;
|
|
1065
1076
|
}
|
|
1066
1077
|
setupCanvas();
|
|
1078
|
+
instance.pal();
|
|
1067
1079
|
if ("loading" === document.readyState) {
|
|
1068
1080
|
on(root, "DOMContentLoaded", () => raf(init));
|
|
1069
1081
|
} else {
|
package/dist/dist.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,l=Math,
|
|
1
|
+
(()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,l=Math,n=2*l.PI,i=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.toLowerCase(),c=e=>e.preventDefault(),f=e=>e.beginPath(),d=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,c=1,f=0,d=0,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=f*=500*z/44100/44100,I=n*=(1-l+2*l*E.random(l=[]))*z/44100,D=0,S=0,A=0,M=1,C=0,L=0,N=0,P=k<0?-1:1,F=z*P*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+P*q)/2/O,G=-(P+q)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,h=44100*h|0,a*=.3*e.zzfxV,P=i+y+o+r+w|0;A<P;l[A++]=N*a)++L%(100*v|0)||(N=s?1<s?2<s?3<s?B(D*D):E.max(E.min(E.tan(D),1),-1):1-(2*D/z%2+2)%2:1-4*E.abs(E.round(D/z)-D/z):B(D),N=(h?1-b+b*B(z*A/h):1)*(N<0?-1:1)*E.abs(N)**c*(A<i?A/i:A<i+y?1-(A-i)/y*(1-x):A<i+y+o?x:A<P-w?(P-A-w)/r*x:0),N=w?N/2+(w>A?0:(A<P-w?1:(P-A)/w)*l[A-w|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),D+=(F=(n+=f+=d)*E.cos(g*S++))+F*m*B(A**5),M&&++M>p&&(n+=u,I+=u,M=0),!h||++C%h||(n=I,f=T,M=M||1);(a=t.createBuffer(1,P,44100)).getChannelData(0).set(l),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let u=!1,p=[],h,m=1,g,v=.5,w=1,x,y=1e3/60,b,k,E="sans-serif",z=20,T=Date.now(),I,D,S=[.5,0,1750,,,.3,1,,,,600,.1],A={},M={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:n,HALF_PI:n/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?M.clamp(o,l,n):o},norm:(e,t,a)=>M.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(T=(1664525*T+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(M.rand(e,t+1)),rseed(e){T=~~e},cls(e){null==e?g.clearRect(0,0,g.canvas.width,g.canvas.height):M.rectfill(0,0,g.canvas.width,g.canvas.height,e)},rect(e,t,a,l,n,i){f(g),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),M.stroke(n)},rectfill(e,t,a,l,n,i){f(g),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),M.fill(n)},circ(e,t,a,l){f(g),g.arc(~~e,~~t,~~a,0,n),M.stroke(l)},circfill(e,t,a,l){f(g),g.arc(~~e,~~t,~~a,0,n),M.fill(l)},oval(e,t,a,l,i){f(g),g.ellipse(~~e,~~t,~~a,~~l,0,0,n),M.stroke(i)},ovalfill(e,t,a,l,i){f(g),g.ellipse(~~e,~~t,~~a,~~l,0,0,n),M.fill(i)},line(e,t,a,l,n){f(g);let i=.5*(0!==v&&~~e==~~a),o=.5*(0!==v&&~~t==~~l);g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),M.stroke(n)},linewidth(e){g.lineWidth=~~e,v=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=3,n="normal"){g.font=`${n} ${z}px ${E}`,g.fillStyle=D[~~l%D.length],g.fillText(a,~~e,~~t)},textfont(e){E=e},textsize(e){z=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a,l,n){let i=n.replace(/\s/g,"");for(let n=0;n<a;n++)for(let a=0;a<l;a++){let o=i[l*a+n]||".";"."!==o&&M.rectfill(e+n,t+a,1,1,parseInt(o,16)||0)}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(){g.save()},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t){g.scale(e,t||e)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=M.clamp(e,0,1)},fill(e){g.fillStyle=D[~~e%D.length],g.fill()},stroke(e){g.strokeStyle=D[~~e%D.length],g.stroke()},clip(e){f(g),e(g),g.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||S,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>h,use(e,t={}){u?F(e,t):p.push([e,t])},listen:(e,t)=>(A[e=s(e)]=A[e]||new Set,A[e].add(t),()=>A&&A[e].delete(t)),emit(e,t,a,l,n){u&&(P("before:"+(e=s(e)),t,a,l,n),P(e,t,a,l,n),P("after:"+e,t,a,l,n))},pal(t=e){D=t,I=[...t]},palc(e,t){null==e?D=[...I]:[D[e],D[t]]=[D[t],D[e]]},def(e,l){M[e]=l,t.global&&(a[e]=l)},timescale(e){w=e},framerate(e){y=1e3/~~e},stat(e){let l={index:e,value:[t,u,y/1e3,m,A,D,S,w,a.zzfxV,T,z,E][e]};return M.emit("stat",l),l.value},quit(){for(let e of(M.pause(),M.emit("quit"),A={},o))e();if(t.global){for(let e in M)delete a[e];delete a.ENGINE}u=!1},pause(){cancelAnimationFrame(k),k=0},resume(){u&&!k&&(b=y,x=Date.now(),k=i(L))},paused:()=>!k};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))M[e]=l[e];function C(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&M.listen(t,e[t]);for(let[e,t]of p)F(e,t);if(t.autoscale&&r(a,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:Date.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&Date.now()-e.t<=300,o=!1;r(h,"mousedown",t=>{if(0===t.button){c(t);let[a,n]=e(t);M.emit("tap",a,n,0),l(0,a,n),o=!0}}),r(h,"mouseup",a=>{if(0===a.button){c(a);let l=t.get(0),[n,r]=e(a);i(l)&&M.emit("tapped",l.xi,l.yi,0),M.emit("untap",n,r,0),t.delete(0),o=!1}}),r(a,"mousemove",t=>{c(t);let[a,l]=e(t);M.def("MX",a),M.def("MY",l),o&&(M.emit("tapping",a,l,0),n(0,a,l))}),r(h,"touchstart",t=>{for(let a of(c(t),t.changedTouches)){let[t,n]=e(a);M.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(h,"touchmove",t=>{for(let a of(c(t),t.changedTouches)){let[t,l]=e(a);M.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{c(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)&&M.emit("tapped",l.xi,l.yi,e),M.emit("untap",l.x,l.y,e),t.delete(e))};r(h,"touchend",s),r(h,"touchcancel",s),r(a,"blur",()=>{for(let[e,a]of(o=!1,t))M.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=s(t))?e.has("space"===t?" ":t):e.size>0,n="";r(a,"keydown",a=>{let l=s(a.key);e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),r(a,"keyup",t=>{e.delete(s(t.key))}),r(a,"blur",()=>e.clear()),M.listen("after:update",()=>t.clear()),M.def("iskeydown",t=>l(e,t)),M.def("iskeypressed",e=>l(t,e)),M.def("lastkey",()=>n)}u=!0,M.emit("init",M),M.resume()}function L(){k=i(L);let e=Date.now(),t=0,a=e-x;for(x=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*w;M.emit("update",e,t),M.def("T",M.T+e)}t&&(M.emit("draw",g),t>1&&(b=0))}function N(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(M.def("W",e),M.def("H",a),h.width=e,h.height=a,t.autoscale){let n=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=l.min(innerWidth/e,innerHeight/a),m=n>1&&m>n?n:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,M.textalign("start","top"),M.emit("resized",m)}function P(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function F(e,t){let a=e(M,t);for(let e in a)M.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,M),a.ENGINE=M}return g=(h=(h="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(h,"click",()=>focus()),N(),h.parentNode||document.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,M.pal(),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(C)):i(C),M}})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litecanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.0",
|
|
4
4
|
"description": "Lightweight HTML5 canvas 2D 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>",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@litecanvas/jsdom-extras": "^2.0.1",
|
|
35
35
|
"@size-limit/preset-small-lib": "^11.2.0",
|
|
36
|
-
"@swc/core": "^1.13.
|
|
36
|
+
"@swc/core": "^1.13.3",
|
|
37
37
|
"@types/jsdom": "^21.1.7",
|
|
38
38
|
"ava": "^6.4.1",
|
|
39
39
|
"esbuild": "^0.25.8",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"test:watch": "ava --watch",
|
|
56
56
|
"dev": "esbuild src/web.js --bundle --watch --outfile=samples/dist.js --servedir=samples",
|
|
57
57
|
"build": "npm run genversion && node script/build.js && size-limit",
|
|
58
|
-
"format": "prettier -w src/* samples/* types/* script/*
|
|
59
|
-
"check-types": "npx
|
|
58
|
+
"format": "prettier -w src/* samples/* types/* script/* tests/*",
|
|
59
|
+
"check-types": "npx tsc ./types/*",
|
|
60
60
|
"genversion": "genversion --es6 src/version.js"
|
|
61
61
|
},
|
|
62
62
|
"files": [
|
package/src/index.js
CHANGED
|
@@ -23,6 +23,8 @@ export default function litecanvas(settings = {}) {
|
|
|
23
23
|
elem.addEventListener(evt, callback, false)
|
|
24
24
|
_browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
|
|
25
25
|
},
|
|
26
|
+
/** @type {(str: string) => string} */
|
|
27
|
+
lowerCase = (str) => str.toLowerCase(),
|
|
26
28
|
/** @type {(ev: Event) => void} */
|
|
27
29
|
preventDefault = (ev) => ev.preventDefault(),
|
|
28
30
|
/** @type {(c: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) => void} */
|
|
@@ -34,13 +36,11 @@ export default function litecanvas(settings = {}) {
|
|
|
34
36
|
width: null,
|
|
35
37
|
height: null,
|
|
36
38
|
autoscale: true,
|
|
37
|
-
pixelart: true,
|
|
38
39
|
canvas: null,
|
|
39
40
|
global: true,
|
|
40
41
|
loop: null,
|
|
41
42
|
tapEvents: true,
|
|
42
43
|
keyboardEvents: true,
|
|
43
|
-
animate: true,
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// setup the settings default values
|
|
@@ -75,7 +75,9 @@ export default function litecanvas(settings = {}) {
|
|
|
75
75
|
/** @type {number} */
|
|
76
76
|
_rngSeed = Date.now(),
|
|
77
77
|
/** @type {string[]} */
|
|
78
|
-
|
|
78
|
+
_currentPalette,
|
|
79
|
+
/** @type {string[]} */
|
|
80
|
+
_colors,
|
|
79
81
|
/** @type {number[]} */
|
|
80
82
|
_defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
81
83
|
/** @type {string} */
|
|
@@ -728,19 +730,46 @@ export default function litecanvas(settings = {}) {
|
|
|
728
730
|
_ctx.drawImage(source, ~~x, ~~y)
|
|
729
731
|
},
|
|
730
732
|
|
|
733
|
+
/**
|
|
734
|
+
* Draw a sprite pxiel by pixel represented by a string. Each pixel must be a base 36 number (0-9 or a-z) or a dot.
|
|
735
|
+
*
|
|
736
|
+
* @param {number} x
|
|
737
|
+
* @param {number} y
|
|
738
|
+
* @param {number} width
|
|
739
|
+
* @param {number} height
|
|
740
|
+
* @param {string} pixels
|
|
741
|
+
*/
|
|
742
|
+
spr(x, y, width, height, pixels) {
|
|
743
|
+
DEV: assert(isNumber(x), '[litecanvas] spr() 1st param must be a number')
|
|
744
|
+
DEV: assert(isNumber(y), '[litecanvas] spr() 2nd param must be a number')
|
|
745
|
+
DEV: assert(isNumber(width), '[litecanvas] spr() 3rd param must be a number')
|
|
746
|
+
DEV: assert(isNumber(height), '[litecanvas] spr() 4th param must be a number')
|
|
747
|
+
DEV: assert('string' === typeof pixels, '[litecanvas] spr() 5th param must be a string')
|
|
748
|
+
|
|
749
|
+
const chars = pixels.replace(/\s/g, '')
|
|
750
|
+
for (let gridx = 0; gridx < width; gridx++) {
|
|
751
|
+
for (let gridy = 0; gridy < height; gridy++) {
|
|
752
|
+
const char = chars[height * gridy + gridx] || '.'
|
|
753
|
+
if (char !== '.') {
|
|
754
|
+
instance.rectfill(x + gridx, y + gridy, 1, 1, parseInt(char, 16) || 0)
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
|
|
731
760
|
/**
|
|
732
761
|
* Draw in an OffscreenCanvas and returns its image.
|
|
733
762
|
*
|
|
734
763
|
* @param {number} width
|
|
735
764
|
* @param {number} height
|
|
736
|
-
* @param {
|
|
765
|
+
* @param {drawCallback} callback
|
|
737
766
|
* @param {object} [options]
|
|
738
767
|
* @param {number} [options.scale=1]
|
|
739
768
|
* @param {OffscreenCanvas} [options.canvas]
|
|
740
769
|
* @returns {ImageBitmap}
|
|
741
770
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
|
|
742
771
|
*/
|
|
743
|
-
paint(width, height,
|
|
772
|
+
paint(width, height, callback, options = {}) {
|
|
744
773
|
DEV: assert(
|
|
745
774
|
isNumber(width) && width >= 1,
|
|
746
775
|
'[litecanvas] paint() 1st param must be a positive number'
|
|
@@ -750,7 +779,7 @@ export default function litecanvas(settings = {}) {
|
|
|
750
779
|
'[litecanvas] paint() 2nd param must be a positive number'
|
|
751
780
|
)
|
|
752
781
|
DEV: assert(
|
|
753
|
-
'function' === typeof
|
|
782
|
+
'function' === typeof callback,
|
|
754
783
|
'[litecanvas] paint() 3rd param must be a function or array'
|
|
755
784
|
)
|
|
756
785
|
DEV: assert(
|
|
@@ -765,37 +794,16 @@ export default function litecanvas(settings = {}) {
|
|
|
765
794
|
const /** @type {OffscreenCanvas} */
|
|
766
795
|
canvas = options.canvas || new OffscreenCanvas(1, 1),
|
|
767
796
|
scale = options.scale || 1,
|
|
768
|
-
|
|
797
|
+
currentContext = _ctx // context backup
|
|
769
798
|
|
|
770
799
|
canvas.width = width * scale
|
|
771
800
|
canvas.height = height * scale
|
|
772
801
|
|
|
773
802
|
_ctx = canvas.getContext('2d')
|
|
774
803
|
_ctx.scale(scale, scale)
|
|
804
|
+
callback(_ctx)
|
|
775
805
|
|
|
776
|
-
|
|
777
|
-
if (Array.isArray(drawing)) {
|
|
778
|
-
let x = 0,
|
|
779
|
-
y = 0
|
|
780
|
-
|
|
781
|
-
_ctx.imageSmoothingEnabled = false
|
|
782
|
-
|
|
783
|
-
for (const str of drawing) {
|
|
784
|
-
for (const color of str) {
|
|
785
|
-
if (' ' !== color && '.' !== color) {
|
|
786
|
-
// support for 16-color palette using hex (from 0 to f)
|
|
787
|
-
instance.rectfill(x, y, 1, 1, parseInt(color, 16))
|
|
788
|
-
}
|
|
789
|
-
x++
|
|
790
|
-
}
|
|
791
|
-
y++
|
|
792
|
-
x = 0
|
|
793
|
-
}
|
|
794
|
-
} else {
|
|
795
|
-
drawing(_ctx)
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
_ctx = contextOriginal // restore the context
|
|
806
|
+
_ctx = currentContext // restore the context
|
|
799
807
|
|
|
800
808
|
return canvas.transferToImageBitmap()
|
|
801
809
|
},
|
|
@@ -1034,7 +1042,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1034
1042
|
'[litecanvas] listen() 2nd param must be a function'
|
|
1035
1043
|
)
|
|
1036
1044
|
|
|
1037
|
-
eventName = eventName
|
|
1045
|
+
eventName = lowerCase(eventName)
|
|
1038
1046
|
|
|
1039
1047
|
_eventListeners[eventName] = _eventListeners[eventName] || new Set()
|
|
1040
1048
|
_eventListeners[eventName].add(callback)
|
|
@@ -1058,7 +1066,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1058
1066
|
'[litecanvas] emit() 1st param must be a string'
|
|
1059
1067
|
)
|
|
1060
1068
|
if (_initialized) {
|
|
1061
|
-
eventName = eventName
|
|
1069
|
+
eventName = lowerCase(eventName)
|
|
1062
1070
|
|
|
1063
1071
|
triggerEvent('before:' + eventName, arg1, arg2, arg3, arg4)
|
|
1064
1072
|
triggerEvent(eventName, arg1, arg2, arg3, arg4)
|
|
@@ -1067,7 +1075,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1067
1075
|
},
|
|
1068
1076
|
|
|
1069
1077
|
/**
|
|
1070
|
-
* Set or reset the color palette
|
|
1078
|
+
* Set or reset the color palette.
|
|
1071
1079
|
*
|
|
1072
1080
|
* @param {string[]} [colors]
|
|
1073
1081
|
*/
|
|
@@ -1077,6 +1085,31 @@ export default function litecanvas(settings = {}) {
|
|
|
1077
1085
|
'[litecanvas] pal() 1st param must be a array of strings'
|
|
1078
1086
|
)
|
|
1079
1087
|
_colors = colors
|
|
1088
|
+
_currentPalette = [...colors]
|
|
1089
|
+
},
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Swap two colors of the current palette.
|
|
1093
|
+
*
|
|
1094
|
+
* If called without arguments, reset the current palette.
|
|
1095
|
+
*
|
|
1096
|
+
* @param {number?} a
|
|
1097
|
+
* @param {number?} b
|
|
1098
|
+
*/
|
|
1099
|
+
palc(a, b) {
|
|
1100
|
+
DEV: assert(
|
|
1101
|
+
null == a || (isNumber(a) && a >= 0),
|
|
1102
|
+
'[litecanvas] palc() 1st param must be a positive number'
|
|
1103
|
+
)
|
|
1104
|
+
DEV: assert(
|
|
1105
|
+
isNumber(a) ? isNumber(b) && b >= 0 : null == b,
|
|
1106
|
+
'[litecanvas] palc() 2nd param must be a positive number'
|
|
1107
|
+
)
|
|
1108
|
+
if (a == null) {
|
|
1109
|
+
_colors = [..._currentPalette]
|
|
1110
|
+
} else {
|
|
1111
|
+
;[_colors[a], _colors[b]] = [_colors[b], _colors[a]]
|
|
1112
|
+
}
|
|
1080
1113
|
},
|
|
1081
1114
|
|
|
1082
1115
|
/**
|
|
@@ -1454,7 +1487,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1454
1487
|
* @returns {boolean}
|
|
1455
1488
|
*/
|
|
1456
1489
|
const keyCheck = (keySet, key = '') => {
|
|
1457
|
-
key = key
|
|
1490
|
+
key = lowerCase(key)
|
|
1458
1491
|
return !key ? keySet.size > 0 : keySet.has('space' === key ? ' ' : key)
|
|
1459
1492
|
}
|
|
1460
1493
|
|
|
@@ -1462,7 +1495,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1462
1495
|
let _lastKey = ''
|
|
1463
1496
|
|
|
1464
1497
|
on(root, 'keydown', (/** @type {KeyboardEvent} */ event) => {
|
|
1465
|
-
const key = event.key
|
|
1498
|
+
const key = lowerCase(event.key)
|
|
1466
1499
|
if (!_keysDown.has(key)) {
|
|
1467
1500
|
_keysDown.add(key)
|
|
1468
1501
|
_keysPress.add(key)
|
|
@@ -1471,7 +1504,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1471
1504
|
})
|
|
1472
1505
|
|
|
1473
1506
|
on(root, 'keyup', (/** @type {KeyboardEvent} */ event) => {
|
|
1474
|
-
_keysDown.delete(event.key
|
|
1507
|
+
_keysDown.delete(lowerCase(event.key))
|
|
1475
1508
|
})
|
|
1476
1509
|
|
|
1477
1510
|
on(root, 'blur', () => _keysDown.clear())
|
|
@@ -1523,9 +1556,8 @@ export default function litecanvas(settings = {}) {
|
|
|
1523
1556
|
}
|
|
1524
1557
|
|
|
1525
1558
|
function drawFrame() {
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
}
|
|
1559
|
+
// request the next frame
|
|
1560
|
+
_rafid = raf(drawFrame)
|
|
1529
1561
|
|
|
1530
1562
|
let now = Date.now()
|
|
1531
1563
|
let updated = 0
|
|
@@ -1555,9 +1587,6 @@ export default function litecanvas(settings = {}) {
|
|
|
1555
1587
|
)
|
|
1556
1588
|
}
|
|
1557
1589
|
}
|
|
1558
|
-
|
|
1559
|
-
// request the next frame
|
|
1560
|
-
_rafid = raf(drawFrame)
|
|
1561
1590
|
}
|
|
1562
1591
|
|
|
1563
1592
|
function setupCanvas() {
|
|
@@ -1582,15 +1611,13 @@ export default function litecanvas(settings = {}) {
|
|
|
1582
1611
|
|
|
1583
1612
|
on(_canvas, 'click', () => focus())
|
|
1584
1613
|
|
|
1585
|
-
/** @ts-ignore */
|
|
1586
|
-
_canvas.style = ''
|
|
1587
|
-
|
|
1588
1614
|
resizeCanvas()
|
|
1589
1615
|
|
|
1590
1616
|
if (!_canvas.parentNode) {
|
|
1591
1617
|
document.body.appendChild(_canvas)
|
|
1592
1618
|
}
|
|
1593
1619
|
|
|
1620
|
+
_canvas.style.imageRendering = 'pixelated'
|
|
1594
1621
|
_canvas.oncontextmenu = () => false
|
|
1595
1622
|
}
|
|
1596
1623
|
|
|
@@ -1632,10 +1659,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1632
1659
|
}
|
|
1633
1660
|
|
|
1634
1661
|
// set canvas image rendering properties
|
|
1635
|
-
|
|
1636
|
-
_ctx.imageSmoothingEnabled = false
|
|
1637
|
-
_canvas.style.imageRendering = 'pixelated'
|
|
1638
|
-
}
|
|
1662
|
+
_ctx.imageSmoothingEnabled = false
|
|
1639
1663
|
|
|
1640
1664
|
// set the default text align and baseline
|
|
1641
1665
|
instance.textalign('start', 'top')
|
|
@@ -1643,11 +1667,6 @@ export default function litecanvas(settings = {}) {
|
|
|
1643
1667
|
// trigger "resized" event
|
|
1644
1668
|
// note: not triggered before the "init" event
|
|
1645
1669
|
instance.emit('resized', _scale)
|
|
1646
|
-
|
|
1647
|
-
// force redraw when the canvas is not animated
|
|
1648
|
-
if (!settings.animate) {
|
|
1649
|
-
raf(drawFrame)
|
|
1650
|
-
}
|
|
1651
1670
|
}
|
|
1652
1671
|
|
|
1653
1672
|
/**
|
|
@@ -1694,6 +1713,9 @@ export default function litecanvas(settings = {}) {
|
|
|
1694
1713
|
|
|
1695
1714
|
setupCanvas()
|
|
1696
1715
|
|
|
1716
|
+
// init the color palette
|
|
1717
|
+
instance.pal()
|
|
1718
|
+
|
|
1697
1719
|
if ('loading' === document.readyState) {
|
|
1698
1720
|
on(root, 'DOMContentLoaded', () => raf(init))
|
|
1699
1721
|
} else {
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '0.
|
|
2
|
+
export const version = '0.97.0'
|
package/types/global.d.ts
CHANGED
|
@@ -356,7 +356,7 @@ declare global {
|
|
|
356
356
|
*/
|
|
357
357
|
function textalign(align: CanvasTextAlign, baseline: CanvasTextBaseline): void
|
|
358
358
|
|
|
359
|
-
/**
|
|
359
|
+
/** BASIC GRAPHICS API */
|
|
360
360
|
/**
|
|
361
361
|
* Draw an image
|
|
362
362
|
*
|
|
@@ -365,19 +365,33 @@ declare global {
|
|
|
365
365
|
* @param source
|
|
366
366
|
*/
|
|
367
367
|
function image(x: number, y: number, source: CanvasImageSource): void
|
|
368
|
+
/**
|
|
369
|
+
* Draw a sprite pxiel by pixel represented by a string. Each pixel must be a base 36 number or a dot:
|
|
370
|
+
*
|
|
371
|
+
* - A base 36 number (`0-9` or `a-z`) represent a pixel color (supporting color palettes with max 36 colors).
|
|
372
|
+
* - A dot (`.`) represent a transparent pixel.
|
|
373
|
+
* - Spaces and lines breaks are ignored and can be used to improve the visualization.
|
|
374
|
+
*
|
|
375
|
+
* @param x the position X of the first pixel
|
|
376
|
+
* @param y the position Y of the first pixel
|
|
377
|
+
* @param width the width of the sprite
|
|
378
|
+
* @param height the height of the sprite
|
|
379
|
+
* @param pixels
|
|
380
|
+
*/
|
|
381
|
+
function spr(x: number, y: number, width: number, height: number, pixels: string): void
|
|
368
382
|
/**
|
|
369
383
|
* Draw in an OffscreenCanvas and returns its image.
|
|
370
384
|
*
|
|
371
385
|
* @param width
|
|
372
386
|
* @param height
|
|
373
|
-
* @param
|
|
387
|
+
* @param callback
|
|
374
388
|
* @param [options]
|
|
375
389
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
|
|
376
390
|
*/
|
|
377
391
|
function paint(
|
|
378
392
|
width: number,
|
|
379
393
|
height: number,
|
|
380
|
-
|
|
394
|
+
callback: drawCallback,
|
|
381
395
|
options?: {
|
|
382
396
|
scale?: number
|
|
383
397
|
canvas?: OffscreenCanvas
|
|
@@ -531,11 +545,22 @@ declare global {
|
|
|
531
545
|
*/
|
|
532
546
|
function emit(event: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any): void
|
|
533
547
|
/**
|
|
534
|
-
* Set or reset the color palette
|
|
548
|
+
* Set or reset the color palette.
|
|
535
549
|
*
|
|
536
550
|
* @param [colors]
|
|
537
551
|
*/
|
|
538
552
|
function pal(colors?: string[]): void
|
|
553
|
+
/**
|
|
554
|
+
* Swap two colors of the current palette.
|
|
555
|
+
*
|
|
556
|
+
* If called without arguments, reset the current palette.
|
|
557
|
+
*
|
|
558
|
+
* Note: `palc()` don't affect drawings made with `image()`.
|
|
559
|
+
*
|
|
560
|
+
* @param a
|
|
561
|
+
* @param b
|
|
562
|
+
*/
|
|
563
|
+
function palc(a?: number, b?: number): void
|
|
539
564
|
/**
|
|
540
565
|
* Define or update a instance property
|
|
541
566
|
*
|
package/types/types.d.ts
CHANGED
|
@@ -350,7 +350,7 @@ type LitecanvasInstance = {
|
|
|
350
350
|
*/
|
|
351
351
|
textalign(align: CanvasTextAlign, baseline: CanvasTextBaseline): void
|
|
352
352
|
|
|
353
|
-
/**
|
|
353
|
+
/** BASIC GRAPHICS API */
|
|
354
354
|
/**
|
|
355
355
|
* Draw an image
|
|
356
356
|
*
|
|
@@ -359,19 +359,33 @@ type LitecanvasInstance = {
|
|
|
359
359
|
* @param source
|
|
360
360
|
*/
|
|
361
361
|
image(x: number, y: number, source: CanvasImageSource): void
|
|
362
|
+
/**
|
|
363
|
+
* Draw a sprite pxiel by pixel represented by a string. Each pixel must be a base 36 number or a dot:
|
|
364
|
+
*
|
|
365
|
+
* - A base 36 number (`0-9` or `a-z`) represent a pixel color (supporting color palettes with max 36 colors).
|
|
366
|
+
* - A dot (`.`) represent a transparent pixel.
|
|
367
|
+
* - Spaces and lines breaks are ignored and can be used to improve the visualization.
|
|
368
|
+
*
|
|
369
|
+
* @param x the position X of the first pixel
|
|
370
|
+
* @param y the position Y of the first pixel
|
|
371
|
+
* @param width the width of the sprite
|
|
372
|
+
* @param height the height of the sprite
|
|
373
|
+
* @param pixels
|
|
374
|
+
*/
|
|
375
|
+
spr(x: number, y: number, width: number, height: number, pixels: string): void
|
|
362
376
|
/**
|
|
363
377
|
* Draw in an OffscreenCanvas and returns its image.
|
|
364
378
|
*
|
|
365
379
|
* @param width
|
|
366
380
|
* @param height
|
|
367
|
-
* @param
|
|
381
|
+
* @param callback
|
|
368
382
|
* @param [options]
|
|
369
383
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
|
|
370
384
|
*/
|
|
371
385
|
paint(
|
|
372
386
|
width: number,
|
|
373
387
|
height: number,
|
|
374
|
-
|
|
388
|
+
callback: drawCallback,
|
|
375
389
|
options?: {
|
|
376
390
|
scale?: number
|
|
377
391
|
canvas?: OffscreenCanvas
|
|
@@ -528,11 +542,22 @@ type LitecanvasInstance = {
|
|
|
528
542
|
*/
|
|
529
543
|
def(key: string, value: any): void
|
|
530
544
|
/**
|
|
531
|
-
* Set or reset the color palette
|
|
545
|
+
* Set or reset the color palette.
|
|
532
546
|
*
|
|
533
547
|
* @param [colors]
|
|
534
548
|
*/
|
|
535
549
|
pal(colors?: string[]): void
|
|
550
|
+
/**
|
|
551
|
+
* Swap two colors of the current palette.
|
|
552
|
+
*
|
|
553
|
+
* If called without arguments, reset the current palette.
|
|
554
|
+
*
|
|
555
|
+
* Note: `palc()` don't affect drawings made with `image()`.
|
|
556
|
+
*
|
|
557
|
+
* @param a
|
|
558
|
+
* @param b
|
|
559
|
+
*/
|
|
560
|
+
palc(a?: number, b?: number): void
|
|
536
561
|
/**
|
|
537
562
|
* The scale of the game's delta time (dt).
|
|
538
563
|
* Values higher than 1 increase the speed of time, while values smaller than 1 decrease it.
|
|
@@ -605,13 +630,6 @@ type LitecanvasOptions = {
|
|
|
605
630
|
* Note: Only works if the option "width" was specified.
|
|
606
631
|
*/
|
|
607
632
|
autoscale?: boolean | number
|
|
608
|
-
/**
|
|
609
|
-
* If `true`, the pixel art images won't look blurry.
|
|
610
|
-
* Also, disables canvas built-in antialias.
|
|
611
|
-
*
|
|
612
|
-
* Default: `true`
|
|
613
|
-
*/
|
|
614
|
-
pixelart?: boolean
|
|
615
633
|
/**
|
|
616
634
|
* If `true` (default), all methods and properties of the engine will be exposed to the global scope (window).
|
|
617
635
|
*/
|
|
@@ -646,12 +664,6 @@ type LitecanvasOptions = {
|
|
|
646
664
|
* Useful when you want to implement your keyboard handler.
|
|
647
665
|
*/
|
|
648
666
|
keyboardEvents?: boolean
|
|
649
|
-
/**
|
|
650
|
-
* default: `true`
|
|
651
|
-
*
|
|
652
|
-
* if `false` stops the code in `update()` and `draw()` from running repeatedly. By default, tries to run these functions 60 times per second.
|
|
653
|
-
*/
|
|
654
|
-
animate?: boolean
|
|
655
667
|
}
|
|
656
668
|
|
|
657
669
|
type LitecanvasGameLoop = {
|