litecanvas 0.204.0 → 0.206.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 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.204.0";
118
+ var version = "0.206.0";
119
119
  function litecanvas(settings = {}) {
120
120
  const root = window,
121
121
  math = Math,
@@ -147,7 +147,7 @@
147
147
  };
148
148
  settings = Object.assign(defaults, settings);
149
149
  let _initialized = false,
150
- _paused = true,
150
+ _paused,
151
151
  _canvas,
152
152
  _canvasScale = 1,
153
153
  _ctx,
@@ -156,7 +156,7 @@
156
156
  _lastFrameTime,
157
157
  _fpsInterval = 1e3 / 60,
158
158
  _accumulated,
159
- _rafid,
159
+ _rafid = 0,
160
160
  _defaultTextColor = 3,
161
161
  _fontFamily = "sans-serif",
162
162
  _fontSize = 20,
@@ -165,8 +165,6 @@
165
165
  _colorPalette = defaultPalette,
166
166
  _colorPaletteState = [],
167
167
  _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
168
  _eventListeners = {};
171
169
  const instance = {
172
170
  W: 0,
@@ -929,7 +927,11 @@
929
927
  if (_initialized) {
930
928
  eventName = lowerCase(eventName);
931
929
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
932
- if (!settings.loop && "function" === typeof root[eventName]) {
930
+ if (
931
+ !settings.loop &&
932
+ root[eventName] !== instance[eventName] &&
933
+ "function" === typeof root[eventName]
934
+ ) {
933
935
  root[eventName](arg1, arg2, arg3, arg4);
934
936
  }
935
937
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
@@ -949,6 +951,7 @@
949
951
  _colorPalette = colors || defaultPalette;
950
952
  _colorPaletteState = [];
951
953
  _defaultTextColor = textColor;
954
+ instance.emit("pal", _colorPalette, _defaultTextColor);
952
955
  },
953
956
  palc(a, b) {
954
957
  DEV: assert(
@@ -1028,7 +1031,7 @@
1028
1031
  pause() {
1029
1032
  if (!_paused) {
1030
1033
  _paused = true;
1031
- cancelAnimationFrame(_rafid);
1034
+ _rafid = ~~cancelAnimationFrame(_rafid);
1032
1035
  instance.emit("paused");
1033
1036
  }
1034
1037
  },
@@ -1039,10 +1042,8 @@
1039
1042
  'resume() cannot be called before the "init" event and neither after the quit() function',
1040
1043
  );
1041
1044
  if (_initialized && _paused) {
1045
+ startGameLoop();
1042
1046
  _paused = false;
1043
- _accumulated = _fpsInterval;
1044
- _lastFrameTime = perf.now();
1045
- _rafid = raf(drawFrame);
1046
1047
  instance.emit("resumed");
1047
1048
  }
1048
1049
  },
@@ -1050,7 +1051,7 @@
1050
1051
  return _paused;
1051
1052
  },
1052
1053
  quit() {
1053
- instance.emit("shutdown");
1054
+ instance.emit("quit");
1054
1055
  instance.pause();
1055
1056
  _initialized = false;
1056
1057
  _eventListeners = {};
@@ -1068,9 +1069,18 @@
1068
1069
  );
1069
1070
  },
1070
1071
  };
1071
- for (const k of _mathFunctions.split(",")) {
1072
+ const mathProps =
1073
+ "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp";
1074
+ for (const k of mathProps.split(",")) {
1072
1075
  instance[k] = math[k];
1073
1076
  }
1077
+ function startGameLoop() {
1078
+ if (!_rafid) {
1079
+ _accumulated = 0;
1080
+ _lastFrameTime = perf.now();
1081
+ _rafid = raf(drawFrame);
1082
+ }
1083
+ }
1074
1084
  function init() {
1075
1085
  if (settings.autoscale) {
1076
1086
  on(root, "resize", resizeCanvas);
@@ -1211,8 +1221,10 @@
1211
1221
  instance.def("lastkey", () => _lastKey);
1212
1222
  }
1213
1223
  _initialized = true;
1214
- instance.resume();
1215
1224
  instance.emit("init", instance);
1225
+ if (!_paused) {
1226
+ startGameLoop();
1227
+ }
1216
1228
  }
1217
1229
  function drawFrame() {
1218
1230
  _rafid = raf(drawFrame);
@@ -1232,18 +1244,13 @@
1232
1244
  instance.emit("draw", _ctx);
1233
1245
  if (updated > 1) {
1234
1246
  _accumulated = 0;
1235
- DEV: console.warn(
1236
- loggerPrefix +
1237
- "the last frame updated " +
1238
- updated +
1239
- " times. This can drop the FPS if it keeps happening.",
1240
- );
1241
1247
  }
1242
1248
  }
1243
1249
  }
1244
1250
  function setupCanvas() {
1251
+ const d = document;
1245
1252
  if ("string" === typeof settings.canvas) {
1246
- _canvas = document.querySelector(settings.canvas);
1253
+ _canvas = d.querySelector(settings.canvas);
1247
1254
  DEV: assert(
1248
1255
  null != _canvas,
1249
1256
  loggerPrefix +
@@ -1252,7 +1259,7 @@
1252
1259
  } else {
1253
1260
  _canvas = settings.canvas;
1254
1261
  }
1255
- _canvas = _canvas || document.createElement("canvas");
1262
+ _canvas = _canvas || d.createElement("canvas");
1256
1263
  DEV: assert(
1257
1264
  _canvas instanceof HTMLElement && "CANVAS" === _canvas.tagName,
1258
1265
  loggerPrefix +
@@ -1262,7 +1269,7 @@
1262
1269
  on(_canvas, "click", () => focus());
1263
1270
  resizeCanvas();
1264
1271
  if (!_canvas.parentNode) {
1265
- document.body.appendChild(_canvas);
1272
+ d.body.appendChild(_canvas);
1266
1273
  }
1267
1274
  _canvas.style.imageRendering = "pixelated";
1268
1275
  _canvas.oncontextmenu = () => false;
@@ -1346,7 +1353,7 @@
1346
1353
  instance.listen(eventName, settings.loop[eventName]);
1347
1354
  }
1348
1355
  }
1349
- _rafid = raf(init);
1356
+ raf(init);
1350
1357
  return instance;
1351
1358
  }
1352
1359
  window.litecanvas = litecanvas;
package/dist/dist.js CHANGED
@@ -143,7 +143,7 @@
143
143
  };
144
144
  settings = Object.assign(defaults, settings);
145
145
  let _initialized = false,
146
- _paused = true,
146
+ _paused,
147
147
  _canvas,
148
148
  _canvasScale = 1,
149
149
  _ctx,
@@ -152,7 +152,7 @@
152
152
  _lastFrameTime,
153
153
  _fpsInterval = 1e3 / 60,
154
154
  _accumulated,
155
- _rafid,
155
+ _rafid = 0,
156
156
  _defaultTextColor = 3,
157
157
  _fontFamily = "sans-serif",
158
158
  _fontSize = 20,
@@ -161,8 +161,6 @@
161
161
  _colorPalette = defaultPalette,
162
162
  _colorPaletteState = [],
163
163
  _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
164
  _eventListeners = {};
167
165
  const instance = {
168
166
  W: 0,
@@ -423,7 +421,11 @@
423
421
  if (_initialized) {
424
422
  eventName = lowerCase(eventName);
425
423
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
426
- if (!settings.loop && "function" === typeof root[eventName]) {
424
+ if (
425
+ !settings.loop &&
426
+ root[eventName] !== instance[eventName] &&
427
+ "function" === typeof root[eventName]
428
+ ) {
427
429
  root[eventName](arg1, arg2, arg3, arg4);
428
430
  }
429
431
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
@@ -435,6 +437,7 @@
435
437
  _colorPalette = colors || defaultPalette;
436
438
  _colorPaletteState = [];
437
439
  _defaultTextColor = textColor;
440
+ instance.emit("pal", _colorPalette, _defaultTextColor);
438
441
  },
439
442
  palc(a, b) {
440
443
  if (a == null) {
@@ -477,16 +480,14 @@
477
480
  pause() {
478
481
  if (!_paused) {
479
482
  _paused = true;
480
- cancelAnimationFrame(_rafid);
483
+ _rafid = ~~cancelAnimationFrame(_rafid);
481
484
  instance.emit("paused");
482
485
  }
483
486
  },
484
487
  resume() {
485
488
  if (_initialized && _paused) {
489
+ startGameLoop();
486
490
  _paused = false;
487
- _accumulated = _fpsInterval;
488
- _lastFrameTime = perf.now();
489
- _rafid = raf(drawFrame);
490
491
  instance.emit("resumed");
491
492
  }
492
493
  },
@@ -494,7 +495,7 @@
494
495
  return _paused;
495
496
  },
496
497
  quit() {
497
- instance.emit("shutdown");
498
+ instance.emit("quit");
498
499
  instance.pause();
499
500
  _initialized = false;
500
501
  _eventListeners = {};
@@ -509,9 +510,18 @@
509
510
  }
510
511
  },
511
512
  };
512
- for (const k of _mathFunctions.split(",")) {
513
+ const mathProps =
514
+ "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp";
515
+ for (const k of mathProps.split(",")) {
513
516
  instance[k] = math[k];
514
517
  }
518
+ function startGameLoop() {
519
+ if (!_rafid) {
520
+ _accumulated = 0;
521
+ _lastFrameTime = perf.now();
522
+ _rafid = raf(drawFrame);
523
+ }
524
+ }
515
525
  function init() {
516
526
  if (settings.autoscale) {
517
527
  on(root, "resize", resizeCanvas);
@@ -642,8 +652,10 @@
642
652
  instance.def("lastkey", () => _lastKey);
643
653
  }
644
654
  _initialized = true;
645
- instance.resume();
646
655
  instance.emit("init", instance);
656
+ if (!_paused) {
657
+ startGameLoop();
658
+ }
647
659
  }
648
660
  function drawFrame() {
649
661
  _rafid = raf(drawFrame);
@@ -667,17 +679,18 @@
667
679
  }
668
680
  }
669
681
  function setupCanvas() {
682
+ const d = document;
670
683
  if ("string" === typeof settings.canvas) {
671
- _canvas = document.querySelector(settings.canvas);
684
+ _canvas = d.querySelector(settings.canvas);
672
685
  } else {
673
686
  _canvas = settings.canvas;
674
687
  }
675
- _canvas = _canvas || document.createElement("canvas");
688
+ _canvas = _canvas || d.createElement("canvas");
676
689
  _ctx = _canvas.getContext("2d");
677
690
  on(_canvas, "click", () => focus());
678
691
  resizeCanvas();
679
692
  if (!_canvas.parentNode) {
680
- document.body.appendChild(_canvas);
693
+ d.body.appendChild(_canvas);
681
694
  }
682
695
  _canvas.style.imageRendering = "pixelated";
683
696
  _canvas.oncontextmenu = () => false;
@@ -737,7 +750,7 @@
737
750
  instance.listen(eventName, settings.loop[eventName]);
738
751
  }
739
752
  }
740
- _rafid = raf(init);
753
+ raf(init);
741
754
  return instance;
742
755
  }
743
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,a=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,l)=>{e.addEventListener(t,l,!1),s.push(()=>e.removeEventListener(t,l,!1))},d=(l=new AudioContext,a.zzfxV=1,(e=1,t=.05,n=220,i=0,o=0,r=.1,s=0,f=1,d=0,c=0,p=0,u=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=d*=500*P/44100/44100,z=n*=(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),B=E.sin,V=B(N)/4,q=1+V,O=-2*W/q,R=(1-V)/q,F=(1+M*W)/2/q,G=-(M+W)/q,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,v*=44100,c*=500*P/85766121e6,g*=P/44100,p*=P/44100,u*=44100,h=44100*h|0,e*=.3*a.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?B(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):B(C),H=(h?1-b+b*B(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=(n+=d+=c)*E.cos(g*I++))+N*m*B(L**5),D&&++D>u&&(n+=p,z+=p,D=0),!h||++A%h||(n=z,d=T,D=D||1);(e=l.createBuffer(1,M,44100)).getChannelData(0).set(t),(n=l.createBufferSource()).buffer=e,n.connect(l.destination),n.start()});t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let c=!1,p=!0,u,h=1,m,g=.5,w=1,v,x=1e3/60,y,b,k=3,E="sans-serif",P=20,T=1.2,z=Date.now(),C=e,I=[],L=[.5,0,1750,,,.3,1,,,,600,.1],D={},A={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,l)=>e+l*(t-e),deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let l=10**t;return n.round(e*l)/l},clamp:(e,t,l)=>e<t?t:e>l?l:e,dist:(e,t,l,a)=>n.hypot(l-e,a-t),wrap:(e,t,l)=>e-(l-t)*n.floor((e-t)/(l-t)),map(e,t,l,a,n,i){let o=(e-t)/(l-t)*(n-a)+a;return i?A.clamp(o,a,n):o},norm:(e,t,l)=>A.map(e,t,l,0,1),rand:(e=0,t=1)=>(z=(1664525*z+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~A.rand(e,t+1),rseed(e){z=~~e},cls(e){null==e?m.clearRect(0,0,A.W,A.H):A.rectfill(0,0,A.W,A.H,e)},rect(e,t,l,a,n,i){m.beginPath(),m[i?"roundRect":"rect"](~~e-g,~~t-g,~~l+2*g,~~a+2*g,i),A.stroke(n)},rectfill(e,t,l,a,n,i){m.beginPath(),m[i?"roundRect":"rect"](~~e,~~t,~~l,~~a,i),A.fill(n)},circ(e,t,l,a){m.beginPath(),m.arc(~~e,~~t,~~l,0,o),A.stroke(a)},circfill(e,t,l,a){m.beginPath(),m.arc(~~e,~~t,~~l,0,o),A.fill(a)},oval(e,t,l,a,n){m.beginPath(),m.ellipse(~~e,~~t,~~l,~~a,0,0,o),A.stroke(n)},ovalfill(e,t,l,a,n){m.beginPath(),m.ellipse(~~e,~~t,~~l,~~a,0,0,o),A.fill(n)},shape(e){m.beginPath();for(let t=0;t<e.length;t+=2)0===t?m.moveTo(~~e[t],~~e[t+1]):m.lineTo(~~e[t],~~e[t+1]);m.lineTo(~~e[0],~~e[1])},line(e,t,l,a,n){m.beginPath();let i=.5*(0!==g&&~~e==~~l),o=.5*(0!==g&&~~t==~~a);m.moveTo(~~e+i,~~t+o),m.lineTo(~~l+i,~~a+o),A.stroke(n)},linewidth(e){m.lineWidth=~~e,g=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,l,a=k,n="normal"){m.font=`${n} ${P}px ${E}`,m.fillStyle=N(a);let i=(""+l).split("\n");for(let l=0;l<i.length;l++)m.fillText(i[l],~~e,~~t+P*T*l)},textgap(e){T=e},textfont(e){E=e},textsize(e){P=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,l){m.drawImage(l,~~e,~~t)},spr(e,t,l){let a=l.trim().split("\n");for(let l=0;l<a.length;l++){let n=a[l].trim();for(let a=0;a<n.length;a++){let i=n[a];"."!==i&&" "!==i&&A.rectfill(e+a,t+l,1,1,parseInt(i,36)||0)}}},paint(e,t,l,a={}){let n=a.canvas||new OffscreenCanvas(1,1),i=a.scale||1,o=m;return n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),l(m),m=o,n.transferToImageBitmap()},ctx:e=>(e&&(m=e),m),push(){m.save()},pop(){m.restore()},translate(e,t){m.translate(~~e,~~t)},scale(e,t=e){m.scale(e,t)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=A.clamp(e,0,1)},fill(e){m.fillStyle=N(e),m.fill()},stroke(e){m.strokeStyle=N(e),m.stroke()},clip(e){m.beginPath(),e(m),m.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||L,(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:()=>u,use(e,t={}){var l=e,a=t;let n=l(A,a);for(let e in n)A.def(e,n[e])},listen:(e,t)=>{D[e=e.toLowerCase()]=D[e]||new Set,D[e].add(t)},unlisten:(e,t)=>{D[e=e.toLowerCase()]&&D[e].delete(t)},emit:(e,l,n,i,o)=>(c&&(M("before:"+(e=e.toLowerCase()),l,n,i,o),t.loop||"function"!=typeof a[e]||a[e](l,n,i,o),M(e,l,n,i,o),M("after:"+e,l,n,i,o)),l),pal(t,l=3){C=t||e,I=[],k=l},palc(e,t){null==e?I=[]:I[e]=t},def(e,l){A[e]=l,t.global&&(a[e]=l)},timescale(e){w=e},framerate(e){x=1e3/~~e},stat:e=>[t,c,x/1e3,h,D,C,L,w,a.zzfxV,z,P,E,I,T][e],pause(){p||(p=!0,cancelAnimationFrame(b),A.emit("paused"))},resume(){c&&p&&(p=!1,y=x,v=i.now(),b=r(S),A.emit("resumed"))},ispaused:()=>p,quit(){for(let e of(A.emit("shutdown"),A.pause(),c=!1,D={},s))e();if(t.global){for(let e in A)delete a[e];delete a.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))A[e]=n[e];function S(){b=r(S);let e=i.now(),t=0,l=e-v;for(v=e,y+=l<100?l:x;y>=x;){t++,y-=x;let e=x/1e3*w;A.emit("update",e,t),A.def("T",A.T+e)}t&&(A.emit("draw",m),t>1&&(y=0))}function H(){let e=t.width>0?t.width:innerWidth,l=t.width>0?t.height||t.width:innerHeight;if(A.def("W",e),A.def("H",l),u.width=e,u.height=l,t.autoscale){let a=+t.autoscale;u.style.display||(u.style.display="block",u.style.margin="auto"),h=n.min(innerWidth/e,innerHeight/l),h=a>1&&h>a?a:h,u.style.width=e*h+"px",u.style.height=l*h+"px"}m.imageSmoothingEnabled=!1,A.textalign("start","top"),A.emit("resized",h)}function M(e,t,l,a,n){if(D[e])for(let i of D[e])i(t,l,a,n)}function N(e){return C[~~(I[e]??e)%C.length]}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,A),a.ENGINE=A}if(m=(u=(u="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),f(u,"click",()=>focus()),H(),u.parentNode||document.body.appendChild(u),u.style.imageRendering="pixelated",u.oncontextmenu=()=>!1,t.loop)for(let e in t.loop)t.loop[e]&&A.listen(e,t.loop[e]);return b=r(function(){if(t.autoscale&&f(a,"resize",H),t.tapEvents){let e=e=>[(e.pageX-u.offsetLeft)/h,(e.pageY-u.offsetTop)/h],t=new Map,l=(e,l,a)=>{let n={x:l,y:a,xi:l,yi:a,t:i.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(u,"mousedown",t=>{if(0===t.button){t.preventDefault();let[a,n]=e(t);A.emit("tap",a,n,0),l(0,a,n),r=!0}}),f(u,"mouseup",l=>{if(0===l.button){l.preventDefault();let a=t.get(0),[n,i]=e(l);o(a)&&A.emit("tapped",a.xi,a.yi,0),A.emit("untap",n,i,0),t.delete(0),r=!1}}),f(a,"mousemove",t=>{t.preventDefault();let[l,a]=e(t);A.def("MX",l),A.def("MY",a),r&&(A.emit("tapping",l,a,0),n(0,l,a))}),f(u,"touchstart",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,n]=e(a);A.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),f(u,"touchmove",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,a]=e(l);A.emit("tapping",t,a,l.identifier+1),n(l.identifier+1,t,a)}});let s=e=>{e.preventDefault();let l=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)l.push(t.identifier+1);for(let[e,a]of t)l.includes(e)||(o(a)&&A.emit("tapped",a.xi,a.yi,e),A.emit("untap",a.x,a.y,e),t.delete(e))};f(u,"touchend",s),f(u,"touchcancel",s),f(a,"blur",()=>{for(let[e,l]of(r=!1,t))A.emit("untap",l.x,l.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";f(a,"keydown",l=>{let a=l.key.toLowerCase();e.has(a)||(e.add(a),t.add(a),n=" "===a?"space":a)}),f(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(a,"blur",()=>e.clear()),A.listen("after:update",()=>t.clear()),A.def("iskeydown",t=>l(e,t)),A.def("iskeypressed",e=>l(t,e)),A.def("lastkey",()=>n)}c=!0,A.resume(),A.emit("init",A)}),A}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let l,a,n=window,i=Math,o=performance,r=2*i.PI,s=requestAnimationFrame,f=[],c=(e,t,l)=>{e.addEventListener(t,l,!1),f.push(()=>e.removeEventListener(t,l,!1))},d=(l=new AudioContext,n.zzfxV=1,(e=1,t=.05,a=220,i=0,o=0,r=.1,s=0,f=1,c=0,d=0,p=0,u=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=a*=(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,p*=P/44100,u*=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=(a+=c+=d)*E.cos(g*I++))+N*m*q(L**5),D&&++D>u&&(a+=p,z+=p,D=0),!h||++A%h||(a=z,c=T,D=D||1);(e=l.createBuffer(1,M,44100)).getChannelData(0).set(t),(a=l.createBufferSource()).buffer=e,a.connect(l.destination),a.start()});t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let p=!1,u,h,m=1,g,w=.5,v=1,x,y=1e3/60,b,k=0,E=3,P="sans-serif",T=20,z=1.2,C=Date.now(),I=e,L=[],D=[.5,0,1750,,,.3,1,,,,600,.1],A={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:r,HALF_PI:r/4,lerp:(e,t,l)=>e+l*(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 l=10**t;return i.round(e*l)/l},clamp:(e,t,l)=>e<t?t:e>l?l:e,dist:(e,t,l,a)=>i.hypot(l-e,a-t),wrap:(e,t,l)=>e-(l-t)*i.floor((e-t)/(l-t)),map(e,t,l,a,n,i){let o=(e-t)/(l-t)*(n-a)+a;return i?S.clamp(o,a,n):o},norm:(e,t,l)=>S.map(e,t,l,0,1),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){C=~~e},cls(e){null==e?g.clearRect(0,0,S.W,S.H):S.rectfill(0,0,S.W,S.H,e)},rect(e,t,l,a,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-w,~~t-w,~~l+2*w,~~a+2*w,i),S.stroke(n)},rectfill(e,t,l,a,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~l,~~a,i),S.fill(n)},circ(e,t,l,a){g.beginPath(),g.arc(~~e,~~t,~~l,0,r),S.stroke(a)},circfill(e,t,l,a){g.beginPath(),g.arc(~~e,~~t,~~l,0,r),S.fill(a)},oval(e,t,l,a,n){g.beginPath(),g.ellipse(~~e,~~t,~~l,~~a,0,0,r),S.stroke(n)},ovalfill(e,t,l,a,n){g.beginPath(),g.ellipse(~~e,~~t,~~l,~~a,0,0,r),S.fill(n)},shape(e){g.beginPath();for(let t=0;t<e.length;t+=2)0===t?g.moveTo(~~e[t],~~e[t+1]):g.lineTo(~~e[t],~~e[t+1]);g.lineTo(~~e[0],~~e[1])},line(e,t,l,a,n){g.beginPath();let i=.5*(0!==w&&~~e==~~l),o=.5*(0!==w&&~~t==~~a);g.moveTo(~~e+i,~~t+o),g.lineTo(~~l+i,~~a+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,w=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,l,a=E,n="normal"){g.font=`${n} ${T}px ${P}`,g.fillStyle=q(a);let i=(""+l).split("\n");for(let l=0;l<i.length;l++)g.fillText(i[l],~~e,~~t+T*z*l)},textgap(e){z=e},textfont(e){P=e},textsize(e){T=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,l){g.drawImage(l,~~e,~~t)},spr(e,t,l){let a=l.trim().split("\n");for(let l=0;l<a.length;l++){let n=a[l].trim();for(let a=0;a<n.length;a++){let i=n[a];"."!==i&&" "!==i&&S.rectfill(e+a,t+l,1,1,parseInt(i,36)||0)}}},paint(e,t,l,a={}){let n=a.canvas||new OffscreenCanvas(1,1),i=a.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),l(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=e){g.scale(e,t)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=q(e),g.fill()},stroke(e){g.strokeStyle=q(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t=0,l=1)=>!!n.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||D,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){n.zzfxV=e},canvas:()=>h,use(e,t={}){var l=e,a=t;let n=l(S,a);for(let e in n)S.def(e,n[e])},listen:(e,t)=>{A[e=e.toLowerCase()]=A[e]||new Set,A[e].add(t)},unlisten:(e,t)=>{A[e=e.toLowerCase()]&&A[e].delete(t)},emit:(e,l,a,i,o)=>(p&&(W("before:"+(e=e.toLowerCase()),l,a,i,o),t.loop||n[e]===S[e]||"function"!=typeof n[e]||n[e](l,a,i,o),W(e,l,a,i,o),W("after:"+e,l,a,i,o)),l),pal(t,l=3){I=t||e,L=[],E=l,S.emit("pal",I,E)},palc(e,t){null==e?L=[]:L[e]=t},def(e,l){S[e]=l,t.global&&(n[e]=l)},timescale(e){v=e},framerate(e){y=1e3/~~e},stat:e=>[t,p,y/1e3,m,A,I,D,v,n.zzfxV,C,T,P,L,z][e],pause(){u||(u=!0,k=~~cancelAnimationFrame(k),S.emit("paused"))},resume(){p&&u&&(H(),u=!1,S.emit("resumed"))},ispaused:()=>u,quit(){for(let e of(S.emit("quit"),S.pause(),p=!1,A={},f))e();if(t.global){for(let e in S)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(","))S[e]=i[e];function H(){k||(b=0,x=o.now(),k=s(M))}function M(){k=s(M);let e=o.now(),t=0,l=e-x;for(x=e,b+=l<100?l:y;b>=y;){t++,b-=y;let e=y/1e3*v;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.emit("draw",g),t>1&&(b=0))}function N(){let e=t.width>0?t.width:innerWidth,l=t.width>0?t.height||t.width:innerHeight;if(S.def("W",e),S.def("H",l),h.width=e,h.height=l,t.autoscale){let a=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=i.min(innerWidth/e,innerHeight/l),m=a>1&&m>a?a:m,h.style.width=e*m+"px",h.style.height=l*m+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",m)}function W(e,t,l,a,n){if(A[e])for(let i of A[e])i(t,l,a,n)}function q(e){return I[~~(L[e]??e)%I.length]}if(t.global){if(n.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(n,S),n.ENGINE=S}if(a=document,g=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),c(h,"click",()=>focus()),N(),h.parentNode||a.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,t.loop)for(let e in t.loop)t.loop[e]&&S.listen(e,t.loop[e]);return s(function(){if(t.autoscale&&c(n,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,l=(e,l,a)=>{let n={x:l,y:a,xi:l,yi:a,t:o.now()};return t.set(e,n),n},a=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&o.now()-e.t<=300,r=!1;c(h,"mousedown",t=>{if(0===t.button){t.preventDefault();let[a,n]=e(t);S.emit("tap",a,n,0),l(0,a,n),r=!0}}),c(h,"mouseup",l=>{if(0===l.button){l.preventDefault();let a=t.get(0),[n,o]=e(l);i(a)&&S.emit("tapped",a.xi,a.yi,0),S.emit("untap",n,o,0),t.delete(0),r=!1}}),c(n,"mousemove",t=>{t.preventDefault();let[l,n]=e(t);S.def("MX",l),S.def("MY",n),r&&(S.emit("tapping",l,n,0),a(0,l,n))}),c(h,"touchstart",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,n]=e(a);S.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),c(h,"touchmove",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tapping",t,n,l.identifier+1),a(l.identifier+1,t,n)}});let s=e=>{e.preventDefault();let l=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)l.push(t.identifier+1);for(let[e,a]of t)l.includes(e)||(i(a)&&S.emit("tapped",a.xi,a.yi,e),S.emit("untap",a.x,a.y,e),t.delete(e))};c(h,"touchend",s),c(h,"touchcancel",s),c(n,"blur",()=>{for(let[e,l]of(r=!1,t))S.emit("untap",l.x,l.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,a="";c(n,"keydown",l=>{let n=l.key.toLowerCase();e.has(n)||(e.add(n),t.add(n),a=" "===n?"space":n)}),c(n,"keyup",t=>{e.delete(t.key.toLowerCase())}),c(n,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>l(e,t)),S.def("iskeypressed",e=>l(t,e)),S.def("lastkey",()=>a)}p=!0,S.emit("init",S),u||H()}),S}})();
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.204.0",
3
+ "version": "0.206.0",
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.github.io",
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,7 +34,7 @@
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.26",
37
+ "@swc/core": "^1.15.32",
38
38
  "ava": "^7.0.0",
39
39
  "esbuild": "^0.27.7",
40
40
  "genversion": "^3.2.0",
package/src/index.js CHANGED
@@ -51,7 +51,7 @@ export default function litecanvas(settings = {}) {
51
51
  let /** @type {boolean} */
52
52
  _initialized = false,
53
53
  /** @type {boolean} */
54
- _paused = true,
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
  */
@@ -1149,9 +1146,17 @@ export default function litecanvas(settings = {}) {
1149
1146
 
1150
1147
  triggerEvent('before:' + eventName, arg1, arg2, arg3, arg4)
1151
1148
 
1152
- if (!settings.loop && 'function' === typeof root[eventName]) {
1149
+ // if the "loop" option not exists,
1150
+ // calls a global function with the same name as the event,
1151
+ // as long as it's not a global litecanvas function (to avoid infinite loops)
1152
+ if (
1153
+ !settings.loop &&
1154
+ root[eventName] !== instance[eventName] &&
1155
+ 'function' === typeof root[eventName] /* if is a function */
1156
+ ) {
1153
1157
  root[eventName](arg1, arg2, arg3, arg4)
1154
1158
  }
1159
+
1155
1160
  triggerEvent(eventName, arg1, arg2, arg3, arg4)
1156
1161
 
1157
1162
  triggerEvent('after:' + eventName, arg1, arg2, arg3, arg4)
@@ -1179,6 +1184,8 @@ export default function litecanvas(settings = {}) {
1179
1184
  _colorPalette = colors || defaultPalette
1180
1185
  _colorPaletteState = []
1181
1186
  _defaultTextColor = textColor
1187
+
1188
+ instance.emit('pal', _colorPalette, _defaultTextColor)
1182
1189
  },
1183
1190
 
1184
1191
  /**
@@ -1333,7 +1340,7 @@ export default function litecanvas(settings = {}) {
1333
1340
  pause() {
1334
1341
  if (!_paused) {
1335
1342
  _paused = true
1336
- cancelAnimationFrame(_rafid)
1343
+ _rafid = ~~cancelAnimationFrame(_rafid)
1337
1344
  instance.emit('paused')
1338
1345
  }
1339
1346
  },
@@ -1348,10 +1355,8 @@ export default function litecanvas(settings = {}) {
1348
1355
  'resume() cannot be called before the "init" event and neither after the quit() function'
1349
1356
  )
1350
1357
  if (_initialized && _paused) {
1358
+ startGameLoop()
1351
1359
  _paused = false
1352
- _accumulated = _fpsInterval
1353
- _lastFrameTime = perf.now()
1354
- _rafid = raf(drawFrame)
1355
1360
  instance.emit('resumed')
1356
1361
  }
1357
1362
  },
@@ -1369,8 +1374,8 @@ export default function litecanvas(settings = {}) {
1369
1374
  * Shutdown the litecanvas instance and remove all event listeners.
1370
1375
  */
1371
1376
  quit() {
1372
- // emit "shutdown" event to manual clean ups
1373
- instance.emit('shutdown')
1377
+ // emit "quit" event to manual clean ups
1378
+ instance.emit('quit')
1374
1379
 
1375
1380
  // stop the game loop (update & draw)
1376
1381
  instance.pause()
@@ -1399,11 +1404,20 @@ export default function litecanvas(settings = {}) {
1399
1404
  }
1400
1405
 
1401
1406
  // prettier-ignore
1402
- for (const k of _mathFunctions.split(',')) {
1407
+ const mathProps = 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp'
1408
+ for (const k of mathProps.split(',')) {
1403
1409
  // import native Math functions
1404
1410
  instance[k] = math[k]
1405
1411
  }
1406
1412
 
1413
+ function startGameLoop() {
1414
+ if (!_rafid) {
1415
+ _accumulated = 0
1416
+ _lastFrameTime = perf.now()
1417
+ _rafid = raf(drawFrame)
1418
+ }
1419
+ }
1420
+
1407
1421
  function init() {
1408
1422
  // listen window resize event when "autoscale" is enabled
1409
1423
  if (settings.autoscale) {
@@ -1590,6 +1604,7 @@ export default function litecanvas(settings = {}) {
1590
1604
  })
1591
1605
  }
1592
1606
 
1607
+ // default keyboard handler
1593
1608
  if (settings.keyboardEvents) {
1594
1609
  /** @type {Set<string>} */
1595
1610
  const _keysDown = new Set()
@@ -1667,8 +1682,10 @@ export default function litecanvas(settings = {}) {
1667
1682
 
1668
1683
  // start the engine
1669
1684
  _initialized = true
1670
- instance.resume()
1671
1685
  instance.emit('init', instance)
1686
+ if (!_paused) {
1687
+ startGameLoop()
1688
+ }
1672
1689
  }
1673
1690
 
1674
1691
  function drawFrame() {
@@ -1693,22 +1710,24 @@ export default function litecanvas(settings = {}) {
1693
1710
  }
1694
1711
 
1695
1712
  if (updated) {
1713
+ // draws only when an update happens.
1696
1714
  instance.emit('draw', _ctx)
1715
+
1716
+ // sometimes the FPS locks at a value below 60
1717
+ // and does not go back up, even with a very simple logic.
1718
+ // One solution I found was to reset the variable that
1719
+ // accumulates time between frames, when multiple updates occur.
1697
1720
  if (updated > 1) {
1698
1721
  _accumulated = 0
1699
- DEV: console.warn(
1700
- loggerPrefix +
1701
- 'the last frame updated ' +
1702
- updated +
1703
- ' times. This can drop the FPS if it keeps happening.'
1704
- )
1705
1722
  }
1706
1723
  }
1707
1724
  }
1708
1725
 
1709
1726
  function setupCanvas() {
1727
+ const d = document
1728
+
1710
1729
  if ('string' === typeof settings.canvas) {
1711
- _canvas = document.querySelector(settings.canvas)
1730
+ _canvas = d.querySelector(settings.canvas)
1712
1731
  DEV: assert(
1713
1732
  null != _canvas,
1714
1733
  loggerPrefix + 'litecanvas() option "canvas" is an invalid CSS selector'
@@ -1717,7 +1736,7 @@ export default function litecanvas(settings = {}) {
1717
1736
  _canvas = settings.canvas
1718
1737
  }
1719
1738
 
1720
- _canvas = _canvas || document.createElement('canvas')
1739
+ _canvas = _canvas || d.createElement('canvas')
1721
1740
 
1722
1741
  DEV: assert(
1723
1742
  _canvas instanceof HTMLElement && 'CANVAS' === _canvas.tagName,
@@ -1732,7 +1751,7 @@ export default function litecanvas(settings = {}) {
1732
1751
  resizeCanvas()
1733
1752
 
1734
1753
  if (!_canvas.parentNode) {
1735
- document.body.appendChild(_canvas)
1754
+ d.body.appendChild(_canvas)
1736
1755
  }
1737
1756
 
1738
1757
  _canvas.style.imageRendering = 'pixelated'
@@ -1853,7 +1872,7 @@ export default function litecanvas(settings = {}) {
1853
1872
  }
1854
1873
 
1855
1874
  // init the engine (async)
1856
- _rafid = raf(init)
1875
+ raf(init)
1857
1876
 
1858
1877
  return instance
1859
1878
  }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.204.0'
2
+ export const version = '0.206.0'