litecanvas 0.85.0 → 0.86.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 +74 -24
- package/dist/dist.js +45 -24
- package/dist/dist.min.js +1 -1
- package/package.json +2 -2
- package/src/index.js +87 -29
- package/types/index.d.ts +20 -0
- package/types/types.d.ts +20 -0
package/dist/dist.dev.js
CHANGED
|
@@ -50,16 +50,7 @@
|
|
|
50
50
|
animate: true
|
|
51
51
|
};
|
|
52
52
|
settings = Object.assign(defaults, settings);
|
|
53
|
-
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
54
|
-
init: null,
|
|
55
|
-
update: null,
|
|
56
|
-
draw: null,
|
|
57
|
-
resized: null,
|
|
58
|
-
tap: null,
|
|
59
|
-
untap: null,
|
|
60
|
-
tapping: null,
|
|
61
|
-
tapped: null
|
|
62
|
-
};
|
|
53
|
+
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _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 = {};
|
|
63
54
|
const instance = {
|
|
64
55
|
/** @type {number} */
|
|
65
56
|
W: 0,
|
|
@@ -212,6 +203,7 @@
|
|
|
212
203
|
DEV: assert(isNumber(value), "norm: 1st param must be a number");
|
|
213
204
|
DEV: assert(isNumber(start), "norm: 2nd param must be a number");
|
|
214
205
|
DEV: assert(isNumber(stop), "norm: 3rd param must be a number");
|
|
206
|
+
DEV: assert(start !== stop, "norm: the 2nd param must be different than the 3rd param");
|
|
215
207
|
return instance.map(value, start, stop, 0, 1);
|
|
216
208
|
},
|
|
217
209
|
/**
|
|
@@ -412,6 +404,62 @@
|
|
|
412
404
|
_ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
|
|
413
405
|
instance.fill(color);
|
|
414
406
|
},
|
|
407
|
+
/**
|
|
408
|
+
* Draw a ellipse outline
|
|
409
|
+
*
|
|
410
|
+
* @param {number} x
|
|
411
|
+
* @param {number} y
|
|
412
|
+
* @param {number} radiusX
|
|
413
|
+
* @param {number} radiusY
|
|
414
|
+
* @param {number} [color=0] the color index
|
|
415
|
+
*/
|
|
416
|
+
oval(x, y, radiusX, radiusY, color) {
|
|
417
|
+
DEV: assert(isNumber(x), "oval: 1st param must be a number");
|
|
418
|
+
DEV: assert(isNumber(y), "oval: 2nd param must be a number");
|
|
419
|
+
DEV: assert(
|
|
420
|
+
isNumber(radiusX) && radiusX >= 0,
|
|
421
|
+
"oval: 3rd param must be a positive number or zero"
|
|
422
|
+
);
|
|
423
|
+
DEV: assert(
|
|
424
|
+
isNumber(radiusY) && radiusY >= 0,
|
|
425
|
+
"oval: 4th param must be a positive number or zero"
|
|
426
|
+
);
|
|
427
|
+
DEV: assert(
|
|
428
|
+
null == color || isNumber(color) && color >= 0,
|
|
429
|
+
"oval: 5th param must be a positive number or zero"
|
|
430
|
+
);
|
|
431
|
+
_ctx.beginPath();
|
|
432
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
|
|
433
|
+
instance.stroke(color);
|
|
434
|
+
},
|
|
435
|
+
/**
|
|
436
|
+
* Draw a color-filled ellipse
|
|
437
|
+
*
|
|
438
|
+
* @param {number} x
|
|
439
|
+
* @param {number} y
|
|
440
|
+
* @param {number} radiusX
|
|
441
|
+
* @param {number} radiusY
|
|
442
|
+
* @param {number} [color=0] the color index
|
|
443
|
+
*/
|
|
444
|
+
ovalfill(x, y, radiusX, radiusY, color) {
|
|
445
|
+
DEV: assert(isNumber(x), "ovalfill: 1st param must be a number");
|
|
446
|
+
DEV: assert(isNumber(y), "ovalfill: 2nd param must be a number");
|
|
447
|
+
DEV: assert(
|
|
448
|
+
isNumber(radiusX) && radiusX >= 0,
|
|
449
|
+
"ovalfill: 3rd param must be a positive number or zero"
|
|
450
|
+
);
|
|
451
|
+
DEV: assert(
|
|
452
|
+
isNumber(radiusY) && radiusY >= 0,
|
|
453
|
+
"ovalfill: 4th param must be a positive number or zero"
|
|
454
|
+
);
|
|
455
|
+
DEV: assert(
|
|
456
|
+
null == color || isNumber(color) && color >= 0,
|
|
457
|
+
"ovalfill: 5th param must be a positive number or zero"
|
|
458
|
+
);
|
|
459
|
+
_ctx.beginPath();
|
|
460
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
|
|
461
|
+
instance.fill(color);
|
|
462
|
+
},
|
|
415
463
|
/**
|
|
416
464
|
* Draw a line
|
|
417
465
|
*
|
|
@@ -812,9 +860,9 @@
|
|
|
812
860
|
DEV: assert("string" === typeof eventName, "listen: 1st param must be a string");
|
|
813
861
|
DEV: assert("function" === typeof callback, "listen: 2nd param must be a function");
|
|
814
862
|
eventName = eventName.toLowerCase();
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
return () =>
|
|
863
|
+
_eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
|
|
864
|
+
_eventListeners[eventName].add(callback);
|
|
865
|
+
return () => _eventListeners && _eventListeners[eventName].delete(callback);
|
|
818
866
|
},
|
|
819
867
|
/**
|
|
820
868
|
* Call all listeners attached to a game event
|
|
@@ -906,7 +954,7 @@
|
|
|
906
954
|
// 3
|
|
907
955
|
_scale,
|
|
908
956
|
// 4
|
|
909
|
-
|
|
957
|
+
_eventListeners,
|
|
910
958
|
// 5
|
|
911
959
|
_colors,
|
|
912
960
|
// 6
|
|
@@ -934,10 +982,10 @@
|
|
|
934
982
|
cancelAnimationFrame(_rafid);
|
|
935
983
|
_rafid = 0;
|
|
936
984
|
instance.emit("quit");
|
|
985
|
+
_eventListeners = {};
|
|
937
986
|
for (const removeListener of _browserEventListeners) {
|
|
938
987
|
removeListener();
|
|
939
988
|
}
|
|
940
|
-
_events = {};
|
|
941
989
|
if (settings.global) {
|
|
942
990
|
for (const key in instance) {
|
|
943
991
|
delete root[key];
|
|
@@ -947,12 +995,12 @@
|
|
|
947
995
|
_initialized = false;
|
|
948
996
|
}
|
|
949
997
|
};
|
|
950
|
-
for (const k of
|
|
998
|
+
for (const k of _mathFunctions.split(",")) {
|
|
951
999
|
instance[k] = math[k];
|
|
952
1000
|
}
|
|
953
1001
|
function init() {
|
|
954
1002
|
const source = settings.loop ? settings.loop : root;
|
|
955
|
-
for (const event
|
|
1003
|
+
for (const event of _coreEvents.split(",")) {
|
|
956
1004
|
if (source[event]) instance.listen(event, source[event]);
|
|
957
1005
|
}
|
|
958
1006
|
for (const [callback, config] of _plugins) {
|
|
@@ -1233,17 +1281,19 @@
|
|
|
1233
1281
|
`Litecanvas' option "width" is required when the option "height" is defined`
|
|
1234
1282
|
);
|
|
1235
1283
|
const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
|
|
1236
|
-
instance.def("W",
|
|
1237
|
-
instance.def("H",
|
|
1284
|
+
instance.def("W", width);
|
|
1285
|
+
instance.def("H", height);
|
|
1286
|
+
_canvas.width = width;
|
|
1287
|
+
_canvas.height = height;
|
|
1238
1288
|
if (settings.autoscale) {
|
|
1239
1289
|
if (!_canvas.style.display) {
|
|
1240
1290
|
_canvas.style.display = "block";
|
|
1241
1291
|
_canvas.style.margin = "auto";
|
|
1242
1292
|
}
|
|
1243
|
-
_scale = math.min(root.innerWidth /
|
|
1293
|
+
_scale = math.min(root.innerWidth / width, root.innerHeight / height);
|
|
1244
1294
|
_scale = (settings.pixelart ? ~~_scale : _scale) || 1;
|
|
1245
|
-
_canvas.style.width =
|
|
1246
|
-
_canvas.style.height =
|
|
1295
|
+
_canvas.style.width = width * _scale + "px";
|
|
1296
|
+
_canvas.style.height = height * _scale + "px";
|
|
1247
1297
|
}
|
|
1248
1298
|
if (!settings.antialias || settings.pixelart) {
|
|
1249
1299
|
_ctx.imageSmoothingEnabled = false;
|
|
@@ -1256,8 +1306,8 @@
|
|
|
1256
1306
|
}
|
|
1257
1307
|
}
|
|
1258
1308
|
function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
|
|
1259
|
-
if (!
|
|
1260
|
-
for (const callback of
|
|
1309
|
+
if (!_eventListeners[eventName]) return;
|
|
1310
|
+
for (const callback of _eventListeners[eventName]) {
|
|
1261
1311
|
callback(arg1, arg2, arg3, arg4);
|
|
1262
1312
|
}
|
|
1263
1313
|
}
|
package/dist/dist.js
CHANGED
|
@@ -45,16 +45,7 @@
|
|
|
45
45
|
animate: true
|
|
46
46
|
};
|
|
47
47
|
settings = Object.assign(defaults, settings);
|
|
48
|
-
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
49
|
-
init: null,
|
|
50
|
-
update: null,
|
|
51
|
-
draw: null,
|
|
52
|
-
resized: null,
|
|
53
|
-
tap: null,
|
|
54
|
-
untap: null,
|
|
55
|
-
tapping: null,
|
|
56
|
-
tapped: null
|
|
57
|
-
};
|
|
48
|
+
let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _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 = {};
|
|
58
49
|
const instance = {
|
|
59
50
|
/** @type {number} */
|
|
60
51
|
W: 0,
|
|
@@ -306,6 +297,34 @@
|
|
|
306
297
|
_ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
|
|
307
298
|
instance.fill(color);
|
|
308
299
|
},
|
|
300
|
+
/**
|
|
301
|
+
* Draw a ellipse outline
|
|
302
|
+
*
|
|
303
|
+
* @param {number} x
|
|
304
|
+
* @param {number} y
|
|
305
|
+
* @param {number} radiusX
|
|
306
|
+
* @param {number} radiusY
|
|
307
|
+
* @param {number} [color=0] the color index
|
|
308
|
+
*/
|
|
309
|
+
oval(x, y, radiusX, radiusY, color) {
|
|
310
|
+
_ctx.beginPath();
|
|
311
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
|
|
312
|
+
instance.stroke(color);
|
|
313
|
+
},
|
|
314
|
+
/**
|
|
315
|
+
* Draw a color-filled ellipse
|
|
316
|
+
*
|
|
317
|
+
* @param {number} x
|
|
318
|
+
* @param {number} y
|
|
319
|
+
* @param {number} radiusX
|
|
320
|
+
* @param {number} radiusY
|
|
321
|
+
* @param {number} [color=0] the color index
|
|
322
|
+
*/
|
|
323
|
+
ovalfill(x, y, radiusX, radiusY, color) {
|
|
324
|
+
_ctx.beginPath();
|
|
325
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
|
|
326
|
+
instance.fill(color);
|
|
327
|
+
},
|
|
309
328
|
/**
|
|
310
329
|
* Draw a line
|
|
311
330
|
*
|
|
@@ -613,9 +632,9 @@
|
|
|
613
632
|
*/
|
|
614
633
|
listen(eventName, callback) {
|
|
615
634
|
eventName = eventName.toLowerCase();
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return () =>
|
|
635
|
+
_eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
|
|
636
|
+
_eventListeners[eventName].add(callback);
|
|
637
|
+
return () => _eventListeners && _eventListeners[eventName].delete(callback);
|
|
619
638
|
},
|
|
620
639
|
/**
|
|
621
640
|
* Call all listeners attached to a game event
|
|
@@ -689,7 +708,7 @@
|
|
|
689
708
|
// 3
|
|
690
709
|
_scale,
|
|
691
710
|
// 4
|
|
692
|
-
|
|
711
|
+
_eventListeners,
|
|
693
712
|
// 5
|
|
694
713
|
_colors,
|
|
695
714
|
// 6
|
|
@@ -717,10 +736,10 @@
|
|
|
717
736
|
cancelAnimationFrame(_rafid);
|
|
718
737
|
_rafid = 0;
|
|
719
738
|
instance.emit("quit");
|
|
739
|
+
_eventListeners = {};
|
|
720
740
|
for (const removeListener of _browserEventListeners) {
|
|
721
741
|
removeListener();
|
|
722
742
|
}
|
|
723
|
-
_events = {};
|
|
724
743
|
if (settings.global) {
|
|
725
744
|
for (const key in instance) {
|
|
726
745
|
delete root[key];
|
|
@@ -730,12 +749,12 @@
|
|
|
730
749
|
_initialized = false;
|
|
731
750
|
}
|
|
732
751
|
};
|
|
733
|
-
for (const k of
|
|
752
|
+
for (const k of _mathFunctions.split(",")) {
|
|
734
753
|
instance[k] = math[k];
|
|
735
754
|
}
|
|
736
755
|
function init() {
|
|
737
756
|
const source = settings.loop ? settings.loop : root;
|
|
738
|
-
for (const event
|
|
757
|
+
for (const event of _coreEvents.split(",")) {
|
|
739
758
|
if (source[event]) instance.listen(event, source[event]);
|
|
740
759
|
}
|
|
741
760
|
for (const [callback, config] of _plugins) {
|
|
@@ -991,17 +1010,19 @@
|
|
|
991
1010
|
}
|
|
992
1011
|
function resizeCanvas() {
|
|
993
1012
|
const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
|
|
994
|
-
instance.def("W",
|
|
995
|
-
instance.def("H",
|
|
1013
|
+
instance.def("W", width);
|
|
1014
|
+
instance.def("H", height);
|
|
1015
|
+
_canvas.width = width;
|
|
1016
|
+
_canvas.height = height;
|
|
996
1017
|
if (settings.autoscale) {
|
|
997
1018
|
if (!_canvas.style.display) {
|
|
998
1019
|
_canvas.style.display = "block";
|
|
999
1020
|
_canvas.style.margin = "auto";
|
|
1000
1021
|
}
|
|
1001
|
-
_scale = math.min(root.innerWidth /
|
|
1022
|
+
_scale = math.min(root.innerWidth / width, root.innerHeight / height);
|
|
1002
1023
|
_scale = (settings.pixelart ? ~~_scale : _scale) || 1;
|
|
1003
|
-
_canvas.style.width =
|
|
1004
|
-
_canvas.style.height =
|
|
1024
|
+
_canvas.style.width = width * _scale + "px";
|
|
1025
|
+
_canvas.style.height = height * _scale + "px";
|
|
1005
1026
|
}
|
|
1006
1027
|
if (!settings.antialias || settings.pixelart) {
|
|
1007
1028
|
_ctx.imageSmoothingEnabled = false;
|
|
@@ -1014,8 +1035,8 @@
|
|
|
1014
1035
|
}
|
|
1015
1036
|
}
|
|
1016
1037
|
function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
|
|
1017
|
-
if (!
|
|
1018
|
-
for (const callback of
|
|
1038
|
+
if (!_eventListeners[eventName]) return;
|
|
1039
|
+
for (const callback of _eventListeners[eventName]) {
|
|
1019
1040
|
callback(arg1, arg2, arg3, arg4);
|
|
1020
1041
|
}
|
|
1021
1042
|
}
|
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,
|
|
1
|
+
(()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,i=Math,n=2*i.PI,l=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,i=.05,n=220,l=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,v=0,w=0,b=1,x=0,y=0,k=0)=>{let E=Math,z=2*E.PI,P=c*=500*z/44100/44100,T=n*=(1-i+2*i*E.random(i=[]))*z/44100,C=0,I=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,N=E.cos(D),F=E.sin,q=F(D)/4,B=1+q,H=-2*N/B,O=(1-q)/B,V=(1+M*N)/2/B,W=-(M+N)/B,R=0,G=0,$=0,j=0;for(l=44100*l+9,x*=44100,r*=44100,o*=44100,w*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=l+x+r+o+w|0;A<M;i[A++]=Y*a)++X%(100*v|0)||(Y=s?1<s?2<s?3<s?F(C*C):E.max(E.min(E.tan(C),1),-1):1-(2*C/z%2+2)%2:1-4*E.abs(E.round(C/z)-C/z):F(C),Y=(h?1-y+y*F(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<l?A/l:A<l+x?1-(A-l)/x*(1-b):A<l+x+r?b:A<M-w?(M-A-w)/o*b:0),Y=w?Y/2+(w>A?0:(A<M-w?1:(M-A)/w)*i[A-w|0]/2/a):Y,k&&(Y=j=V*R+W*(R=G)+V*(G=Y)-O*$-H*($=j))),C+=(D=(n+=c+=d)*E.cos(m*I++))+D*g*F(A**5),L&&++L>u&&(n+=p,T+=p,L=0),!h||++S%h||(n=T,c=P,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(i),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,v=1/60,w=0,b,x="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],P={},T={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=>i.PI/180*e,rad2deg:e=>180/i.PI*e,round:(e,t=0)=>{if(!t)return i.round(e);let a=10**t;return i.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,i,n,l){let r=(e-t)/(a-t)*(n-i)+i;return l?T.clamp(r,i,n):r},norm:(e,t,a)=>T.map(e,t,a,0,1),wave:(e,t,a,i=Math.sin)=>e+(i(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>i.floor(T.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):T.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,i,n,l){u.beginPath(),u[l?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~i+2*h,l),T.stroke(n)},rectfill(e,t,a,i,n,l){u.beginPath(),u[l?"roundRect":"rect"](~~e,~~t,~~a,~~i,l),T.fill(n)},circ(e,t,a,i){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.stroke(i)},circfill(e,t,a,i){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.fill(i)},oval(e,t,a,i,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~i,0,0,n),T.stroke(l)},ovalfill(e,t,a,i,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~i,0,0,n),T.fill(l)},line(e,t,a,i,n){u.beginPath();let l=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~i);u.moveTo(~~e+l,~~t+r),u.lineTo(~~a+l,~~i+r),T.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,i=3,n="normal"){u.font=`${n} ${y}px ${x}`,u.fillStyle=E[~~i%E.length],u.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,i={}){let n=i.canvas||new OffscreenCanvas(1,1),l=i.scale||1,r=u;if(n.width=e*l,n.height=t*l,(u=n.getContext("2d")).scale(l,l),Array.isArray(a)){let e=0,t=0;for(let i of(u.imageSmoothingEnabled=!1,a)){for(let a of i)" "!==a&&"."!==a&&T.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,n.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=T.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,i=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==i)&&((e=e.slice())[0]=i*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(P[e=e.toLowerCase()]=P[e]||new Set,P[e].add(t),()=>P&&P[e].delete(t)),emit(e,t,a,i,n){f&&(L("before:"+(e=e.toLowerCase()),t,a,i,n),L(e,t,a,i,n),L("after:"+e,t,a,i,n))},pal(t=e){E=t},def(e,i){T[e]=i,t.global&&(a[e]=i)},timescale(e){g=e},framerate(e){v=1/~~e},stat(e){let i={index:e,value:[t,f,b,p,P,E,z,g,a.zzfxV||1,k,y,x][e]};return T.emit("stat",i),i.value},quit(){for(let e of(cancelAnimationFrame(b),b=0,T.emit("quit"),P={},r))e();if(t.global){for(let e in T)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))T[e]=i[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]&&T.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&o(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,i=(e,a,i)=>{let n={x:a,y:i,startX:a,startY:i,ts:performance.now()};return t.set(e,n),n},n=(e,a,n)=>{let l=t.get(e)||i(e);l.x=a,l.y=n},l=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(d,"mousedown",t=>{if(0===t.button){r(t);let[a,n]=e(t.pageX,t.pageY);T.emit("tap",a,n,0),i(0,a,n),s=!0}}),o(d,"mouseup",a=>{if(0===a.button){r(a);let i=t.get(0),[n,o]=e(a.pageX,a.pageY);l(i)&&T.emit("tapped",i.startX,i.startY,0),T.emit("untap",n,o,0),t.delete(0),s=!1}}),o(d,"mousemove",t=>{r(t);let[a,i]=e(t.pageX,t.pageY);T.def("MX",a),T.def("MY",i),s&&(T.emit("tapping",a,i,0),n(0,a,i))}),o(d,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);T.emit("tap",t,n,a.identifier+1),i(a.identifier+1,t,n)}}),o(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,i]=e(a.pageX,a.pageY);T.emit("tapping",t,i,a.identifier+1),n(a.identifier+1,t,i)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,i]of t)a.includes(e)||(l(i)&&T.emit("tapped",i.startX,i.startY,e),T.emit("untap",i.x,i.y,e),t.delete(e))};o(d,"touchend",f),o(d,"touchcancel",f),o(a,"blur",()=>{for(let[e,a]of(s=!1,t))T.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,i=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let i=a.key.toLowerCase();e.has(i)||(e.add(i),t.add(i))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),T.listen("after:update",()=>t.clear()),T.def("iskeydown",t=>i(e,t)),T.def("iskeypressed",e=>i(t,e))}f=!0,T.emit("init",T),m=performance.now(),b=l(I)}function I(e){let a=0;if(t.animate){for(w+=i.min(.1,(e-m)/1e3),m=e;w>=v;)a++,T.emit("update",v*g,a),T.def("T",T.T+v*g),w-=v;b&&(b=l(I))}else a=1;a&&(T.textalign("start","top"),T.emit("draw"))}function A(){let e=t.width||a.innerWidth,n=t.height||t.width||a.innerHeight;T.def("W",e),T.def("H",n),d.width=e,d.height=n,t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=i.min(a.innerWidth/e,a.innerHeight/n),p=(t.pixelart?~~p:p)||1,d.style.width=e*p+"px",d.style.height=n*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),T.emit("resized",p),T.cls(0),t.animate||l(I)}function L(e,t,a,i,n){if(P[e])for(let l of P[e])l(t,a,i,n)}function S(e,t){let a=e(T,t);for(let e in a)T.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,T),a.ENGINE=T}return u=(d=(d="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),o(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>l(C)):l(C),T}})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litecanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.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>",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"esbuild": "^0.25.5",
|
|
14
14
|
"gzip-size": "^7.0.0",
|
|
15
15
|
"jsdom": "^26.1.0",
|
|
16
|
-
"prettier": "^3.6.
|
|
16
|
+
"prettier": "^3.6.2",
|
|
17
17
|
"tap-min": "^3.0.0"
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://litecanvas.github.io/about.html",
|
package/src/index.js
CHANGED
|
@@ -74,21 +74,15 @@ export default function litecanvas(settings = {}) {
|
|
|
74
74
|
_colors = defaultPalette,
|
|
75
75
|
/** @type {number[]} */
|
|
76
76
|
_defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
77
|
+
/** @type {string} */
|
|
78
|
+
_coreEvents = 'init,update,draw,tap,untap,tapping,tapped,resized',
|
|
79
|
+
/** @type {string} list of functions copied from `Math` module*/
|
|
80
|
+
_mathFunctions =
|
|
81
|
+
'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp',
|
|
77
82
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* @type {Object<string,Set<Function>>}
|
|
83
|
+
* @type {Object<string,Set<Function>>} game event listeners
|
|
81
84
|
*/
|
|
82
|
-
|
|
83
|
-
init: null,
|
|
84
|
-
update: null,
|
|
85
|
-
draw: null,
|
|
86
|
-
resized: null,
|
|
87
|
-
tap: null,
|
|
88
|
-
untap: null,
|
|
89
|
-
tapping: null,
|
|
90
|
-
tapped: null,
|
|
91
|
-
}
|
|
85
|
+
_eventListeners = {}
|
|
92
86
|
|
|
93
87
|
/** @type {Omit<LitecanvasInstance,'PI'|'sin'|'cos'|'atan2'|'hypot'|'tan'|'abs'|'ceil'|'floor'|'trunc'|'min'|'max'|'pow'|'sqrt'|'sign'|'exp'|'iskeydown'|'iskeypressed'>} */
|
|
94
88
|
const instance = {
|
|
@@ -264,6 +258,7 @@ export default function litecanvas(settings = {}) {
|
|
|
264
258
|
DEV: assert(isNumber(value), 'norm: 1st param must be a number')
|
|
265
259
|
DEV: assert(isNumber(start), 'norm: 2nd param must be a number')
|
|
266
260
|
DEV: assert(isNumber(stop), 'norm: 3rd param must be a number')
|
|
261
|
+
DEV: assert(start !== stop, 'norm: the 2nd param must be different than the 3rd param')
|
|
267
262
|
|
|
268
263
|
return instance.map(value, start, stop, 0, 1)
|
|
269
264
|
},
|
|
@@ -485,6 +480,66 @@ export default function litecanvas(settings = {}) {
|
|
|
485
480
|
instance.fill(color)
|
|
486
481
|
},
|
|
487
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Draw a ellipse outline
|
|
485
|
+
*
|
|
486
|
+
* @param {number} x
|
|
487
|
+
* @param {number} y
|
|
488
|
+
* @param {number} radiusX
|
|
489
|
+
* @param {number} radiusY
|
|
490
|
+
* @param {number} [color=0] the color index
|
|
491
|
+
*/
|
|
492
|
+
oval(x, y, radiusX, radiusY, color) {
|
|
493
|
+
DEV: assert(isNumber(x), 'oval: 1st param must be a number')
|
|
494
|
+
DEV: assert(isNumber(y), 'oval: 2nd param must be a number')
|
|
495
|
+
DEV: assert(
|
|
496
|
+
isNumber(radiusX) && radiusX >= 0,
|
|
497
|
+
'oval: 3rd param must be a positive number or zero'
|
|
498
|
+
)
|
|
499
|
+
DEV: assert(
|
|
500
|
+
isNumber(radiusY) && radiusY >= 0,
|
|
501
|
+
'oval: 4th param must be a positive number or zero'
|
|
502
|
+
)
|
|
503
|
+
DEV: assert(
|
|
504
|
+
null == color || (isNumber(color) && color >= 0),
|
|
505
|
+
'oval: 5th param must be a positive number or zero'
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
_ctx.beginPath()
|
|
509
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
|
|
510
|
+
instance.stroke(color)
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Draw a color-filled ellipse
|
|
515
|
+
*
|
|
516
|
+
* @param {number} x
|
|
517
|
+
* @param {number} y
|
|
518
|
+
* @param {number} radiusX
|
|
519
|
+
* @param {number} radiusY
|
|
520
|
+
* @param {number} [color=0] the color index
|
|
521
|
+
*/
|
|
522
|
+
ovalfill(x, y, radiusX, radiusY, color) {
|
|
523
|
+
DEV: assert(isNumber(x), 'ovalfill: 1st param must be a number')
|
|
524
|
+
DEV: assert(isNumber(y), 'ovalfill: 2nd param must be a number')
|
|
525
|
+
DEV: assert(
|
|
526
|
+
isNumber(radiusX) && radiusX >= 0,
|
|
527
|
+
'ovalfill: 3rd param must be a positive number or zero'
|
|
528
|
+
)
|
|
529
|
+
DEV: assert(
|
|
530
|
+
isNumber(radiusY) && radiusY >= 0,
|
|
531
|
+
'ovalfill: 4th param must be a positive number or zero'
|
|
532
|
+
)
|
|
533
|
+
DEV: assert(
|
|
534
|
+
null == color || (isNumber(color) && color >= 0),
|
|
535
|
+
'ovalfill: 5th param must be a positive number or zero'
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
_ctx.beginPath()
|
|
539
|
+
_ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
|
|
540
|
+
instance.fill(color)
|
|
541
|
+
},
|
|
542
|
+
|
|
488
543
|
/**
|
|
489
544
|
* Draw a line
|
|
490
545
|
*
|
|
@@ -956,11 +1011,11 @@ export default function litecanvas(settings = {}) {
|
|
|
956
1011
|
|
|
957
1012
|
eventName = eventName.toLowerCase()
|
|
958
1013
|
|
|
959
|
-
|
|
960
|
-
|
|
1014
|
+
_eventListeners[eventName] = _eventListeners[eventName] || new Set()
|
|
1015
|
+
_eventListeners[eventName].add(callback)
|
|
961
1016
|
|
|
962
1017
|
// return a function to remove this event listener
|
|
963
|
-
return () =>
|
|
1018
|
+
return () => _eventListeners && _eventListeners[eventName].delete(callback)
|
|
964
1019
|
},
|
|
965
1020
|
|
|
966
1021
|
/**
|
|
@@ -1063,7 +1118,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1063
1118
|
// 3
|
|
1064
1119
|
_scale,
|
|
1065
1120
|
// 4
|
|
1066
|
-
|
|
1121
|
+
_eventListeners,
|
|
1067
1122
|
// 5
|
|
1068
1123
|
_colors,
|
|
1069
1124
|
// 6
|
|
@@ -1101,14 +1156,14 @@ export default function litecanvas(settings = {}) {
|
|
|
1101
1156
|
// emit "quit" event to manual clean ups
|
|
1102
1157
|
instance.emit('quit')
|
|
1103
1158
|
|
|
1159
|
+
// clear all engine event listeners
|
|
1160
|
+
_eventListeners = {}
|
|
1161
|
+
|
|
1104
1162
|
// clear all browser event listeners
|
|
1105
1163
|
for (const removeListener of _browserEventListeners) {
|
|
1106
1164
|
removeListener()
|
|
1107
1165
|
}
|
|
1108
1166
|
|
|
1109
|
-
// clear all engine event listeners
|
|
1110
|
-
_events = {}
|
|
1111
|
-
|
|
1112
1167
|
// maybe clear global context
|
|
1113
1168
|
if (settings.global) {
|
|
1114
1169
|
for (const key in instance) {
|
|
@@ -1124,7 +1179,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1124
1179
|
}
|
|
1125
1180
|
|
|
1126
1181
|
// prettier-ignore
|
|
1127
|
-
for (const k of
|
|
1182
|
+
for (const k of _mathFunctions.split(',')) {
|
|
1128
1183
|
// import native Math functions
|
|
1129
1184
|
instance[k] = math[k]
|
|
1130
1185
|
}
|
|
@@ -1132,7 +1187,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1132
1187
|
function init() {
|
|
1133
1188
|
// setup default event listeners
|
|
1134
1189
|
const source = settings.loop ? settings.loop : root
|
|
1135
|
-
for (const event
|
|
1190
|
+
for (const event of _coreEvents.split(',')) {
|
|
1136
1191
|
if (source[event]) instance.listen(event, source[event])
|
|
1137
1192
|
}
|
|
1138
1193
|
|
|
@@ -1486,8 +1541,11 @@ export default function litecanvas(settings = {}) {
|
|
|
1486
1541
|
const width = settings.width || root.innerWidth,
|
|
1487
1542
|
height = settings.height || settings.width || root.innerHeight
|
|
1488
1543
|
|
|
1489
|
-
instance.def('W',
|
|
1490
|
-
instance.def('H',
|
|
1544
|
+
instance.def('W', width)
|
|
1545
|
+
instance.def('H', height)
|
|
1546
|
+
|
|
1547
|
+
_canvas.width = width
|
|
1548
|
+
_canvas.height = height
|
|
1491
1549
|
|
|
1492
1550
|
if (settings.autoscale) {
|
|
1493
1551
|
if (!_canvas.style.display) {
|
|
@@ -1495,11 +1553,11 @@ export default function litecanvas(settings = {}) {
|
|
|
1495
1553
|
_canvas.style.margin = 'auto'
|
|
1496
1554
|
}
|
|
1497
1555
|
|
|
1498
|
-
_scale = math.min(root.innerWidth /
|
|
1556
|
+
_scale = math.min(root.innerWidth / width, root.innerHeight / height)
|
|
1499
1557
|
_scale = (settings.pixelart ? ~~_scale : _scale) || 1
|
|
1500
1558
|
|
|
1501
|
-
_canvas.style.width =
|
|
1502
|
-
_canvas.style.height =
|
|
1559
|
+
_canvas.style.width = width * _scale + 'px'
|
|
1560
|
+
_canvas.style.height = height * _scale + 'px'
|
|
1503
1561
|
}
|
|
1504
1562
|
|
|
1505
1563
|
// restore canvas image rendering properties
|
|
@@ -1527,8 +1585,8 @@ export default function litecanvas(settings = {}) {
|
|
|
1527
1585
|
* @param {*} arg4
|
|
1528
1586
|
*/
|
|
1529
1587
|
function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
|
|
1530
|
-
if (!
|
|
1531
|
-
for (const callback of
|
|
1588
|
+
if (!_eventListeners[eventName]) return
|
|
1589
|
+
for (const callback of _eventListeners[eventName]) {
|
|
1532
1590
|
callback(arg1, arg2, arg3, arg4)
|
|
1533
1591
|
}
|
|
1534
1592
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -283,6 +283,26 @@ declare global {
|
|
|
283
283
|
* @param [color=0] the color index
|
|
284
284
|
*/
|
|
285
285
|
function circfill(x: number, y: number, radius: number, color?: number): void
|
|
286
|
+
/**
|
|
287
|
+
* Draw a ellipse outline
|
|
288
|
+
*
|
|
289
|
+
* @param x
|
|
290
|
+
* @param y
|
|
291
|
+
* @param radiusX
|
|
292
|
+
* @param radiusY
|
|
293
|
+
* @param [color=0] the color index
|
|
294
|
+
*/
|
|
295
|
+
function oval(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
|
|
296
|
+
/**
|
|
297
|
+
* Draw a color-filled ellipse
|
|
298
|
+
*
|
|
299
|
+
* @param x
|
|
300
|
+
* @param y
|
|
301
|
+
* @param radiusX
|
|
302
|
+
* @param radiusY
|
|
303
|
+
* @param [color=0] the color index
|
|
304
|
+
*/
|
|
305
|
+
function ovalfill(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
|
|
286
306
|
/**
|
|
287
307
|
* Draw a line
|
|
288
308
|
*
|
package/types/types.d.ts
CHANGED
|
@@ -274,6 +274,26 @@ type LitecanvasInstance = {
|
|
|
274
274
|
* @param [color=0] the color index
|
|
275
275
|
*/
|
|
276
276
|
circfill(x: number, y: number, radius: number, color?: number): void
|
|
277
|
+
/**
|
|
278
|
+
* Draw a ellipse outline
|
|
279
|
+
*
|
|
280
|
+
* @param x
|
|
281
|
+
* @param y
|
|
282
|
+
* @param radiusX
|
|
283
|
+
* @param radiusY
|
|
284
|
+
* @param [color=0] the color index
|
|
285
|
+
*/
|
|
286
|
+
oval(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
|
|
287
|
+
/**
|
|
288
|
+
* Draw a color-filled ellipse
|
|
289
|
+
*
|
|
290
|
+
* @param x
|
|
291
|
+
* @param y
|
|
292
|
+
* @param radiusX
|
|
293
|
+
* @param radiusY
|
|
294
|
+
* @param [color=0] the color index
|
|
295
|
+
*/
|
|
296
|
+
ovalfill(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
|
|
277
297
|
/**
|
|
278
298
|
* Draw a line
|
|
279
299
|
*
|