litecanvas 0.205.0 → 0.206.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dist.dev.js +35 -33
- package/dist/dist.js +32 -24
- package/dist/dist.min.js +1 -1
- package/package.json +4 -4
- package/src/index.js +50 -35
- package/src/version.js +1 -1
package/dist/dist.dev.js
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
var assert = (condition, message = "Assertion failed") => {
|
|
116
116
|
if (!condition) throw new Error("[litecanvas] " + message);
|
|
117
117
|
};
|
|
118
|
-
var version = "0.
|
|
118
|
+
var version = "0.206.1";
|
|
119
119
|
function litecanvas(settings = {}) {
|
|
120
120
|
const root = window,
|
|
121
121
|
math = Math,
|
|
@@ -146,8 +146,9 @@
|
|
|
146
146
|
keyboardEvents: true,
|
|
147
147
|
};
|
|
148
148
|
settings = Object.assign(defaults, settings);
|
|
149
|
-
let
|
|
150
|
-
|
|
149
|
+
let _loop = settings.loop,
|
|
150
|
+
_initialized = false,
|
|
151
|
+
_paused,
|
|
151
152
|
_canvas,
|
|
152
153
|
_canvasScale = 1,
|
|
153
154
|
_ctx,
|
|
@@ -156,7 +157,7 @@
|
|
|
156
157
|
_lastFrameTime,
|
|
157
158
|
_fpsInterval = 1e3 / 60,
|
|
158
159
|
_accumulated,
|
|
159
|
-
_rafid,
|
|
160
|
+
_rafid = 0,
|
|
160
161
|
_defaultTextColor = 3,
|
|
161
162
|
_fontFamily = "sans-serif",
|
|
162
163
|
_fontSize = 20,
|
|
@@ -165,8 +166,6 @@
|
|
|
165
166
|
_colorPalette = defaultPalette,
|
|
166
167
|
_colorPaletteState = [],
|
|
167
168
|
_defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
168
|
-
_mathFunctions =
|
|
169
|
-
"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp",
|
|
170
169
|
_eventListeners = {};
|
|
171
170
|
const instance = {
|
|
172
171
|
W: 0,
|
|
@@ -846,17 +845,17 @@
|
|
|
846
845
|
callback(_ctx);
|
|
847
846
|
_ctx.clip();
|
|
848
847
|
},
|
|
849
|
-
sfx(zzfxParams, pitchSlide
|
|
848
|
+
sfx(zzfxParams, pitchSlide, volumeFactor) {
|
|
850
849
|
DEV: assert(
|
|
851
850
|
null == zzfxParams || Array.isArray(zzfxParams),
|
|
852
851
|
loggerPrefix + "sfx() 1st param must be an array",
|
|
853
852
|
);
|
|
854
853
|
DEV: assert(
|
|
855
|
-
isNumber(pitchSlide),
|
|
854
|
+
null == pitchSlide || isNumber(pitchSlide),
|
|
856
855
|
loggerPrefix + "sfx() 2nd param must be a number",
|
|
857
856
|
);
|
|
858
857
|
DEV: assert(
|
|
859
|
-
isNumber(volumeFactor),
|
|
858
|
+
null == volumeFactor || isNumber(volumeFactor),
|
|
860
859
|
loggerPrefix + "sfx() 3rd param must be a number",
|
|
861
860
|
);
|
|
862
861
|
if (
|
|
@@ -865,10 +864,10 @@
|
|
|
865
864
|
) {
|
|
866
865
|
return false;
|
|
867
866
|
}
|
|
868
|
-
zzfxParams
|
|
869
|
-
if (pitchSlide
|
|
867
|
+
zzfxParams ||= _defaultSound;
|
|
868
|
+
if (pitchSlide || volumeFactor) {
|
|
870
869
|
zzfxParams = zzfxParams.slice();
|
|
871
|
-
zzfxParams[0] = volumeFactor * (zzfxParams[0] || 1);
|
|
870
|
+
zzfxParams[0] = (volumeFactor || 1) * (zzfxParams[0] || 1);
|
|
872
871
|
zzfxParams[10] = ~~zzfxParams[10] + pitchSlide;
|
|
873
872
|
}
|
|
874
873
|
zzfx.apply(0, zzfxParams);
|
|
@@ -930,7 +929,7 @@
|
|
|
930
929
|
eventName = lowerCase(eventName);
|
|
931
930
|
triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
|
|
932
931
|
if (
|
|
933
|
-
!
|
|
932
|
+
!_loop &&
|
|
934
933
|
root[eventName] !== instance[eventName] &&
|
|
935
934
|
"function" === typeof root[eventName]
|
|
936
935
|
) {
|
|
@@ -1033,7 +1032,7 @@
|
|
|
1033
1032
|
pause() {
|
|
1034
1033
|
if (!_paused) {
|
|
1035
1034
|
_paused = true;
|
|
1036
|
-
cancelAnimationFrame(_rafid);
|
|
1035
|
+
_rafid = ~~cancelAnimationFrame(_rafid);
|
|
1037
1036
|
instance.emit("paused");
|
|
1038
1037
|
}
|
|
1039
1038
|
},
|
|
@@ -1044,10 +1043,8 @@
|
|
|
1044
1043
|
'resume() cannot be called before the "init" event and neither after the quit() function',
|
|
1045
1044
|
);
|
|
1046
1045
|
if (_initialized && _paused) {
|
|
1046
|
+
startGameLoop();
|
|
1047
1047
|
_paused = false;
|
|
1048
|
-
_accumulated = _fpsInterval;
|
|
1049
|
-
_lastFrameTime = perf.now();
|
|
1050
|
-
_rafid = raf(drawFrame);
|
|
1051
1048
|
instance.emit("resumed");
|
|
1052
1049
|
}
|
|
1053
1050
|
},
|
|
@@ -1073,9 +1070,18 @@
|
|
|
1073
1070
|
);
|
|
1074
1071
|
},
|
|
1075
1072
|
};
|
|
1076
|
-
|
|
1073
|
+
const mathProps =
|
|
1074
|
+
"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp";
|
|
1075
|
+
for (const k of mathProps.split(",")) {
|
|
1077
1076
|
instance[k] = math[k];
|
|
1078
1077
|
}
|
|
1078
|
+
function startGameLoop() {
|
|
1079
|
+
if (!_rafid) {
|
|
1080
|
+
_accumulated = 0;
|
|
1081
|
+
_lastFrameTime = perf.now();
|
|
1082
|
+
_rafid = raf(drawFrame);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1079
1085
|
function init() {
|
|
1080
1086
|
if (settings.autoscale) {
|
|
1081
1087
|
on(root, "resize", resizeCanvas);
|
|
@@ -1216,8 +1222,10 @@
|
|
|
1216
1222
|
instance.def("lastkey", () => _lastKey);
|
|
1217
1223
|
}
|
|
1218
1224
|
_initialized = true;
|
|
1219
|
-
instance.resume();
|
|
1220
1225
|
instance.emit("init", instance);
|
|
1226
|
+
if (!_paused) {
|
|
1227
|
+
startGameLoop();
|
|
1228
|
+
}
|
|
1221
1229
|
}
|
|
1222
1230
|
function drawFrame() {
|
|
1223
1231
|
_rafid = raf(drawFrame);
|
|
@@ -1237,18 +1245,13 @@
|
|
|
1237
1245
|
instance.emit("draw", _ctx);
|
|
1238
1246
|
if (updated > 1) {
|
|
1239
1247
|
_accumulated = 0;
|
|
1240
|
-
DEV: console.warn(
|
|
1241
|
-
loggerPrefix +
|
|
1242
|
-
"the last frame updated " +
|
|
1243
|
-
updated +
|
|
1244
|
-
" times. This can drop the FPS if it keeps happening.",
|
|
1245
|
-
);
|
|
1246
1248
|
}
|
|
1247
1249
|
}
|
|
1248
1250
|
}
|
|
1249
1251
|
function setupCanvas() {
|
|
1252
|
+
const d = document;
|
|
1250
1253
|
if ("string" === typeof settings.canvas) {
|
|
1251
|
-
_canvas =
|
|
1254
|
+
_canvas = d.querySelector(settings.canvas);
|
|
1252
1255
|
DEV: assert(
|
|
1253
1256
|
null != _canvas,
|
|
1254
1257
|
loggerPrefix +
|
|
@@ -1257,7 +1260,7 @@
|
|
|
1257
1260
|
} else {
|
|
1258
1261
|
_canvas = settings.canvas;
|
|
1259
1262
|
}
|
|
1260
|
-
_canvas = _canvas ||
|
|
1263
|
+
_canvas = _canvas || d.createElement("canvas");
|
|
1261
1264
|
DEV: assert(
|
|
1262
1265
|
_canvas instanceof HTMLElement && "CANVAS" === _canvas.tagName,
|
|
1263
1266
|
loggerPrefix +
|
|
@@ -1267,7 +1270,7 @@
|
|
|
1267
1270
|
on(_canvas, "click", () => focus());
|
|
1268
1271
|
resizeCanvas();
|
|
1269
1272
|
if (!_canvas.parentNode) {
|
|
1270
|
-
|
|
1273
|
+
d.body.appendChild(_canvas);
|
|
1271
1274
|
}
|
|
1272
1275
|
_canvas.style.imageRendering = "pixelated";
|
|
1273
1276
|
_canvas.oncontextmenu = () => false;
|
|
@@ -1345,13 +1348,12 @@
|
|
|
1345
1348
|
DEV: console.info(loggerPrefix + `version ${version} started`);
|
|
1346
1349
|
DEV: console.debug(loggerPrefix + `litecanvas() options =`, settings);
|
|
1347
1350
|
setupCanvas();
|
|
1348
|
-
if (
|
|
1349
|
-
for (const eventName in
|
|
1350
|
-
if (
|
|
1351
|
-
instance.listen(eventName, settings.loop[eventName]);
|
|
1351
|
+
if (_loop) {
|
|
1352
|
+
for (const eventName in _loop) {
|
|
1353
|
+
if (_loop[eventName]) instance.listen(eventName, _loop[eventName]);
|
|
1352
1354
|
}
|
|
1353
1355
|
}
|
|
1354
|
-
|
|
1356
|
+
raf(init);
|
|
1355
1357
|
return instance;
|
|
1356
1358
|
}
|
|
1357
1359
|
window.litecanvas = litecanvas;
|
package/dist/dist.js
CHANGED
|
@@ -142,8 +142,9 @@
|
|
|
142
142
|
keyboardEvents: true,
|
|
143
143
|
};
|
|
144
144
|
settings = Object.assign(defaults, settings);
|
|
145
|
-
let
|
|
146
|
-
|
|
145
|
+
let _loop = settings.loop,
|
|
146
|
+
_initialized = false,
|
|
147
|
+
_paused,
|
|
147
148
|
_canvas,
|
|
148
149
|
_canvasScale = 1,
|
|
149
150
|
_ctx,
|
|
@@ -152,7 +153,7 @@
|
|
|
152
153
|
_lastFrameTime,
|
|
153
154
|
_fpsInterval = 1e3 / 60,
|
|
154
155
|
_accumulated,
|
|
155
|
-
_rafid,
|
|
156
|
+
_rafid = 0,
|
|
156
157
|
_defaultTextColor = 3,
|
|
157
158
|
_fontFamily = "sans-serif",
|
|
158
159
|
_fontSize = 20,
|
|
@@ -161,8 +162,6 @@
|
|
|
161
162
|
_colorPalette = defaultPalette,
|
|
162
163
|
_colorPaletteState = [],
|
|
163
164
|
_defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
164
|
-
_mathFunctions =
|
|
165
|
-
"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp",
|
|
166
165
|
_eventListeners = {};
|
|
167
166
|
const instance = {
|
|
168
167
|
W: 0,
|
|
@@ -385,17 +384,17 @@
|
|
|
385
384
|
callback(_ctx);
|
|
386
385
|
_ctx.clip();
|
|
387
386
|
},
|
|
388
|
-
sfx(zzfxParams, pitchSlide
|
|
387
|
+
sfx(zzfxParams, pitchSlide, volumeFactor) {
|
|
389
388
|
if (
|
|
390
389
|
!root.zzfxV ||
|
|
391
390
|
(navigator.userActivation && !navigator.userActivation.hasBeenActive)
|
|
392
391
|
) {
|
|
393
392
|
return false;
|
|
394
393
|
}
|
|
395
|
-
zzfxParams
|
|
396
|
-
if (pitchSlide
|
|
394
|
+
zzfxParams ||= _defaultSound;
|
|
395
|
+
if (pitchSlide || volumeFactor) {
|
|
397
396
|
zzfxParams = zzfxParams.slice();
|
|
398
|
-
zzfxParams[0] = volumeFactor * (zzfxParams[0] || 1);
|
|
397
|
+
zzfxParams[0] = (volumeFactor || 1) * (zzfxParams[0] || 1);
|
|
399
398
|
zzfxParams[10] = ~~zzfxParams[10] + pitchSlide;
|
|
400
399
|
}
|
|
401
400
|
zzfx.apply(0, zzfxParams);
|
|
@@ -424,7 +423,7 @@
|
|
|
424
423
|
eventName = lowerCase(eventName);
|
|
425
424
|
triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
|
|
426
425
|
if (
|
|
427
|
-
!
|
|
426
|
+
!_loop &&
|
|
428
427
|
root[eventName] !== instance[eventName] &&
|
|
429
428
|
"function" === typeof root[eventName]
|
|
430
429
|
) {
|
|
@@ -482,16 +481,14 @@
|
|
|
482
481
|
pause() {
|
|
483
482
|
if (!_paused) {
|
|
484
483
|
_paused = true;
|
|
485
|
-
cancelAnimationFrame(_rafid);
|
|
484
|
+
_rafid = ~~cancelAnimationFrame(_rafid);
|
|
486
485
|
instance.emit("paused");
|
|
487
486
|
}
|
|
488
487
|
},
|
|
489
488
|
resume() {
|
|
490
489
|
if (_initialized && _paused) {
|
|
490
|
+
startGameLoop();
|
|
491
491
|
_paused = false;
|
|
492
|
-
_accumulated = _fpsInterval;
|
|
493
|
-
_lastFrameTime = perf.now();
|
|
494
|
-
_rafid = raf(drawFrame);
|
|
495
492
|
instance.emit("resumed");
|
|
496
493
|
}
|
|
497
494
|
},
|
|
@@ -514,9 +511,18 @@
|
|
|
514
511
|
}
|
|
515
512
|
},
|
|
516
513
|
};
|
|
517
|
-
|
|
514
|
+
const mathProps =
|
|
515
|
+
"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp";
|
|
516
|
+
for (const k of mathProps.split(",")) {
|
|
518
517
|
instance[k] = math[k];
|
|
519
518
|
}
|
|
519
|
+
function startGameLoop() {
|
|
520
|
+
if (!_rafid) {
|
|
521
|
+
_accumulated = 0;
|
|
522
|
+
_lastFrameTime = perf.now();
|
|
523
|
+
_rafid = raf(drawFrame);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
520
526
|
function init() {
|
|
521
527
|
if (settings.autoscale) {
|
|
522
528
|
on(root, "resize", resizeCanvas);
|
|
@@ -647,8 +653,10 @@
|
|
|
647
653
|
instance.def("lastkey", () => _lastKey);
|
|
648
654
|
}
|
|
649
655
|
_initialized = true;
|
|
650
|
-
instance.resume();
|
|
651
656
|
instance.emit("init", instance);
|
|
657
|
+
if (!_paused) {
|
|
658
|
+
startGameLoop();
|
|
659
|
+
}
|
|
652
660
|
}
|
|
653
661
|
function drawFrame() {
|
|
654
662
|
_rafid = raf(drawFrame);
|
|
@@ -672,17 +680,18 @@
|
|
|
672
680
|
}
|
|
673
681
|
}
|
|
674
682
|
function setupCanvas() {
|
|
683
|
+
const d = document;
|
|
675
684
|
if ("string" === typeof settings.canvas) {
|
|
676
|
-
_canvas =
|
|
685
|
+
_canvas = d.querySelector(settings.canvas);
|
|
677
686
|
} else {
|
|
678
687
|
_canvas = settings.canvas;
|
|
679
688
|
}
|
|
680
|
-
_canvas = _canvas ||
|
|
689
|
+
_canvas = _canvas || d.createElement("canvas");
|
|
681
690
|
_ctx = _canvas.getContext("2d");
|
|
682
691
|
on(_canvas, "click", () => focus());
|
|
683
692
|
resizeCanvas();
|
|
684
693
|
if (!_canvas.parentNode) {
|
|
685
|
-
|
|
694
|
+
d.body.appendChild(_canvas);
|
|
686
695
|
}
|
|
687
696
|
_canvas.style.imageRendering = "pixelated";
|
|
688
697
|
_canvas.oncontextmenu = () => false;
|
|
@@ -736,13 +745,12 @@
|
|
|
736
745
|
root.ENGINE = instance;
|
|
737
746
|
}
|
|
738
747
|
setupCanvas();
|
|
739
|
-
if (
|
|
740
|
-
for (const eventName in
|
|
741
|
-
if (
|
|
742
|
-
instance.listen(eventName, settings.loop[eventName]);
|
|
748
|
+
if (_loop) {
|
|
749
|
+
for (const eventName in _loop) {
|
|
750
|
+
if (_loop[eventName]) instance.listen(eventName, _loop[eventName]);
|
|
743
751
|
}
|
|
744
752
|
}
|
|
745
|
-
|
|
753
|
+
raf(init);
|
|
746
754
|
return instance;
|
|
747
755
|
}
|
|
748
756
|
window.litecanvas = litecanvas;
|
package/dist/dist.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let l,
|
|
1
|
+
(()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l,n=window,i=Math,o=performance,r=2*i.PI,s=requestAnimationFrame,f=[],c=(e,t,a)=>{e.addEventListener(t,a,!1),f.push(()=>e.removeEventListener(t,a,!1))},d=(a=new AudioContext,n.zzfxV=1,(e=1,t=.05,l=220,i=0,o=0,r=.1,s=0,f=1,c=0,d=0,u=0,p=0,h=0,m=0,g=0,w=0,v=0,x=1,y=0,b=0,k=0)=>{let E=Math,P=2*E.PI,T=c*=500*P/44100/44100,z=l*=(1-t+2*t*E.random(t=[]))*P/44100,C=0,I=0,L=0,D=1,A=0,S=0,H=0,M=k<0?-1:1,N=P*M*k*2/44100,W=E.cos(N),q=E.sin,B=q(N)/4,V=1+B,O=-2*W/V,R=(1-B)/V,F=(1+M*W)/2/V,G=-(M+W)/V,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,v*=44100,d*=500*P/85766121e6,g*=P/44100,u*=P/44100,p*=44100,h=44100*h|0,e*=.3*n.zzfxV,M=i+y+o+r+v|0;L<M;t[L++]=H*e)++S%(100*w|0)||(H=s?1<s?2<s?3<s?q(C*C):E.max(E.min(E.tan(C),1),-1):1-(2*C/P%2+2)%2:1-4*E.abs(E.round(C/P)-C/P):q(C),H=(h?1-b+b*q(P*L/h):1)*(H<0?-1:1)*E.abs(H)**f*(L<i?L/i:L<i+y?1-(L-i)/y*(1-x):L<i+y+o?x:L<M-v?(M-L-v)/r*x:0),H=v?H/2+(v>L?0:(L<M-v?1:(M-L)/v)*t[L-v|0]/2/e):H,k&&(H=j=F*X+G*(X=Y)+F*(Y=H)-R*$-O*($=j))),C+=(N=(l+=c+=d)*E.cos(g*I++))+N*m*q(L**5),D&&++D>p&&(l+=u,z+=u,D=0),!h||++A%h||(l=z,c=T,D=D||1);(e=a.createBuffer(1,M,44100)).getChannelData(0).set(t),(l=a.createBufferSource()).buffer=e,l.connect(a.destination),l.start()}),u=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p=!1,h,m,g=1,w,v=.5,x=1,y,b=1e3/60,k,E=0,P=3,T="sans-serif",z=20,C=1.2,I=Date.now(),L=e,D=[],A=[.5,0,1750,,,.3,1,,,,600,.1],S={},H={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:r,HALF_PI:r/4,lerp:(e,t,a)=>e+a*(t-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,dist:(e,t,a,l)=>i.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?H.clamp(o,l,n):o},norm:(e,t,a)=>H.map(e,t,a,0,1),rand:(e=0,t=1)=>(I=(1664525*I+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~H.rand(e,t+1),rseed(e){I=~~e},cls(e){null==e?w.clearRect(0,0,H.W,H.H):H.rectfill(0,0,H.W,H.H,e)},rect(e,t,a,l,n,i){w.beginPath(),w[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),H.stroke(n)},rectfill(e,t,a,l,n,i){w.beginPath(),w[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),H.fill(n)},circ(e,t,a,l){w.beginPath(),w.arc(~~e,~~t,~~a,0,r),H.stroke(l)},circfill(e,t,a,l){w.beginPath(),w.arc(~~e,~~t,~~a,0,r),H.fill(l)},oval(e,t,a,l,n){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~l,0,0,r),H.stroke(n)},ovalfill(e,t,a,l,n){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~l,0,0,r),H.fill(n)},shape(e){w.beginPath();for(let t=0;t<e.length;t+=2)0===t?w.moveTo(~~e[t],~~e[t+1]):w.lineTo(~~e[t],~~e[t+1]);w.lineTo(~~e[0],~~e[1])},line(e,t,a,l,n){w.beginPath();let i=.5*(0!==v&&~~e==~~a),o=.5*(0!==v&&~~t==~~l);w.moveTo(~~e+i,~~t+o),w.lineTo(~~a+i,~~l+o),H.stroke(n)},linewidth(e){w.lineWidth=~~e,v=.5*(0!=~~e%2)},linedash(e,t=0){w.setLineDash(e),w.lineDashOffset=t},text(e,t,a,l=P,n="normal"){w.font=`${n} ${z}px ${T}`,w.fillStyle=B(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)w.fillText(i[a],~~e,~~t+z*C*a)},textgap(e){C=e},textfont(e){T=e},textsize(e){z=e},textalign(e,t){e&&(w.textAlign=e),t&&(w.textBaseline=t)},image(e,t,a){w.drawImage(a,~~e,~~t)},spr(e,t,a){let l=a.trim().split("\n");for(let a=0;a<l.length;a++){let n=l[a].trim();for(let l=0;l<n.length;l++){let i=n[l];"."!==i&&" "!==i&&H.rectfill(e+l,t+a,1,1,parseInt(i,36)||0)}}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=w;return n.width=e*i,n.height=t*i,(w=n.getContext("2d")).scale(i,i),a(w),w=o,n.transferToImageBitmap()},ctx:e=>(e&&(w=e),w),push(){w.save()},pop(){w.restore()},translate(e,t){w.translate(~~e,~~t)},scale(e,t=e){w.scale(e,t)},rotate(e){w.rotate(e)},alpha(e){w.globalAlpha=H.clamp(e,0,1)},fill(e){w.fillStyle=B(e),w.fill()},stroke(e){w.strokeStyle=B(e),w.stroke()},clip(e){w.beginPath(),e(w),w.clip()},sfx:(e,t,a)=>!!n.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=A,(t||a)&&((e=e.slice())[0]=(a||1)*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){n.zzfxV=e},canvas:()=>m,use(e,t={}){var a=e,l=t;let n=a(H,l);for(let e in n)H.def(e,n[e])},listen:(e,t)=>{S[e=e.toLowerCase()]=S[e]||new Set,S[e].add(t)},unlisten:(e,t)=>{S[e=e.toLowerCase()]&&S[e].delete(t)},emit:(e,t,a,l,i)=>(p&&(q("before:"+(e=e.toLowerCase()),t,a,l,i),u||n[e]===H[e]||"function"!=typeof n[e]||n[e](t,a,l,i),q(e,t,a,l,i),q("after:"+e,t,a,l,i)),t),pal(t,a=3){L=t||e,D=[],P=a,H.emit("pal",L,P)},palc(e,t){null==e?D=[]:D[e]=t},def(e,a){H[e]=a,t.global&&(n[e]=a)},timescale(e){x=e},framerate(e){b=1e3/~~e},stat:e=>[t,p,b/1e3,g,S,L,A,x,n.zzfxV,I,z,T,D,C][e],pause(){h||(h=!0,E=~~cancelAnimationFrame(E),H.emit("paused"))},resume(){p&&h&&(M(),h=!1,H.emit("resumed"))},ispaused:()=>h,quit(){for(let e of(H.emit("quit"),H.pause(),p=!1,S={},f))e();if(t.global){for(let e in H)delete n[e];delete n.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))H[e]=i[e];function M(){E||(k=0,y=o.now(),E=s(N))}function N(){E=s(N);let e=o.now(),t=0,a=e-y;for(y=e,k+=a<100?a:b;k>=b;){t++,k-=b;let e=b/1e3*x;H.emit("update",e,t),H.def("T",H.T+e)}t&&(H.emit("draw",w),t>1&&(k=0))}function W(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(H.def("W",e),H.def("H",a),m.width=e,m.height=a,t.autoscale){let l=+t.autoscale;m.style.display||(m.style.display="block",m.style.margin="auto"),g=i.min(innerWidth/e,innerHeight/a),g=l>1&&g>l?l:g,m.style.width=e*g+"px",m.style.height=a*g+"px"}w.imageSmoothingEnabled=!1,H.textalign("start","top"),H.emit("resized",g)}function q(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function B(e){return L[~~(D[e]??e)%L.length]}if(t.global){if(n.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(n,H),n.ENGINE=H}if(l=document,w=(m=(m="string"==typeof t.canvas?l.querySelector(t.canvas):t.canvas)||l.createElement("canvas")).getContext("2d"),c(m,"click",()=>focus()),W(),m.parentNode||l.body.appendChild(m),m.style.imageRendering="pixelated",m.oncontextmenu=()=>!1,u)for(let e in u)u[e]&&H.listen(e,u[e]);return s(function(){if(t.autoscale&&c(n,"resize",W),t.tapEvents){let e=e=>[(e.pageX-m.offsetLeft)/g,(e.pageY-m.offsetTop)/g],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:o.now()};return t.set(e,n),n},l=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&o.now()-e.t<=300,r=!1;c(m,"mousedown",t=>{if(0===t.button){t.preventDefault();let[l,n]=e(t);H.emit("tap",l,n,0),a(0,l,n),r=!0}}),c(m,"mouseup",a=>{if(0===a.button){a.preventDefault();let l=t.get(0),[n,o]=e(a);i(l)&&H.emit("tapped",l.xi,l.yi,0),H.emit("untap",n,o,0),t.delete(0),r=!1}}),c(n,"mousemove",t=>{t.preventDefault();let[a,n]=e(t);H.def("MX",a),H.def("MY",n),r&&(H.emit("tapping",a,n,0),l(0,a,n))}),c(m,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);H.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),c(m,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,n]=e(a);H.emit("tapping",t,n,a.identifier+1),l(a.identifier+1,t,n)}});let s=e=>{e.preventDefault();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)&&H.emit("tapped",l.xi,l.yi,e),H.emit("untap",l.x,l.y,e),t.delete(e))};c(m,"touchend",s),c(m,"touchcancel",s),c(n,"blur",()=>{for(let[e,a]of(r=!1,t))H.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,l="";c(n,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n),l=" "===n?"space":n)}),c(n,"keyup",t=>{e.delete(t.key.toLowerCase())}),c(n,"blur",()=>e.clear()),H.listen("after:update",()=>t.clear()),H.def("iskeydown",t=>a(e,t)),H.def("iskeypressed",e=>a(t,e)),H.def("lastkey",()=>l)}p=!0,H.emit("init",H),h||M()}),H}})();
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litecanvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.206.1",
|
|
4
4
|
"description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and p5.js/Processing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Luiz Bills <luizbills@pm.me>",
|
|
7
7
|
"contributors": [],
|
|
8
|
-
"homepage": "https://litecanvas.
|
|
8
|
+
"homepage": "https://litecanvas.js.org",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/litecanvas/game-engine.git"
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@happy-dom/global-registrator": "^20.9.0",
|
|
36
36
|
"@size-limit/preset-small-lib": "^12.1.0",
|
|
37
|
-
"@swc/core": "^1.15.
|
|
37
|
+
"@swc/core": "^1.15.33",
|
|
38
38
|
"ava": "^7.0.0",
|
|
39
|
-
"esbuild": "^0.
|
|
39
|
+
"esbuild": "^0.28.0",
|
|
40
40
|
"genversion": "^3.2.0",
|
|
41
41
|
"gzip-size": "^7.0.0",
|
|
42
42
|
"prettier": "^3.8.3",
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,7 @@ import { version } from './version.js'
|
|
|
11
11
|
* @returns {LitecanvasInstance}
|
|
12
12
|
*/
|
|
13
13
|
export default function litecanvas(settings = {}) {
|
|
14
|
-
const
|
|
15
|
-
root = window,
|
|
14
|
+
const root = window,
|
|
16
15
|
math = Math,
|
|
17
16
|
perf = performance,
|
|
18
17
|
TWO_PI = math.PI * 2,
|
|
@@ -48,10 +47,11 @@ export default function litecanvas(settings = {}) {
|
|
|
48
47
|
// setup the settings default values
|
|
49
48
|
settings = Object.assign(defaults, settings)
|
|
50
49
|
|
|
51
|
-
let
|
|
50
|
+
let _loop = settings.loop,
|
|
51
|
+
/** @type {boolean} */
|
|
52
52
|
_initialized = false,
|
|
53
53
|
/** @type {boolean} */
|
|
54
|
-
_paused
|
|
54
|
+
_paused,
|
|
55
55
|
/** @type {HTMLCanvasElement} _canvas */
|
|
56
56
|
_canvas,
|
|
57
57
|
/** @type {number} */
|
|
@@ -68,8 +68,8 @@ export default function litecanvas(settings = {}) {
|
|
|
68
68
|
_fpsInterval = 1000 / 60,
|
|
69
69
|
/** @type {number} */
|
|
70
70
|
_accumulated,
|
|
71
|
-
/** @type {number
|
|
72
|
-
_rafid,
|
|
71
|
+
/** @type {number} */
|
|
72
|
+
_rafid = 0,
|
|
73
73
|
/** @type {number} */
|
|
74
74
|
_defaultTextColor = 3,
|
|
75
75
|
/** @type {string} */
|
|
@@ -86,9 +86,6 @@ export default function litecanvas(settings = {}) {
|
|
|
86
86
|
_colorPaletteState = [],
|
|
87
87
|
/** @type {number[]} */
|
|
88
88
|
_defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
|
|
89
|
-
/** @type {string} list of functions copied from `Math` module */
|
|
90
|
-
_mathFunctions =
|
|
91
|
-
'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp',
|
|
92
89
|
/**
|
|
93
90
|
* @type {Object<string,Set<Function>>} game event listeners
|
|
94
91
|
*/
|
|
@@ -1008,13 +1005,19 @@ export default function litecanvas(settings = {}) {
|
|
|
1008
1005
|
*
|
|
1009
1006
|
* @see https://github.com/KilledByAPixel/ZzFX
|
|
1010
1007
|
*/
|
|
1011
|
-
sfx(zzfxParams, pitchSlide
|
|
1008
|
+
sfx(zzfxParams, pitchSlide, volumeFactor) {
|
|
1012
1009
|
DEV: assert(
|
|
1013
1010
|
null == zzfxParams || Array.isArray(zzfxParams),
|
|
1014
1011
|
loggerPrefix + 'sfx() 1st param must be an array'
|
|
1015
1012
|
)
|
|
1016
|
-
DEV: assert(
|
|
1017
|
-
|
|
1013
|
+
DEV: assert(
|
|
1014
|
+
null == pitchSlide || isNumber(pitchSlide),
|
|
1015
|
+
loggerPrefix + 'sfx() 2nd param must be a number'
|
|
1016
|
+
)
|
|
1017
|
+
DEV: assert(
|
|
1018
|
+
null == volumeFactor || isNumber(volumeFactor),
|
|
1019
|
+
loggerPrefix + 'sfx() 3rd param must be a number'
|
|
1020
|
+
)
|
|
1018
1021
|
|
|
1019
1022
|
if (
|
|
1020
1023
|
!root.zzfxV ||
|
|
@@ -1023,12 +1026,12 @@ export default function litecanvas(settings = {}) {
|
|
|
1023
1026
|
return false
|
|
1024
1027
|
}
|
|
1025
1028
|
|
|
1026
|
-
zzfxParams
|
|
1029
|
+
zzfxParams ||= _defaultSound
|
|
1027
1030
|
|
|
1028
1031
|
// if has other arguments, copy the sound to not change the original
|
|
1029
|
-
if (pitchSlide
|
|
1032
|
+
if (pitchSlide || volumeFactor) {
|
|
1030
1033
|
zzfxParams = zzfxParams.slice()
|
|
1031
|
-
zzfxParams[0] = volumeFactor * (zzfxParams[0] || 1)
|
|
1034
|
+
zzfxParams[0] = (volumeFactor || 1) * (zzfxParams[0] || 1)
|
|
1032
1035
|
zzfxParams[10] = ~~zzfxParams[10] + pitchSlide
|
|
1033
1036
|
}
|
|
1034
1037
|
|
|
@@ -1153,7 +1156,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1153
1156
|
// calls a global function with the same name as the event,
|
|
1154
1157
|
// as long as it's not a global litecanvas function (to avoid infinite loops)
|
|
1155
1158
|
if (
|
|
1156
|
-
!
|
|
1159
|
+
!_loop &&
|
|
1157
1160
|
root[eventName] !== instance[eventName] &&
|
|
1158
1161
|
'function' === typeof root[eventName] /* if is a function */
|
|
1159
1162
|
) {
|
|
@@ -1343,7 +1346,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1343
1346
|
pause() {
|
|
1344
1347
|
if (!_paused) {
|
|
1345
1348
|
_paused = true
|
|
1346
|
-
cancelAnimationFrame(_rafid)
|
|
1349
|
+
_rafid = ~~cancelAnimationFrame(_rafid)
|
|
1347
1350
|
instance.emit('paused')
|
|
1348
1351
|
}
|
|
1349
1352
|
},
|
|
@@ -1358,10 +1361,8 @@ export default function litecanvas(settings = {}) {
|
|
|
1358
1361
|
'resume() cannot be called before the "init" event and neither after the quit() function'
|
|
1359
1362
|
)
|
|
1360
1363
|
if (_initialized && _paused) {
|
|
1364
|
+
startGameLoop()
|
|
1361
1365
|
_paused = false
|
|
1362
|
-
_accumulated = _fpsInterval
|
|
1363
|
-
_lastFrameTime = perf.now()
|
|
1364
|
-
_rafid = raf(drawFrame)
|
|
1365
1366
|
instance.emit('resumed')
|
|
1366
1367
|
}
|
|
1367
1368
|
},
|
|
@@ -1409,11 +1410,20 @@ export default function litecanvas(settings = {}) {
|
|
|
1409
1410
|
}
|
|
1410
1411
|
|
|
1411
1412
|
// prettier-ignore
|
|
1412
|
-
|
|
1413
|
+
const mathProps = 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp'
|
|
1414
|
+
for (const k of mathProps.split(',')) {
|
|
1413
1415
|
// import native Math functions
|
|
1414
1416
|
instance[k] = math[k]
|
|
1415
1417
|
}
|
|
1416
1418
|
|
|
1419
|
+
function startGameLoop() {
|
|
1420
|
+
if (!_rafid) {
|
|
1421
|
+
_accumulated = 0
|
|
1422
|
+
_lastFrameTime = perf.now()
|
|
1423
|
+
_rafid = raf(drawFrame)
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1417
1427
|
function init() {
|
|
1418
1428
|
// listen window resize event when "autoscale" is enabled
|
|
1419
1429
|
if (settings.autoscale) {
|
|
@@ -1600,6 +1610,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1600
1610
|
})
|
|
1601
1611
|
}
|
|
1602
1612
|
|
|
1613
|
+
// default keyboard handler
|
|
1603
1614
|
if (settings.keyboardEvents) {
|
|
1604
1615
|
/** @type {Set<string>} */
|
|
1605
1616
|
const _keysDown = new Set()
|
|
@@ -1677,8 +1688,10 @@ export default function litecanvas(settings = {}) {
|
|
|
1677
1688
|
|
|
1678
1689
|
// start the engine
|
|
1679
1690
|
_initialized = true
|
|
1680
|
-
instance.resume()
|
|
1681
1691
|
instance.emit('init', instance)
|
|
1692
|
+
if (!_paused) {
|
|
1693
|
+
startGameLoop()
|
|
1694
|
+
}
|
|
1682
1695
|
}
|
|
1683
1696
|
|
|
1684
1697
|
function drawFrame() {
|
|
@@ -1703,22 +1716,24 @@ export default function litecanvas(settings = {}) {
|
|
|
1703
1716
|
}
|
|
1704
1717
|
|
|
1705
1718
|
if (updated) {
|
|
1719
|
+
// draws only when an update happens.
|
|
1706
1720
|
instance.emit('draw', _ctx)
|
|
1721
|
+
|
|
1722
|
+
// sometimes the FPS locks at a value below 60
|
|
1723
|
+
// and does not go back up, even with a very simple logic.
|
|
1724
|
+
// One solution I found was to reset the variable that
|
|
1725
|
+
// accumulates time between frames, when multiple updates occur.
|
|
1707
1726
|
if (updated > 1) {
|
|
1708
1727
|
_accumulated = 0
|
|
1709
|
-
DEV: console.warn(
|
|
1710
|
-
loggerPrefix +
|
|
1711
|
-
'the last frame updated ' +
|
|
1712
|
-
updated +
|
|
1713
|
-
' times. This can drop the FPS if it keeps happening.'
|
|
1714
|
-
)
|
|
1715
1728
|
}
|
|
1716
1729
|
}
|
|
1717
1730
|
}
|
|
1718
1731
|
|
|
1719
1732
|
function setupCanvas() {
|
|
1733
|
+
const d = document
|
|
1734
|
+
|
|
1720
1735
|
if ('string' === typeof settings.canvas) {
|
|
1721
|
-
_canvas =
|
|
1736
|
+
_canvas = d.querySelector(settings.canvas)
|
|
1722
1737
|
DEV: assert(
|
|
1723
1738
|
null != _canvas,
|
|
1724
1739
|
loggerPrefix + 'litecanvas() option "canvas" is an invalid CSS selector'
|
|
@@ -1727,7 +1742,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1727
1742
|
_canvas = settings.canvas
|
|
1728
1743
|
}
|
|
1729
1744
|
|
|
1730
|
-
_canvas = _canvas ||
|
|
1745
|
+
_canvas = _canvas || d.createElement('canvas')
|
|
1731
1746
|
|
|
1732
1747
|
DEV: assert(
|
|
1733
1748
|
_canvas instanceof HTMLElement && 'CANVAS' === _canvas.tagName,
|
|
@@ -1742,7 +1757,7 @@ export default function litecanvas(settings = {}) {
|
|
|
1742
1757
|
resizeCanvas()
|
|
1743
1758
|
|
|
1744
1759
|
if (!_canvas.parentNode) {
|
|
1745
|
-
|
|
1760
|
+
d.body.appendChild(_canvas)
|
|
1746
1761
|
}
|
|
1747
1762
|
|
|
1748
1763
|
_canvas.style.imageRendering = 'pixelated'
|
|
@@ -1856,14 +1871,14 @@ export default function litecanvas(settings = {}) {
|
|
|
1856
1871
|
setupCanvas()
|
|
1857
1872
|
|
|
1858
1873
|
// setup default event listeners
|
|
1859
|
-
if (
|
|
1860
|
-
for (const eventName in
|
|
1861
|
-
if (
|
|
1874
|
+
if (_loop) {
|
|
1875
|
+
for (const eventName in _loop) {
|
|
1876
|
+
if (_loop[eventName]) instance.listen(eventName, _loop[eventName])
|
|
1862
1877
|
}
|
|
1863
1878
|
}
|
|
1864
1879
|
|
|
1865
1880
|
// init the engine (async)
|
|
1866
|
-
|
|
1881
|
+
raf(init)
|
|
1867
1882
|
|
|
1868
1883
|
return instance
|
|
1869
1884
|
}
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '0.
|
|
2
|
+
export const version = '0.206.1'
|