litecanvas 0.200.0 → 0.202.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.200.0";
118
+ var version = "0.202.0";
119
119
  function litecanvas(settings = {}) {
120
120
  const root = window,
121
121
  math = Math,
@@ -165,7 +165,6 @@
165
165
  _colorPalette = defaultPalette,
166
166
  _colorPaletteState = [],
167
167
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
168
- _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized",
169
168
  _mathFunctions =
170
169
  "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp",
171
170
  _eventListeners = {};
@@ -686,7 +685,7 @@
686
685
  if (align) _ctx.textAlign = align;
687
686
  if (baseline) _ctx.textBaseline = baseline;
688
687
  },
689
- image(x, y, source2) {
688
+ image(x, y, source) {
690
689
  DEV: assert(
691
690
  isNumber(x),
692
691
  loggerPrefix + "image() 1st param must be a number",
@@ -695,7 +694,7 @@
695
694
  isNumber(y),
696
695
  loggerPrefix + "image() 2nd param must be a number",
697
696
  );
698
- _ctx.drawImage(source2, ~~x, ~~y);
697
+ _ctx.drawImage(source, ~~x, ~~y);
699
698
  },
700
699
  spr(x, y, pixels) {
701
700
  DEV: assert(
@@ -916,6 +915,9 @@
916
915
  if (_initialized) {
917
916
  eventName = lowerCase(eventName);
918
917
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
918
+ if (!settings.loop && "function" === typeof root[eventName]) {
919
+ root[eventName](arg1, arg2, arg3, arg4);
920
+ }
919
921
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
920
922
  triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
921
923
  }
@@ -981,8 +983,8 @@
981
983
  },
982
984
  stat(index) {
983
985
  DEV: assert(
984
- isNumber(index) || "string" === typeof index,
985
- loggerPrefix + "stat() 1st param must be a number or string",
986
+ isNumber(index),
987
+ loggerPrefix + "stat() 1st param must be a number",
986
988
  );
987
989
  const internals = [
988
990
  settings,
@@ -1000,9 +1002,13 @@
1000
1002
  _colorPaletteState,
1001
1003
  _fontLineHeight,
1002
1004
  ];
1003
- const data = { index, value: internals[index] };
1004
- instance.emit("stat", data);
1005
- return data.value;
1005
+ DEV: assert(
1006
+ index >= 0 && index < internals.length,
1007
+ loggerPrefix +
1008
+ "stat() 1st param must be a number between 0 and " +
1009
+ (internals.length - 1),
1010
+ );
1011
+ return internals[index];
1006
1012
  },
1007
1013
  pause() {
1008
1014
  _paused = true;
@@ -1229,9 +1235,9 @@
1229
1235
  }
1230
1236
  _canvas = _canvas || document.createElement("canvas");
1231
1237
  DEV: assert(
1232
- "CANVAS" === _canvas.tagName,
1238
+ _canvas instanceof HTMLElement && "CANVAS" === _canvas.tagName,
1233
1239
  loggerPrefix +
1234
- 'litecanvas() option "canvas" should be a canvas element or string (CSS selector)',
1240
+ 'litecanvas() option "canvas" should be a canvas element or string (CSS selector of a canvas)',
1235
1241
  );
1236
1242
  _ctx = _canvas.getContext("2d");
1237
1243
  on(_canvas, "click", () => focus());
@@ -1284,9 +1290,10 @@
1284
1290
  instance.emit("resized", _canvasScale);
1285
1291
  }
1286
1292
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1287
- if (!_eventListeners[eventName]) return;
1288
- for (const callback of _eventListeners[eventName]) {
1289
- callback(arg1, arg2, arg3, arg4);
1293
+ if (_eventListeners[eventName]) {
1294
+ for (const callback of _eventListeners[eventName]) {
1295
+ callback(arg1, arg2, arg3, arg4);
1296
+ }
1290
1297
  }
1291
1298
  }
1292
1299
  function loadPlugin(callback, config) {
@@ -1314,12 +1321,11 @@
1314
1321
  DEV: console.info(loggerPrefix + `version ${version} started`);
1315
1322
  DEV: console.debug(loggerPrefix + `litecanvas() options =`, settings);
1316
1323
  setupCanvas();
1317
- const source = settings.loop ? settings.loop : root;
1318
- for (const event of _coreEvents.split(",")) {
1319
- DEV: if (root === source && source[event]) {
1320
- console.info(loggerPrefix + `using window.${event}()`);
1324
+ if (settings.loop) {
1325
+ for (const eventName in settings.loop) {
1326
+ if (settings.loop[eventName])
1327
+ instance.listen(eventName, settings.loop[eventName]);
1321
1328
  }
1322
- if (source[event]) instance.listen(event, source[event]);
1323
1329
  }
1324
1330
  if ("loading" === document.readyState) {
1325
1331
  on(root, "DOMContentLoaded", () => raf(init));
package/dist/dist.js CHANGED
@@ -161,7 +161,6 @@
161
161
  _colorPalette = defaultPalette,
162
162
  _colorPaletteState = [],
163
163
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
164
- _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized",
165
164
  _mathFunctions =
166
165
  "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp",
167
166
  _eventListeners = {};
@@ -316,8 +315,8 @@
316
315
  if (align) _ctx.textAlign = align;
317
316
  if (baseline) _ctx.textBaseline = baseline;
318
317
  },
319
- image(x, y, source2) {
320
- _ctx.drawImage(source2, ~~x, ~~y);
318
+ image(x, y, source) {
319
+ _ctx.drawImage(source, ~~x, ~~y);
321
320
  },
322
321
  spr(x, y, pixels) {
323
322
  const rows = pixels.trim().split("\n");
@@ -424,6 +423,9 @@
424
423
  if (_initialized) {
425
424
  eventName = lowerCase(eventName);
426
425
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
426
+ if (!settings.loop && "function" === typeof root[eventName]) {
427
+ root[eventName](arg1, arg2, arg3, arg4);
428
+ }
427
429
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
428
430
  triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
429
431
  }
@@ -469,9 +471,7 @@
469
471
  _colorPaletteState,
470
472
  _fontLineHeight,
471
473
  ];
472
- const data = { index, value: internals[index] };
473
- instance.emit("stat", data);
474
- return data.value;
474
+ return internals[index];
475
475
  },
476
476
  pause() {
477
477
  _paused = true;
@@ -702,9 +702,10 @@
702
702
  instance.emit("resized", _canvasScale);
703
703
  }
704
704
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
705
- if (!_eventListeners[eventName]) return;
706
- for (const callback of _eventListeners[eventName]) {
707
- callback(arg1, arg2, arg3, arg4);
705
+ if (_eventListeners[eventName]) {
706
+ for (const callback of _eventListeners[eventName]) {
707
+ callback(arg1, arg2, arg3, arg4);
708
+ }
708
709
  }
709
710
  }
710
711
  function loadPlugin(callback, config) {
@@ -725,9 +726,11 @@
725
726
  root.ENGINE = instance;
726
727
  }
727
728
  setupCanvas();
728
- const source = settings.loop ? settings.loop : root;
729
- for (const event of _coreEvents.split(",")) {
730
- if (source[event]) instance.listen(event, source[event]);
729
+ if (settings.loop) {
730
+ for (const eventName in settings.loop) {
731
+ if (settings.loop[eventName])
732
+ instance.listen(eventName, settings.loop[eventName]);
733
+ }
731
734
  }
732
735
  if ("loading" === document.readyState) {
733
736
  on(root, "DOMContentLoaded", () => raf(init));
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(a=new AudioContext,l.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,g=0,m=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let z=Math,E=2*z.PI,P=d*=500*E/44100/44100,T=n*=(1-t+2*t*z.random(t=[]))*E/44100,C=0,I=0,L=0,D=1,S=0,A=0,M=0,H=k<0?-1:1,N=E*H*k*2/44100,W=z.cos(N),q=z.sin,B=q(N)/4,O=1+B,V=-2*W/O,R=(1-B)/O,F=(1+H*W)/2/O,G=-(H+W)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,c*=500*E/85766121e6,m*=E/44100,p*=E/44100,u*=44100,h=44100*h|0,e*=.3*l.zzfxV,H=i+y+o+r+w|0;L<H;t[L++]=M*e)++A%(100*v|0)||(M=s?1<s?2<s?3<s?q(C*C):z.max(z.min(z.tan(C),1),-1):1-(2*C/E%2+2)%2:1-4*z.abs(z.round(C/E)-C/E):q(C),M=(h?1-b+b*q(E*L/h):1)*(M<0?-1:1)*z.abs(M)**f*(L<i?L/i:L<i+y?1-(L-i)/y*(1-x):L<i+y+o?x:L<H-w?(H-L-w)/r*x:0),M=w?M/2+(w>L?0:(L<H-w?1:(H-L)/w)*t[L-w|0]/2/e):M,k&&(M=j=F*X+G*(X=Y)+F*(Y=M)-R*$-V*($=j))),C+=(N=(n+=d+=c)*z.cos(m*I++))+N*g*q(L**5),D&&++D>u&&(n+=p,T+=p,D=0),!h||++S%h||(n=T,d=P,D=D||1);(e=a.createBuffer(1,H,44100)).getChannelData(0).set(t),(n=a.createBufferSource()).buffer=e,n.connect(a.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,g,m=.5,v=1,w,x=1e3/60,y,b,k=3,z="sans-serif",E=20,P=1.2,T=Date.now(),C=e,I=[],L=[.5,0,1750,,,.3,1,,,,600,.1],D={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,a)=>e+a*(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 a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?S.clamp(o,l,n):o},norm:(e,t,a)=>S.map(e,t,a,0,1),rand:(e=0,t=1)=>(T=(1664525*T+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){T=~~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,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-m,~~t-m,~~a+2*m,~~l+2*m,i),S.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},circ(e,t,a,l){g.beginPath(),g.arc(~~e,~~t,~~a,0,o),S.stroke(l)},circfill(e,t,a,l){g.beginPath(),g.arc(~~e,~~t,~~a,0,o),S.fill(l)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),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,a,l,n){g.beginPath();let i=.5*(0!==m&&~~e==~~a),o=.5*(0!==m&&~~t==~~l);g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,m=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${E}px ${z}`,g.fillStyle=W(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+E*P*a)},textgap(e){P=e},textfont(e){z=e},textsize(e){E=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a){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&&S.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=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(){g.save()},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t){g.scale(e,t||e)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=W(e),g.fill()},stroke(e){g.strokeStyle=W(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t=0,a=1)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||L,(0!==t||1!==a)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>u,use(e,t={}){var a=e,l=t;let n=a(S,l);for(let e in n)S.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,t,a,l,n){c&&(N("before:"+(e=e.toLowerCase()),t,a,l,n),N(e,t,a,l,n),N("after:"+e,t,a,l,n))},pal(t,a=3){C=t||e,I=[],k=a},palc(e,t){null==e?I=[]:I[e]=t},def(e,a){S[e]=a,t.global&&(l[e]=a)},timescale(e){v=e},framerate(e){x=1e3/~~e},stat(e){let a={index:e,value:[t,c,x/1e3,h,D,C,L,v,l.zzfxV,T,E,z,I,P][e]};return S.emit("stat",a),a.value},pause(){p=!0,cancelAnimationFrame(b)},resume(){c&&p&&(p=!1,y=x,w=i.now(),b=r(M))},paused:()=>p,quit(){for(let e of(S.emit("quit"),S.pause(),c=!1,D={},s))e();if(t.global){for(let e in S)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))S[e]=n[e];function A(){if(t.autoscale&&f(l,"resize",H),t.tapEvents){let e=e=>[(e.pageX-u.offsetLeft)/h,(e.pageY-u.offsetTop)/h],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(u,"mousedown",t=>{if(0===t.button){t.preventDefault();let[l,n]=e(t);S.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(u,"mouseup",a=>{if(0===a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&S.emit("tapped",l.xi,l.yi,0),S.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);S.def("MX",a),S.def("MY",l),r&&(S.emit("tapping",a,l,0),n(0,a,l))}),f(u,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(u,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);S.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});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)||(o(l)&&S.emit("tapped",l.xi,l.yi,e),S.emit("untap",l.x,l.y,e),t.delete(e))};f(u,"touchend",s),f(u,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))S.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,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>a(e,t)),S.def("iskeypressed",e=>a(t,e)),S.def("lastkey",()=>n)}c=!0,S.resume(),S.emit("init",S)}function M(){b=r(M);let e=i.now(),t=0,a=e-w;for(w=e,y+=a<100?a:x;y>=x;){t++,y-=x;let e=x/1e3*v;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.emit("draw",g),t>1&&(y=0))}function H(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(S.def("W",e),S.def("H",a),u.width=e,u.height=a,t.autoscale){let l=+t.autoscale;u.style.display||(u.style.display="block",u.style.margin="auto"),h=n.min(innerWidth/e,innerHeight/a),h=l>1&&h>l?l:h,u.style.width=e*h+"px",u.style.height=a*h+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",h)}function N(e,t,a,l,n){if(D[e])for(let i of D[e])i(t,a,l,n)}function W(e){return C[~~(I[e]??e)%C.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,S),l.ENGINE=S}g=(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;let q=t.loop?t.loop:l;for(let e of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))q[e]&&S.listen(e,q[e]);return"loading"===document.readyState?f(l,"DOMContentLoaded",()=>r(A)):b=r(A),S}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(a=new AudioContext,l.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,g=0,m=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,S=0,A=0,M=0,H=k<0?-1:1,N=P*H*k*2/44100,W=E.cos(N),q=E.sin,B=q(N)/4,O=1+B,V=-2*W/O,R=(1-B)/O,F=(1+H*W)/2/O,G=-(H+W)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,v*=44100,c*=500*P/85766121e6,m*=P/44100,p*=P/44100,u*=44100,h=44100*h|0,e*=.3*l.zzfxV,H=i+y+o+r+v|0;L<H;t[L++]=M*e)++A%(100*w|0)||(M=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),M=(h?1-b+b*q(P*L/h):1)*(M<0?-1:1)*E.abs(M)**f*(L<i?L/i:L<i+y?1-(L-i)/y*(1-x):L<i+y+o?x:L<H-v?(H-L-v)/r*x:0),M=v?M/2+(v>L?0:(L<H-v?1:(H-L)/v)*t[L-v|0]/2/e):M,k&&(M=j=F*X+G*(X=Y)+F*(Y=M)-R*$-V*($=j))),C+=(N=(n+=d+=c)*E.cos(m*I++))+N*g*q(L**5),D&&++D>u&&(n+=p,z+=p,D=0),!h||++S%h||(n=z,d=T,D=D||1);(e=a.createBuffer(1,H,44100)).getChannelData(0).set(t),(n=a.createBufferSource()).buffer=e,n.connect(a.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,g,m=.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={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,a)=>e+a*(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 a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?S.clamp(o,l,n):o},norm:(e,t,a)=>S.map(e,t,a,0,1),rand:(e=0,t=1)=>(z=(1664525*z+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){z=~~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,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-m,~~t-m,~~a+2*m,~~l+2*m,i),S.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},circ(e,t,a,l){g.beginPath(),g.arc(~~e,~~t,~~a,0,o),S.stroke(l)},circfill(e,t,a,l){g.beginPath(),g.arc(~~e,~~t,~~a,0,o),S.fill(l)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),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,a,l,n){g.beginPath();let i=.5*(0!==m&&~~e==~~a),o=.5*(0!==m&&~~t==~~l);g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,m=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${P}px ${E}`,g.fillStyle=W(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+P*T*a)},textgap(e){T=e},textfont(e){E=e},textsize(e){P=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a){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&&S.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=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(){g.save()},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t){g.scale(e,t||e)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=W(e),g.fill()},stroke(e){g.strokeStyle=W(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t=0,a=1)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||L,(0!==t||1!==a)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>u,use(e,t={}){var a=e,l=t;let n=a(S,l);for(let e in n)S.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,a,n,i,o){c&&(N("before:"+(e=e.toLowerCase()),a,n,i,o),t.loop||"function"!=typeof l[e]||l[e](a,n,i,o),N(e,a,n,i,o),N("after:"+e,a,n,i,o))},pal(t,a=3){C=t||e,I=[],k=a},palc(e,t){null==e?I=[]:I[e]=t},def(e,a){S[e]=a,t.global&&(l[e]=a)},timescale(e){w=e},framerate(e){x=1e3/~~e},stat:e=>[t,c,x/1e3,h,D,C,L,w,l.zzfxV,z,P,E,I,T][e],pause(){p=!0,cancelAnimationFrame(b)},resume(){c&&p&&(p=!1,y=x,v=i.now(),b=r(M))},paused:()=>p,quit(){for(let e of(S.emit("quit"),S.pause(),c=!1,D={},s))e();if(t.global){for(let e in S)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))S[e]=n[e];function A(){if(t.autoscale&&f(l,"resize",H),t.tapEvents){let e=e=>[(e.pageX-u.offsetLeft)/h,(e.pageY-u.offsetTop)/h],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(u,"mousedown",t=>{if(0===t.button){t.preventDefault();let[l,n]=e(t);S.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(u,"mouseup",a=>{if(0===a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&S.emit("tapped",l.xi,l.yi,0),S.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);S.def("MX",a),S.def("MY",l),r&&(S.emit("tapping",a,l,0),n(0,a,l))}),f(u,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(u,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);S.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});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)||(o(l)&&S.emit("tapped",l.xi,l.yi,e),S.emit("untap",l.x,l.y,e),t.delete(e))};f(u,"touchend",s),f(u,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))S.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,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>a(e,t)),S.def("iskeypressed",e=>a(t,e)),S.def("lastkey",()=>n)}c=!0,S.resume(),S.emit("init",S)}function M(){b=r(M);let e=i.now(),t=0,a=e-v;for(v=e,y+=a<100?a:x;y>=x;){t++,y-=x;let e=x/1e3*w;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.emit("draw",g),t>1&&(y=0))}function H(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(S.def("W",e),S.def("H",a),u.width=e,u.height=a,t.autoscale){let l=+t.autoscale;u.style.display||(u.style.display="block",u.style.margin="auto"),h=n.min(innerWidth/e,innerHeight/a),h=l>1&&h>l?l:h,u.style.width=e*h+"px",u.style.height=a*h+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",h)}function N(e,t,a,l,n){if(D[e])for(let i of D[e])i(t,a,l,n)}function W(e){return C[~~(I[e]??e)%C.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,S),l.ENGINE=S}if(g=(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]&&S.listen(e,t.loop[e]);return"loading"===document.readyState?f(l,"DOMContentLoaded",()=>r(A)):b=r(A),S}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.200.0",
3
+ "version": "0.202.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>",
package/src/index.js CHANGED
@@ -86,8 +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} */
90
- _coreEvents = 'init,update,draw,tap,untap,tapping,tapped,resized',
91
89
  /** @type {string} list of functions copied from `Math` module*/
92
90
  _mathFunctions =
93
91
  'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp',
@@ -1075,7 +1073,7 @@ export default function litecanvas(settings = {}) {
1075
1073
  },
1076
1074
 
1077
1075
  /**
1078
- * Add a game event listener
1076
+ * Add a game event listener.
1079
1077
  *
1080
1078
  * @param {string} eventName the event type name
1081
1079
  * @param {Function} callback the function that is called when the event occurs
@@ -1097,7 +1095,7 @@ export default function litecanvas(settings = {}) {
1097
1095
  },
1098
1096
 
1099
1097
  /**
1100
- * Remove a game event listener
1098
+ * Remove a game event listener.
1101
1099
  *
1102
1100
  * @param {string} eventName the event type name
1103
1101
  * @param {Function} callback the function that is called when the event occurs
@@ -1120,13 +1118,17 @@ export default function litecanvas(settings = {}) {
1120
1118
  },
1121
1119
 
1122
1120
  /**
1123
- * Call all listeners attached to a game event
1121
+ * Call all listeners attached to a game event.
1122
+ *
1123
+ * Note: when the `litecanvas()` "loop" option is `null` (default),
1124
+ * `emit()` will first call a global function matching the event name (if it exists).
1125
+ * E.g: `emit("boom")` calls `window.boom()`.
1124
1126
  *
1125
1127
  * @param {string} eventName The event type name
1126
- * @param {*} [arg1] any data to be passed over the listeners
1127
- * @param {*} [arg2] any data to be passed over the listeners
1128
- * @param {*} [arg3] any data to be passed over the listeners
1129
- * @param {*} [arg4] any data to be passed over the listeners
1128
+ * @param {any} [arg1] any data to be passed over the listeners
1129
+ * @param {any} [arg2] any data to be passed over the listeners
1130
+ * @param {any} [arg3] any data to be passed over the listeners
1131
+ * @param {any} [arg4] any data to be passed over the listeners
1130
1132
  */
1131
1133
  emit(eventName, arg1, arg2, arg3, arg4) {
1132
1134
  DEV: assert(
@@ -1138,7 +1140,12 @@ export default function litecanvas(settings = {}) {
1138
1140
  eventName = lowerCase(eventName)
1139
1141
 
1140
1142
  triggerEvent('before:' + eventName, arg1, arg2, arg3, arg4)
1143
+
1144
+ if (!settings.loop && 'function' === typeof root[eventName]) {
1145
+ root[eventName](arg1, arg2, arg3, arg4)
1146
+ }
1141
1147
  triggerEvent(eventName, arg1, arg2, arg3, arg4)
1148
+
1142
1149
  triggerEvent('after:' + eventName, arg1, arg2, arg3, arg4)
1143
1150
  }
1144
1151
  },
@@ -1194,8 +1201,13 @@ export default function litecanvas(settings = {}) {
1194
1201
  /**
1195
1202
  * Define or update a instance property.
1196
1203
  *
1197
- * @param {string} key
1198
- * @param {*} value
1204
+ * Note: when the `litecanvas()` option "global" is `true` (default),
1205
+ * `def()` with set/update a global property.
1206
+ *
1207
+ * E.g: `def('ONE', 1)` do `window.ONE = 1`.
1208
+ *
1209
+ * @param {string} key the property name
1210
+ * @param {any} value the property value
1199
1211
  */
1200
1212
  def(key, value) {
1201
1213
  DEV: assert('string' === typeof key, loggerPrefix + 'def() 1st param must be a string')
@@ -1245,14 +1257,11 @@ export default function litecanvas(settings = {}) {
1245
1257
  /**
1246
1258
  * Returns information about the engine instance.
1247
1259
  *
1248
- * @param {number|string} index
1260
+ * @param {number} index
1249
1261
  * @returns {any}
1250
1262
  */
1251
1263
  stat(index) {
1252
- DEV: assert(
1253
- isNumber(index) || 'string' === typeof index,
1254
- loggerPrefix + 'stat() 1st param must be a number or string'
1255
- )
1264
+ DEV: assert(isNumber(index), loggerPrefix + 'stat() 1st param must be a number')
1256
1265
 
1257
1266
  const internals = [
1258
1267
  // 0
@@ -1285,12 +1294,14 @@ export default function litecanvas(settings = {}) {
1285
1294
  _fontLineHeight,
1286
1295
  ]
1287
1296
 
1288
- const data = { index, value: internals[index] }
1289
-
1290
- // plugins can modify or create new stats
1291
- instance.emit('stat', data)
1297
+ DEV: assert(
1298
+ index >= 0 && index < internals.length,
1299
+ loggerPrefix +
1300
+ 'stat() 1st param must be a number between 0 and ' +
1301
+ (internals.length - 1)
1302
+ )
1292
1303
 
1293
- return data.value
1304
+ return internals[index]
1294
1305
  },
1295
1306
 
1296
1307
  /**
@@ -1682,9 +1693,9 @@ export default function litecanvas(settings = {}) {
1682
1693
  _canvas = _canvas || document.createElement('canvas')
1683
1694
 
1684
1695
  DEV: assert(
1685
- 'CANVAS' === _canvas.tagName,
1696
+ _canvas instanceof HTMLElement && 'CANVAS' === _canvas.tagName,
1686
1697
  loggerPrefix +
1687
- 'litecanvas() option "canvas" should be a canvas element or string (CSS selector)'
1698
+ 'litecanvas() option "canvas" should be a canvas element or string (CSS selector of a canvas)'
1688
1699
  )
1689
1700
 
1690
1701
  _ctx = _canvas.getContext('2d')
@@ -1698,6 +1709,8 @@ export default function litecanvas(settings = {}) {
1698
1709
  }
1699
1710
 
1700
1711
  _canvas.style.imageRendering = 'pixelated'
1712
+
1713
+ // disable default browser's right click in canvas
1701
1714
  _canvas.oncontextmenu = () => false
1702
1715
  }
1703
1716
 
@@ -1752,21 +1765,22 @@ export default function litecanvas(settings = {}) {
1752
1765
 
1753
1766
  /**
1754
1767
  * @param {string} eventName
1755
- * @param {*} arg1
1756
- * @param {*} arg2
1757
- * @param {*} arg3
1758
- * @param {*} arg4
1768
+ * @param {any} [arg1]
1769
+ * @param {any} [arg2]
1770
+ * @param {any} [arg3]
1771
+ * @param {any} [arg4]
1759
1772
  */
1760
1773
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1761
- if (!_eventListeners[eventName]) return
1762
- for (const callback of _eventListeners[eventName]) {
1763
- callback(arg1, arg2, arg3, arg4)
1774
+ if (_eventListeners[eventName]) {
1775
+ for (const callback of _eventListeners[eventName]) {
1776
+ callback(arg1, arg2, arg3, arg4)
1777
+ }
1764
1778
  }
1765
1779
  }
1766
1780
 
1767
1781
  /**
1768
1782
  * @param {pluginCallback} callback
1769
- * @param {*} config
1783
+ * @param {any} config
1770
1784
  */
1771
1785
  function loadPlugin(callback, config) {
1772
1786
  const pluginData = callback(instance, config)
@@ -1805,12 +1819,10 @@ export default function litecanvas(settings = {}) {
1805
1819
  setupCanvas()
1806
1820
 
1807
1821
  // setup default event listeners
1808
- const source = settings.loop ? settings.loop : root
1809
- for (const event of _coreEvents.split(',')) {
1810
- DEV: if (root === source && source[event]) {
1811
- console.info(loggerPrefix + `using window.${event}()`)
1822
+ if (settings.loop) {
1823
+ for (const eventName in settings.loop) {
1824
+ if (settings.loop[eventName]) instance.listen(eventName, settings.loop[eventName])
1812
1825
  }
1813
- if (source[event]) instance.listen(event, source[event])
1814
1826
  }
1815
1827
 
1816
1828
  // init the engine (async)
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.200.0'
2
+ export const version = '0.202.0'
package/types/global.d.ts CHANGED
@@ -547,21 +547,25 @@ declare global {
547
547
  */
548
548
  function use(callback: pluginCallback): void
549
549
  /**
550
- * Add a game loop event listener
550
+ * Add a game loop event listener.
551
551
  *
552
552
  * @param event The game event type
553
553
  * @param callback the function that is called when the event occurs
554
554
  */
555
555
  function listen(event: string, callback: Function): void
556
556
  /**
557
- * Remove a game loop event listener
557
+ * Remove a game loop event listener.
558
558
  *
559
559
  * @param event The game event type
560
560
  * @param callback the function that is called when the event occurs
561
561
  */
562
562
  function unlisten(event: string, callback: Function): void
563
563
  /**
564
- * Call all listeners attached to a game event
564
+ * Call all listeners attached to a game event.
565
+ *
566
+ * Note: when the `litecanvas()` "loop" option is `null` (default),
567
+ * `emit()` will first call a global function matching the event name (if it exists).
568
+ * E.g: `emit("boom", 10)` calls `window.boom(10)`.
565
569
  *
566
570
  * @param event The game event type
567
571
  * @param [arg1] any data to be passed over the listeners
@@ -589,10 +593,14 @@ declare global {
589
593
  */
590
594
  function palc(a?: number, b?: number): void
591
595
  /**
592
- * Define or update a instance property
596
+ * Define or update a instance property.
593
597
  *
594
- * @param key
595
- * @param value
598
+ * Note: when the `litecanvas()` option "global" is `true` (default),
599
+ * `def()` with set/update a global property.
600
+ * E.g: `def('ONE', 1)` also do `window.ONE = 1`.
601
+ *
602
+ * @param key the property name
603
+ * @param value the property value
596
604
  */
597
605
  function def(key: string, value: any): void
598
606
  /**
@@ -626,11 +634,10 @@ declare global {
626
634
  * - n = 11: the current font family
627
635
  * - n = 12: the current state of the color palette
628
636
  * - n = 13: the current font gap
629
- * - n = *any other value*: probably returns undefined
630
637
  *
631
638
  * @param index
632
639
  */
633
- function stat(index: number | string): any
640
+ function stat(index: number): any
634
641
  /**
635
642
  * Pauses the engine loop (update & draw).
636
643
  */
package/types/types.d.ts CHANGED
@@ -531,21 +531,25 @@ type LitecanvasInstance = {
531
531
  */
532
532
  use(callback: pluginCallback): void
533
533
  /**
534
- * Add a game loop event listener
534
+ * Add a game loop event listener.
535
535
  *
536
536
  * @param event The game event type
537
537
  * @param callback the function that is called when the event occurs
538
538
  */
539
539
  listen(event: string, callback: Function): void
540
540
  /**
541
- * Remove a game loop event listener
541
+ * Remove a game loop event listener.
542
542
  *
543
543
  * @param event The game event type
544
544
  * @param callback the function that is called when the event occurs
545
545
  */
546
546
  unlisten(event: string, callback: Function): void
547
547
  /**
548
- * Call all listeners attached to a game event
548
+ * Call all listeners attached to a game event.
549
+ *
550
+ * Note: when the `litecanvas()` "loop" option is `null` (default),
551
+ * `emit()` will first call a global function matching the event name (if it exists).
552
+ * E.g: `emit("boom", 10)` calls `window.boom(10)`.
549
553
  *
550
554
  * @param event The game event type
551
555
  * @param [arg1] any data to be passed over the listeners
@@ -555,10 +559,14 @@ type LitecanvasInstance = {
555
559
  */
556
560
  emit(event: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any): void
557
561
  /**
558
- * Define or update a instance property
562
+ * Define or update a instance property.
559
563
  *
560
- * @param key
561
- * @param value
564
+ * Note: when the `litecanvas()` option "global" is `true` (default),
565
+ * `def()` with set/update a global property.
566
+ * E.g: `def('ONE', 1)` also do `window.ONE = 1`.
567
+ *
568
+ * @param key the property name
569
+ * @param value the property value
562
570
  */
563
571
  def(key: string, value: any): void
564
572
  /**
@@ -610,11 +618,10 @@ type LitecanvasInstance = {
610
618
  * - n = 11: the current font family
611
619
  * - n = 12: the current state of the color palette
612
620
  * - n = 13: the current font gap
613
- * - n = *any other value*: probably returns undefined
614
621
  *
615
622
  * @param index
616
623
  */
617
- stat(index: number | string): any
624
+ stat(index: number): any
618
625
  /**
619
626
  * Pauses the engine loop (update & draw).
620
627
  */
@@ -658,19 +665,13 @@ type LitecanvasOptions = {
658
665
  */
659
666
  global?: boolean
660
667
  /**
661
- * Specify your game loop callbacks.
662
- * By default use that global functions (if they exist):
663
- * - `window.init(instance: LitecanvasInstance): void`
664
- * - `window.update(dt: number): void`
665
- * - `window.draw(ctx: CanvasRenderingContext2D): void`
666
- * - `window.resized(scale: number): void`
667
- * - `window.tap(tapX: number, tapY: number, touchId: number): void`
668
- * - `window.untap(tapX: number, tapY: number, touchId: number): void`
669
- * - `window.tapped(tapX: number, tapY: number, touchId: number): void`
670
- * - `window.tapping(tapX: number, tapY: number, touchId: number): void`
668
+ * Specify your game loop listener callbacks.
669
+ *
670
+ * By default, it uses global functions with the same name as the events (if they exist).
671
+ *
672
+ * Example: `window.init`, `window.update`, `window.draw`, etc
671
673
  */
672
674
  loop?: LitecanvasGameLoop
673
-
674
675
  /**
675
676
  * default: `true`
676
677
  *