litecanvas 0.200.0 → 0.201.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.201.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
  }
@@ -1229,9 +1231,9 @@
1229
1231
  }
1230
1232
  _canvas = _canvas || document.createElement("canvas");
1231
1233
  DEV: assert(
1232
- "CANVAS" === _canvas.tagName,
1234
+ _canvas instanceof HTMLElement && "CANVAS" === _canvas.tagName,
1233
1235
  loggerPrefix +
1234
- 'litecanvas() option "canvas" should be a canvas element or string (CSS selector)',
1236
+ 'litecanvas() option "canvas" should be a canvas element or string (CSS selector of a canvas)',
1235
1237
  );
1236
1238
  _ctx = _canvas.getContext("2d");
1237
1239
  on(_canvas, "click", () => focus());
@@ -1284,9 +1286,10 @@
1284
1286
  instance.emit("resized", _canvasScale);
1285
1287
  }
1286
1288
  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);
1289
+ if (_eventListeners[eventName]) {
1290
+ for (const callback of _eventListeners[eventName]) {
1291
+ callback(arg1, arg2, arg3, arg4);
1292
+ }
1290
1293
  }
1291
1294
  }
1292
1295
  function loadPlugin(callback, config) {
@@ -1314,12 +1317,11 @@
1314
1317
  DEV: console.info(loggerPrefix + `version ${version} started`);
1315
1318
  DEV: console.debug(loggerPrefix + `litecanvas() options =`, settings);
1316
1319
  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}()`);
1320
+ if (settings.loop) {
1321
+ for (const eventName in settings.loop) {
1322
+ if (settings.loop[eventName])
1323
+ instance.listen(eventName, settings.loop[eventName]);
1321
1324
  }
1322
- if (source[event]) instance.listen(event, source[event]);
1323
1325
  }
1324
1326
  if ("loading" === document.readyState) {
1325
1327
  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
  }
@@ -702,9 +704,10 @@
702
704
  instance.emit("resized", _canvasScale);
703
705
  }
704
706
  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);
707
+ if (_eventListeners[eventName]) {
708
+ for (const callback of _eventListeners[eventName]) {
709
+ callback(arg1, arg2, arg3, arg4);
710
+ }
708
711
  }
709
712
  }
710
713
  function loadPlugin(callback, config) {
@@ -725,9 +728,11 @@
725
728
  root.ENGINE = instance;
726
729
  }
727
730
  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]);
731
+ if (settings.loop) {
732
+ for (const eventName in settings.loop) {
733
+ if (settings.loop[eventName])
734
+ instance.listen(eventName, settings.loop[eventName]);
735
+ }
731
736
  }
732
737
  if ("loading" === document.readyState) {
733
738
  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,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,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,w*=44100,c*=500*P/85766121e6,g*=P/44100,u*=P/44100,p*=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):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-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)*E.cos(g*I++))+N*m*q(L**5),D&&++D>p&&(n+=u,z+=u,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,u=!0,p,h=1,m,g=.5,v=1,w,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?m.clearRect(0,0,S.W,S.H):S.rectfill(0,0,S.W,S.H,e)},rect(e,t,a,l,n,i){m.beginPath(),m[i?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~l+2*g,i),S.stroke(n)},rectfill(e,t,a,l,n,i){m.beginPath(),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},circ(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,o),S.stroke(l)},circfill(e,t,a,l){m.beginPath(),m.arc(~~e,~~t,~~a,0,o),S.fill(l)},oval(e,t,a,l,n){m.beginPath(),m.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){m.beginPath(),m.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.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,a,l,n){m.beginPath();let i=.5*(0!==g&&~~e==~~a),o=.5*(0!==g&&~~t==~~l);m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~l+o),S.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,a,l=k,n="normal"){m.font=`${n} ${P}px ${E}`,m.fillStyle=W(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)m.fillText(i[a],~~e,~~t+P*T*a)},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,a){m.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=m;return n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a(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){m.scale(e,t||e)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=S.clamp(e,0,1)},fill(e){m.fillStyle=W(e),m.fill()},stroke(e){m.strokeStyle=W(e),m.stroke()},clip(e){m.beginPath(),e(m),m.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:()=>p,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){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,z,P,E,I,T][e]};return S.emit("stat",a),a.value},pause(){u=!0,cancelAnimationFrame(b)},resume(){c&&u&&(u=!1,y=x,w=i.now(),b=r(M))},paused:()=>u,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-p.offsetLeft)/h,(e.pageY-p.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(p,"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(p,"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(p,"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(p,"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(p,"touchend",s),f(p,"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",m),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),p.width=e,p.height=a,t.autoscale){let l=+t.autoscale;p.style.display||(p.style.display="block",p.style.margin="auto"),h=n.min(innerWidth/e,innerHeight/a),h=l>1&&h>l?l:h,p.style.width=e*h+"px",p.style.height=a*h+"px"}m.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(m=(p=(p="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),f(p,"click",()=>focus()),H(),p.parentNode||document.body.appendChild(p),p.style.imageRendering="pixelated",p.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.201.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')
@@ -1682,9 +1694,9 @@ export default function litecanvas(settings = {}) {
1682
1694
  _canvas = _canvas || document.createElement('canvas')
1683
1695
 
1684
1696
  DEV: assert(
1685
- 'CANVAS' === _canvas.tagName,
1697
+ _canvas instanceof HTMLElement && 'CANVAS' === _canvas.tagName,
1686
1698
  loggerPrefix +
1687
- 'litecanvas() option "canvas" should be a canvas element or string (CSS selector)'
1699
+ 'litecanvas() option "canvas" should be a canvas element or string (CSS selector of a canvas)'
1688
1700
  )
1689
1701
 
1690
1702
  _ctx = _canvas.getContext('2d')
@@ -1698,6 +1710,8 @@ export default function litecanvas(settings = {}) {
1698
1710
  }
1699
1711
 
1700
1712
  _canvas.style.imageRendering = 'pixelated'
1713
+
1714
+ // disable default browser's right click in canvas
1701
1715
  _canvas.oncontextmenu = () => false
1702
1716
  }
1703
1717
 
@@ -1752,21 +1766,22 @@ export default function litecanvas(settings = {}) {
1752
1766
 
1753
1767
  /**
1754
1768
  * @param {string} eventName
1755
- * @param {*} arg1
1756
- * @param {*} arg2
1757
- * @param {*} arg3
1758
- * @param {*} arg4
1769
+ * @param {any} [arg1]
1770
+ * @param {any} [arg2]
1771
+ * @param {any} [arg3]
1772
+ * @param {any} [arg4]
1759
1773
  */
1760
1774
  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)
1775
+ if (_eventListeners[eventName]) {
1776
+ for (const callback of _eventListeners[eventName]) {
1777
+ callback(arg1, arg2, arg3, arg4)
1778
+ }
1764
1779
  }
1765
1780
  }
1766
1781
 
1767
1782
  /**
1768
1783
  * @param {pluginCallback} callback
1769
- * @param {*} config
1784
+ * @param {any} config
1770
1785
  */
1771
1786
  function loadPlugin(callback, config) {
1772
1787
  const pluginData = callback(instance, config)
@@ -1805,12 +1820,10 @@ export default function litecanvas(settings = {}) {
1805
1820
  setupCanvas()
1806
1821
 
1807
1822
  // 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}()`)
1823
+ if (settings.loop) {
1824
+ for (const eventName in settings.loop) {
1825
+ if (settings.loop[eventName]) instance.listen(eventName, settings.loop[eventName])
1812
1826
  }
1813
- if (source[event]) instance.listen(event, source[event])
1814
1827
  }
1815
1828
 
1816
1829
  // 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.201.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
  /**
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
  /**
@@ -658,19 +666,13 @@ type LitecanvasOptions = {
658
666
  */
659
667
  global?: boolean
660
668
  /**
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`
669
+ * Specify your game loop listener callbacks.
670
+ *
671
+ * By default, it uses global functions with the same name as the events (if they exist).
672
+ *
673
+ * Example: `window.init`, `window.update`, `window.draw`, etc
671
674
  */
672
675
  loop?: LitecanvasGameLoop
673
-
674
676
  /**
675
677
  * default: `true`
676
678
  *