litecanvas 0.84.2 → 0.85.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.dev.js CHANGED
@@ -50,16 +50,7 @@
50
50
  animate: true
51
51
  };
52
52
  settings = Object.assign(defaults, settings);
53
- let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _events = {
54
- init: null,
55
- update: null,
56
- draw: null,
57
- resized: null,
58
- tap: null,
59
- untap: null,
60
- tapping: null,
61
- tapped: null
62
- };
53
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
63
54
  const instance = {
64
55
  /** @type {number} */
65
56
  W: 0,
@@ -566,11 +557,11 @@
566
557
  "paint: 3rd param must be a function or array"
567
558
  );
568
559
  DEV: assert(
569
- options && !options.scale || isNumber(options.scale),
560
+ options && null == options.scale || isNumber(options.scale),
570
561
  "paint: 4th param (options.scale) must be a number"
571
562
  );
572
563
  DEV: assert(
573
- options && !options.canvas || options.canvas instanceof OffscreenCanvas,
564
+ options && null == options.canvas || options.canvas instanceof OffscreenCanvas,
574
565
  "paint: 4th param (options.canvas) must be an OffscreenCanvas"
575
566
  );
576
567
  const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1, contextOriginal = _ctx;
@@ -812,9 +803,9 @@
812
803
  DEV: assert("string" === typeof eventName, "listen: 1st param must be a string");
813
804
  DEV: assert("function" === typeof callback, "listen: 2nd param must be a function");
814
805
  eventName = eventName.toLowerCase();
815
- _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
816
- _events[eventName].add(callback);
817
- return () => _events[eventName].delete(callback);
806
+ _eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
807
+ _eventListeners[eventName].add(callback);
808
+ return () => _eventListeners && _eventListeners[eventName].delete(callback);
818
809
  },
819
810
  /**
820
811
  * Call all listeners attached to a game event
@@ -906,7 +897,7 @@
906
897
  // 3
907
898
  _scale,
908
899
  // 4
909
- _events,
900
+ _eventListeners,
910
901
  // 5
911
902
  _colors,
912
903
  // 6
@@ -934,10 +925,10 @@
934
925
  cancelAnimationFrame(_rafid);
935
926
  _rafid = 0;
936
927
  instance.emit("quit");
928
+ _eventListeners = {};
937
929
  for (const removeListener of _browserEventListeners) {
938
930
  removeListener();
939
931
  }
940
- _events = {};
941
932
  if (settings.global) {
942
933
  for (const key in instance) {
943
934
  delete root[key];
@@ -947,12 +938,12 @@
947
938
  _initialized = false;
948
939
  }
949
940
  };
950
- for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
941
+ for (const k of _mathFunctions.split(",")) {
951
942
  instance[k] = math[k];
952
943
  }
953
944
  function init() {
954
945
  const source = settings.loop ? settings.loop : root;
955
- for (const event in _events) {
946
+ for (const event of _coreEvents.split(",")) {
956
947
  if (source[event]) instance.listen(event, source[event]);
957
948
  }
958
949
  for (const [callback, config] of _plugins) {
@@ -1200,15 +1191,17 @@
1200
1191
  }
1201
1192
  }
1202
1193
  function setupCanvas() {
1203
- if (settings.canvas) {
1204
- DEV: assert(
1205
- "string" === typeof settings.canvas,
1206
- `Litecanvas' option "canvas" should be a string (a selector)`
1207
- );
1194
+ if ("string" === typeof settings.canvas) {
1208
1195
  _canvas = document.querySelector(settings.canvas);
1196
+ DEV: assert(null != _canvas, `Litecanvas' option "canvas" is an invalid CSS selector`);
1197
+ } else {
1198
+ _canvas = settings.canvas;
1209
1199
  }
1210
1200
  _canvas = _canvas || document.createElement("canvas");
1211
- DEV: assert(_canvas && _canvas.tagName === "CANVAS", "Invalid canvas element");
1201
+ DEV: assert(
1202
+ "CANVAS" === _canvas.tagName,
1203
+ `Litecanvas' option "canvas" should be a canvas element or string (CSS selector)`
1204
+ );
1212
1205
  _ctx = _canvas.getContext("2d");
1213
1206
  on(_canvas, "click", () => root.focus());
1214
1207
  _canvas.style = "";
@@ -1254,8 +1247,8 @@
1254
1247
  }
1255
1248
  }
1256
1249
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1257
- if (!_events[eventName]) return;
1258
- for (const callback of _events[eventName]) {
1250
+ if (!_eventListeners[eventName]) return;
1251
+ for (const callback of _eventListeners[eventName]) {
1259
1252
  callback(arg1, arg2, arg3, arg4);
1260
1253
  }
1261
1254
  }
package/dist/dist.js CHANGED
@@ -45,16 +45,7 @@
45
45
  animate: true
46
46
  };
47
47
  settings = Object.assign(defaults, settings);
48
- let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _events = {
49
- init: null,
50
- update: null,
51
- draw: null,
52
- resized: null,
53
- tap: null,
54
- untap: null,
55
- tapping: null,
56
- tapped: null
57
- };
48
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
58
49
  const instance = {
59
50
  /** @type {number} */
60
51
  W: 0,
@@ -613,9 +604,9 @@
613
604
  */
614
605
  listen(eventName, callback) {
615
606
  eventName = eventName.toLowerCase();
616
- _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
617
- _events[eventName].add(callback);
618
- return () => _events[eventName].delete(callback);
607
+ _eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
608
+ _eventListeners[eventName].add(callback);
609
+ return () => _eventListeners && _eventListeners[eventName].delete(callback);
619
610
  },
620
611
  /**
621
612
  * Call all listeners attached to a game event
@@ -689,7 +680,7 @@
689
680
  // 3
690
681
  _scale,
691
682
  // 4
692
- _events,
683
+ _eventListeners,
693
684
  // 5
694
685
  _colors,
695
686
  // 6
@@ -717,10 +708,10 @@
717
708
  cancelAnimationFrame(_rafid);
718
709
  _rafid = 0;
719
710
  instance.emit("quit");
711
+ _eventListeners = {};
720
712
  for (const removeListener of _browserEventListeners) {
721
713
  removeListener();
722
714
  }
723
- _events = {};
724
715
  if (settings.global) {
725
716
  for (const key in instance) {
726
717
  delete root[key];
@@ -730,12 +721,12 @@
730
721
  _initialized = false;
731
722
  }
732
723
  };
733
- for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
724
+ for (const k of _mathFunctions.split(",")) {
734
725
  instance[k] = math[k];
735
726
  }
736
727
  function init() {
737
728
  const source = settings.loop ? settings.loop : root;
738
- for (const event in _events) {
729
+ for (const event of _coreEvents.split(",")) {
739
730
  if (source[event]) instance.listen(event, source[event]);
740
731
  }
741
732
  for (const [callback, config] of _plugins) {
@@ -975,8 +966,10 @@
975
966
  }
976
967
  }
977
968
  function setupCanvas() {
978
- if (settings.canvas) {
969
+ if ("string" === typeof settings.canvas) {
979
970
  _canvas = document.querySelector(settings.canvas);
971
+ } else {
972
+ _canvas = settings.canvas;
980
973
  }
981
974
  _canvas = _canvas || document.createElement("canvas");
982
975
  _ctx = _canvas.getContext("2d");
@@ -1012,8 +1005,8 @@
1012
1005
  }
1013
1006
  }
1014
1007
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1015
- if (!_events[eventName]) return;
1016
- for (const callback of _events[eventName]) {
1008
+ if (!_eventListeners[eventName]) return;
1009
+ for (const callback of _eventListeners[eventName]) {
1017
1010
  callback(arg1, arg2, arg3, arg4);
1018
1011
  }
1019
1012
  }
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,n=Math,l=2*n.PI,i=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,n=.05,l=220,i=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,v=0,x=1,b=0,y=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=l*=(1-n+2*n*E.random(n=[]))*z/44100,I=0,P=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,H=E.cos(D),N=E.sin,W=N(D)/4,F=1+W,q=-2*H/F,B=(1-W)/F,O=(1+M*H)/2/F,V=-(M+H)/F,R=0,G=0,$=0,j=0;for(i=44100*i+9,b*=44100,r*=44100,o*=44100,v*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=i+b+r+o+v|0;A<M;n[A++]=Y*a)++X%(100*w|0)||(Y=s?1<s?2<s?3<s?N(I*I):E.max(E.min(E.tan(I),1),-1):1-(2*I/z%2+2)%2:1-4*E.abs(E.round(I/z)-I/z):N(I),Y=(h?1-y+y*N(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<i?A/i:A<i+b?1-(A-i)/b*(1-x):A<i+b+r?x:A<M-v?(M-A-v)/o*x:0),Y=v?Y/2+(v>A?0:(A<M-v?1:(M-A)/v)*n[A-v|0]/2/a):Y,k&&(Y=j=O*R+V*(R=G)+O*(G=Y)-B*$-q*($=j))),I+=(D=(l+=c+=d)*E.cos(m*P++))+D*g*N(A**5),L&&++L>u&&(l+=p,C+=p,L=0),!h||++S%h||(l=C,c=T,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(n),(l=t.createBufferSource()).buffer=a,l.connect(t.destination),l.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,v=0,x,b="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],T={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},C={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:l,HALF_PI:l/4,lerp:(e,t,a)=>a*(t-e)+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,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,n,l,i){let r=(e-t)/(a-t)*(l-n)+n;return i?C.clamp(r,n,l):r},norm:(e,t,a)=>C.map(e,t,a,0,1),wave:(e,t,a,n=Math.sin)=>e+(n(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(C.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):C.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,n,l,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~n+2*h,i),C.stroke(l)},rectfill(e,t,a,n,l,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~n,i),C.fill(l)},circ(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),C.stroke(n)},circfill(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),C.fill(n)},line(e,t,a,n,l){u.beginPath();let i=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~n);u.moveTo(~~e+i,~~t+r),u.lineTo(~~a+i,~~n+r),C.stroke(l)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,n=3,l="normal"){u.font=`${l} ${y}px ${b}`,u.fillStyle=E[~~n%E.length],u.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,n={}){let l=n.canvas||new OffscreenCanvas(1,1),i=n.scale||1,r=u;if(l.width=e*i,l.height=t*i,(u=l.getContext("2d")).scale(i,i),Array.isArray(a)){let e=0,t=0;for(let n of(u.imageSmoothingEnabled=!1,a)){for(let a of n)" "!==a&&"."!==a&&C.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,l.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=C.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,n=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(T[e=e.toLowerCase()]=T[e]||new Set,T[e].add(t),()=>T[e].delete(t)),emit(e,t,a,n,l){f&&(L("before:"+(e=e.toLowerCase()),t,a,n,l),L(e,t,a,n,l),L("after:"+e,t,a,n,l))},pal(t=e){E=t},def(e,n){C[e]=n,t.global&&(a[e]=n)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let n={index:e,value:[t,f,x,p,T,E,z,g,a.zzfxV||1,k,y,b][e]};return C.emit("stat",n),n.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,C.emit("quit"),r))e();if(T={},t.global){for(let e in C)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))C[e]=n[e];function I(){let e=t.loop?t.loop:a;for(let t in T)e[t]&&C.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&o(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,n=(e,a,n)=>{let l={x:a,y:n,startX:a,startY:n,ts:performance.now()};return t.set(e,l),l},l=(e,a,l)=>{let i=t.get(e)||n(e);i.x=a,i.y=l},i=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(d,"mousedown",t=>{if(0===t.button){r(t);let[a,l]=e(t.pageX,t.pageY);C.emit("tap",a,l,0),n(0,a,l),s=!0}}),o(d,"mouseup",a=>{if(0===a.button){r(a);let n=t.get(0),[l,o]=e(a.pageX,a.pageY);i(n)&&C.emit("tapped",n.startX,n.startY,0),C.emit("untap",l,o,0),t.delete(0),s=!1}}),o(d,"mousemove",t=>{r(t);let[a,n]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",n),s&&(C.emit("tapping",a,n,0),l(0,a,n))}),o(d,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);C.emit("tap",t,l,a.identifier+1),n(a.identifier+1,t,l)}}),o(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);C.emit("tapping",t,n,a.identifier+1),l(a.identifier+1,t,n)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,n]of t)a.includes(e)||(i(n)&&C.emit("tapped",n.startX,n.startY,e),C.emit("untap",n.x,n.y,e),t.delete(e))};o(d,"touchend",f),o(d,"touchcancel",f),o(a,"blur",()=>{for(let[e,a]of(s=!1,t))C.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,n=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),C.listen("after:update",()=>t.clear()),C.def("iskeydown",t=>n(e,t)),C.def("iskeypressed",e=>n(t,e))}f=!0,C.emit("init",C),m=performance.now(),x=i(P)}function P(e){let a=0;if(t.animate){for(v+=n.min(.1,(e-m)/1e3),m=e;v>=w;)a++,C.emit("update",w*g,a),C.def("T",C.T+w*g),v-=w;x&&(x=i(P))}else a=1;a&&(C.textalign("start","top"),C.emit("draw"))}function A(){let e=t.width||a.innerWidth,l=t.height||t.width||a.innerHeight;C.def("W",d.width=e),C.def("H",d.height=l),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(a.innerWidth/C.W,a.innerHeight/C.H),p=(t.pixelart?~~p:p)||1,d.style.width=C.W*p+"px",d.style.height=C.H*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),C.emit("resized",p),C.cls(0),t.animate||i(P)}function L(e,t,a,n,l){if(T[e])for(let i of T[e])i(t,a,n,l)}function S(e,t){let a=e(C,t);for(let e in a)C.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,C),a.ENGINE=C}return t.canvas&&(d=document.querySelector(t.canvas)),u=(d=d||document.createElement("canvas")).getContext("2d"),o(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>i(I)):i(I),C}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,n=Math,i=2*n.PI,l=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,n=.05,i=220,l=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,v=0,x=1,b=0,y=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=i*=(1-n+2*n*E.random(n=[]))*z/44100,I=0,P=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,H=E.cos(D),N=E.sin,W=N(D)/4,F=1+W,q=-2*H/F,B=(1-W)/F,O=(1+M*H)/2/F,V=-(M+H)/F,R=0,G=0,$=0,j=0;for(l=44100*l+9,b*=44100,r*=44100,o*=44100,v*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=l+b+r+o+v|0;A<M;n[A++]=Y*a)++X%(100*w|0)||(Y=s?1<s?2<s?3<s?N(I*I):E.max(E.min(E.tan(I),1),-1):1-(2*I/z%2+2)%2:1-4*E.abs(E.round(I/z)-I/z):N(I),Y=(h?1-y+y*N(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<l?A/l:A<l+b?1-(A-l)/b*(1-x):A<l+b+r?x:A<M-v?(M-A-v)/o*x:0),Y=v?Y/2+(v>A?0:(A<M-v?1:(M-A)/v)*n[A-v|0]/2/a):Y,k&&(Y=j=O*R+V*(R=G)+O*(G=Y)-B*$-q*($=j))),I+=(D=(i+=c+=d)*E.cos(m*P++))+D*g*N(A**5),L&&++L>u&&(i+=p,C+=p,L=0),!h||++S%h||(i=C,c=T,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(n),(i=t.createBufferSource()).buffer=a,i.connect(t.destination),i.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,v=0,x,b="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],T={},C={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:i/4,lerp:(e,t,a)=>a*(t-e)+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,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,n,i,l){let r=(e-t)/(a-t)*(i-n)+n;return l?C.clamp(r,n,i):r},norm:(e,t,a)=>C.map(e,t,a,0,1),wave:(e,t,a,n=Math.sin)=>e+(n(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(C.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):C.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~n+2*h,l),C.stroke(i)},rectfill(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e,~~t,~~a,~~n,l),C.fill(i)},circ(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),C.stroke(n)},circfill(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),C.fill(n)},line(e,t,a,n,i){u.beginPath();let l=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~n);u.moveTo(~~e+l,~~t+r),u.lineTo(~~a+l,~~n+r),C.stroke(i)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,n=3,i="normal"){u.font=`${i} ${y}px ${b}`,u.fillStyle=E[~~n%E.length],u.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,n={}){let i=n.canvas||new OffscreenCanvas(1,1),l=n.scale||1,r=u;if(i.width=e*l,i.height=t*l,(u=i.getContext("2d")).scale(l,l),Array.isArray(a)){let e=0,t=0;for(let n of(u.imageSmoothingEnabled=!1,a)){for(let a of n)" "!==a&&"."!==a&&C.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,i.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=C.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,n=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(T[e=e.toLowerCase()]=T[e]||new Set,T[e].add(t),()=>T&&T[e].delete(t)),emit(e,t,a,n,i){f&&(L("before:"+(e=e.toLowerCase()),t,a,n,i),L(e,t,a,n,i),L("after:"+e,t,a,n,i))},pal(t=e){E=t},def(e,n){C[e]=n,t.global&&(a[e]=n)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let n={index:e,value:[t,f,x,p,T,E,z,g,a.zzfxV||1,k,y,b][e]};return C.emit("stat",n),n.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,C.emit("quit"),T={},r))e();if(t.global){for(let e in C)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))C[e]=n[e];function I(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&C.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&o(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,n=(e,a,n)=>{let i={x:a,y:n,startX:a,startY:n,ts:performance.now()};return t.set(e,i),i},i=(e,a,i)=>{let l=t.get(e)||n(e);l.x=a,l.y=i},l=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(d,"mousedown",t=>{if(0===t.button){r(t);let[a,i]=e(t.pageX,t.pageY);C.emit("tap",a,i,0),n(0,a,i),s=!0}}),o(d,"mouseup",a=>{if(0===a.button){r(a);let n=t.get(0),[i,o]=e(a.pageX,a.pageY);l(n)&&C.emit("tapped",n.startX,n.startY,0),C.emit("untap",i,o,0),t.delete(0),s=!1}}),o(d,"mousemove",t=>{r(t);let[a,n]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",n),s&&(C.emit("tapping",a,n,0),i(0,a,n))}),o(d,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,i]=e(a.pageX,a.pageY);C.emit("tap",t,i,a.identifier+1),n(a.identifier+1,t,i)}}),o(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);C.emit("tapping",t,n,a.identifier+1),i(a.identifier+1,t,n)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,n]of t)a.includes(e)||(l(n)&&C.emit("tapped",n.startX,n.startY,e),C.emit("untap",n.x,n.y,e),t.delete(e))};o(d,"touchend",f),o(d,"touchcancel",f),o(a,"blur",()=>{for(let[e,a]of(s=!1,t))C.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,n=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),C.listen("after:update",()=>t.clear()),C.def("iskeydown",t=>n(e,t)),C.def("iskeypressed",e=>n(t,e))}f=!0,C.emit("init",C),m=performance.now(),x=l(P)}function P(e){let a=0;if(t.animate){for(v+=n.min(.1,(e-m)/1e3),m=e;v>=w;)a++,C.emit("update",w*g,a),C.def("T",C.T+w*g),v-=w;x&&(x=l(P))}else a=1;a&&(C.textalign("start","top"),C.emit("draw"))}function A(){let e=t.width||a.innerWidth,i=t.height||t.width||a.innerHeight;C.def("W",d.width=e),C.def("H",d.height=i),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(a.innerWidth/C.W,a.innerHeight/C.H),p=(t.pixelart?~~p:p)||1,d.style.width=C.W*p+"px",d.style.height=C.H*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),C.emit("resized",p),C.cls(0),t.animate||l(P)}function L(e,t,a,n,i){if(T[e])for(let l of T[e])l(t,a,n,i)}function S(e,t){let a=e(C,t);for(let e in a)C.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,C),a.ENGINE=C}return u=(d=(d="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),o(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>l(I)):l(I),C}})();
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.84.2",
3
+ "version": "0.85.1",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
7
7
  "contributors": [],
8
8
  "devDependencies": {
9
9
  "@litecanvas/jsdom-extras": "^1.1.0",
10
- "@swc/core": "^1.12.6",
10
+ "@swc/core": "^1.12.7",
11
11
  "@types/jsdom": "^21.1.7",
12
12
  "ava": "^6.4.0",
13
13
  "esbuild": "^0.25.5",
14
14
  "gzip-size": "^7.0.0",
15
15
  "jsdom": "^26.1.0",
16
- "prettier": "^3.6.0",
16
+ "prettier": "^3.6.2",
17
17
  "tap-min": "^3.0.0"
18
18
  },
19
19
  "homepage": "https://litecanvas.github.io/about.html",
package/src/index.js CHANGED
@@ -74,21 +74,15 @@ export default function litecanvas(settings = {}) {
74
74
  _colors = defaultPalette,
75
75
  /** @type {number[]} */
76
76
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
77
+ /** @type {string} */
78
+ _coreEvents = 'init,update,draw,tap,untap,tapping,tapped,resized',
79
+ /** @type {string} list of functions copied from `Math` module*/
80
+ _mathFunctions =
81
+ 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp',
77
82
  /**
78
- * default game events
79
- *
80
- * @type {Object<string,Set<Function>>}
83
+ * @type {Object<string,Set<Function>>} game event listeners
81
84
  */
82
- _events = {
83
- init: null,
84
- update: null,
85
- draw: null,
86
- resized: null,
87
- tap: null,
88
- untap: null,
89
- tapping: null,
90
- tapped: null,
91
- }
85
+ _eventListeners = {}
92
86
 
93
87
  /** @type {Omit<LitecanvasInstance,'PI'|'sin'|'cos'|'atan2'|'hypot'|'tan'|'abs'|'ceil'|'floor'|'trunc'|'min'|'max'|'pow'|'sqrt'|'sign'|'exp'|'iskeydown'|'iskeypressed'>} */
94
88
  const instance = {
@@ -659,11 +653,11 @@ export default function litecanvas(settings = {}) {
659
653
  'paint: 3rd param must be a function or array'
660
654
  )
661
655
  DEV: assert(
662
- (options && !options.scale) || isNumber(options.scale),
656
+ (options && null == options.scale) || isNumber(options.scale),
663
657
  'paint: 4th param (options.scale) must be a number'
664
658
  )
665
659
  DEV: assert(
666
- (options && !options.canvas) || options.canvas instanceof OffscreenCanvas,
660
+ (options && null == options.canvas) || options.canvas instanceof OffscreenCanvas,
667
661
  'paint: 4th param (options.canvas) must be an OffscreenCanvas'
668
662
  )
669
663
 
@@ -956,11 +950,11 @@ export default function litecanvas(settings = {}) {
956
950
 
957
951
  eventName = eventName.toLowerCase()
958
952
 
959
- _events[eventName] = _events[eventName] || new Set()
960
- _events[eventName].add(callback)
953
+ _eventListeners[eventName] = _eventListeners[eventName] || new Set()
954
+ _eventListeners[eventName].add(callback)
961
955
 
962
956
  // return a function to remove this event listener
963
- return () => _events[eventName].delete(callback)
957
+ return () => _eventListeners && _eventListeners[eventName].delete(callback)
964
958
  },
965
959
 
966
960
  /**
@@ -1063,7 +1057,7 @@ export default function litecanvas(settings = {}) {
1063
1057
  // 3
1064
1058
  _scale,
1065
1059
  // 4
1066
- _events,
1060
+ _eventListeners,
1067
1061
  // 5
1068
1062
  _colors,
1069
1063
  // 6
@@ -1101,14 +1095,14 @@ export default function litecanvas(settings = {}) {
1101
1095
  // emit "quit" event to manual clean ups
1102
1096
  instance.emit('quit')
1103
1097
 
1098
+ // clear all engine event listeners
1099
+ _eventListeners = {}
1100
+
1104
1101
  // clear all browser event listeners
1105
1102
  for (const removeListener of _browserEventListeners) {
1106
1103
  removeListener()
1107
1104
  }
1108
1105
 
1109
- // clear all engine event listeners
1110
- _events = {}
1111
-
1112
1106
  // maybe clear global context
1113
1107
  if (settings.global) {
1114
1108
  for (const key in instance) {
@@ -1124,7 +1118,7 @@ export default function litecanvas(settings = {}) {
1124
1118
  }
1125
1119
 
1126
1120
  // prettier-ignore
1127
- for (const k of 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp'.split(',')) {
1121
+ for (const k of _mathFunctions.split(',')) {
1128
1122
  // import native Math functions
1129
1123
  instance[k] = math[k]
1130
1124
  }
@@ -1132,7 +1126,7 @@ export default function litecanvas(settings = {}) {
1132
1126
  function init() {
1133
1127
  // setup default event listeners
1134
1128
  const source = settings.loop ? settings.loop : root
1135
- for (const event in _events) {
1129
+ for (const event of _coreEvents.split(',')) {
1136
1130
  if (source[event]) instance.listen(event, source[event])
1137
1131
  }
1138
1132
 
@@ -1441,17 +1435,19 @@ export default function litecanvas(settings = {}) {
1441
1435
  }
1442
1436
 
1443
1437
  function setupCanvas() {
1444
- if (settings.canvas) {
1445
- DEV: assert(
1446
- 'string' === typeof settings.canvas,
1447
- 'Litecanvas\' option "canvas" should be a string (a selector)'
1448
- )
1438
+ if ('string' === typeof settings.canvas) {
1449
1439
  _canvas = document.querySelector(settings.canvas)
1440
+ DEV: assert(null != _canvas, 'Litecanvas\' option "canvas" is an invalid CSS selector')
1441
+ } else {
1442
+ _canvas = settings.canvas
1450
1443
  }
1451
1444
 
1452
1445
  _canvas = _canvas || document.createElement('canvas')
1453
1446
 
1454
- DEV: assert(_canvas && _canvas.tagName === 'CANVAS', 'Invalid canvas element')
1447
+ DEV: assert(
1448
+ 'CANVAS' === _canvas.tagName,
1449
+ 'Litecanvas\' option "canvas" should be a canvas element or string (CSS selector)'
1450
+ )
1455
1451
 
1456
1452
  _ctx = _canvas.getContext('2d')
1457
1453
 
@@ -1525,8 +1521,8 @@ export default function litecanvas(settings = {}) {
1525
1521
  * @param {*} arg4
1526
1522
  */
1527
1523
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1528
- if (!_events[eventName]) return
1529
- for (const callback of _events[eventName]) {
1524
+ if (!_eventListeners[eventName]) return
1525
+ for (const callback of _eventListeners[eventName]) {
1530
1526
  callback(arg1, arg2, arg3, arg4)
1531
1527
  }
1532
1528
  }
package/types/types.d.ts CHANGED
@@ -578,7 +578,7 @@ type LitecanvasOptions = {
578
578
  /**
579
579
  * Used to specify the selector of a custom canvas element
580
580
  */
581
- canvas?: string
581
+ canvas?: HTMLCanvasElement | string
582
582
  /**
583
583
  * If `true` (default) scales the canvas to fill the screen, but preserving the aspect ratio.
584
584
  * Only works if a game screen width was specified.
@@ -592,7 +592,6 @@ type LitecanvasOptions = {
592
592
  * If `false` (default), disable the canvas antialias.
593
593
  */
594
594
  antialias?: boolean
595
-
596
595
  /**
597
596
  * If `true` (default), all methods and properties of the engine will be exposed to the global scope (window).
598
597
  */