litecanvas 0.81.2 → 0.83.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
@@ -44,7 +44,6 @@
44
44
  canvas: null,
45
45
  global: true,
46
46
  loop: null,
47
- pauseOnBlur: true,
48
47
  tapEvents: true,
49
48
  keyboardEvents: true,
50
49
  animate: true
@@ -855,6 +854,7 @@
855
854
  "function" === typeof callback,
856
855
  "listen: 2nd param must be a function"
857
856
  );
857
+ eventName = eventName.toLowerCase();
858
858
  _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
859
859
  _events[eventName].add(callback);
860
860
  return () => _events[eventName].delete(callback);
@@ -874,6 +874,7 @@
874
874
  "emit: 1st param must be a string"
875
875
  );
876
876
  if (_initialized) {
877
+ eventName = eventName.toLowerCase();
877
878
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
878
879
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
879
880
  triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
@@ -975,13 +976,16 @@
975
976
  // 11
976
977
  _fontFamily
977
978
  ];
978
- return list[n];
979
+ const data = { index: n, value: list[n] };
980
+ instance.emit("stat", data);
981
+ return data.value;
979
982
  },
980
983
  /**
981
984
  * Stops the litecanvas instance and remove all event listeners.
982
985
  */
983
986
  quit() {
984
987
  cancelAnimationFrame(_rafid);
988
+ _rafid = 0;
985
989
  instance.emit("quit");
986
990
  for (const removeListener of _browserEventListeners) {
987
991
  removeListener();
@@ -1142,25 +1146,24 @@
1142
1146
  });
1143
1147
  }
1144
1148
  if (settings.keyboardEvents) {
1145
- const toLowerCase = (s) => s.toLowerCase();
1146
1149
  const _keysDown = /* @__PURE__ */ new Set();
1147
1150
  const _keysPress = /* @__PURE__ */ new Set();
1148
- const keyCheck = (keysSet, key) => {
1149
- return !key ? keysSet.size > 0 : keysSet.has(
1150
- "space" === toLowerCase(key) ? " " : toLowerCase(key)
1151
- );
1151
+ const keyCheck = (keySet, key = "") => {
1152
+ key = key.toLowerCase();
1153
+ return !key ? keySet.size > 0 : keySet.has("space" === key ? " " : key);
1152
1154
  };
1153
1155
  on(root, "keydown", (event) => {
1154
- if (!_keysDown.has(toLowerCase(event.key))) {
1155
- _keysDown.add(toLowerCase(event.key));
1156
- _keysPress.add(toLowerCase(event.key));
1156
+ const key = event.key.toLowerCase();
1157
+ if (!_keysDown.has(key)) {
1158
+ _keysDown.add(key);
1159
+ _keysPress.add(key);
1157
1160
  }
1158
1161
  });
1159
1162
  on(root, "keyup", (event) => {
1160
- _keysDown.delete(toLowerCase(event.key));
1163
+ _keysDown.delete(event.key.toLowerCase());
1161
1164
  });
1162
1165
  on(root, "blur", () => _keysDown.clear());
1163
- instance.listen("after:draw", () => _keysPress.clear());
1166
+ instance.listen("after:update", () => _keysPress.clear());
1164
1167
  instance.def(
1165
1168
  "iskeydown",
1166
1169
  /**
@@ -1196,17 +1199,6 @@
1196
1199
  }
1197
1200
  );
1198
1201
  }
1199
- if (settings.pauseOnBlur) {
1200
- on(root, "blur", () => {
1201
- _rafid = cancelAnimationFrame(_rafid);
1202
- });
1203
- on(root, "focus", () => {
1204
- if (!_rafid) {
1205
- _accumulated = 0;
1206
- _rafid = raf(drawFrame);
1207
- }
1208
- });
1209
- }
1210
1202
  _initialized = true;
1211
1203
  instance.emit("init", instance);
1212
1204
  _lastFrameTime = performance.now();
@@ -1216,17 +1208,18 @@
1216
1208
  let updated = 0, frameTime = (now - _lastFrameTime) / 1e3;
1217
1209
  _lastFrameTime = now;
1218
1210
  if (settings.animate) {
1219
- _rafid = raf(drawFrame);
1220
1211
  if (frameTime > 0.3) {
1221
- return console.warn("skipping too long frame");
1222
- }
1223
- _accumulated += frameTime;
1224
- while (_accumulated >= _deltaTime) {
1225
- instance.emit("update", _deltaTime * _timeScale);
1226
- instance.def("T", instance.T + _deltaTime * _timeScale);
1227
- updated++;
1228
- _accumulated -= _deltaTime;
1212
+ console.warn("skipping too long frame");
1213
+ } else {
1214
+ _accumulated += frameTime;
1215
+ while (_accumulated >= _deltaTime) {
1216
+ updated++;
1217
+ instance.emit("update", _deltaTime * _timeScale, updated);
1218
+ instance.def("T", instance.T + _deltaTime * _timeScale);
1219
+ _accumulated -= _deltaTime;
1220
+ }
1229
1221
  }
1222
+ if (_rafid) _rafid = raf(drawFrame);
1230
1223
  } else {
1231
1224
  updated = 1;
1232
1225
  }
@@ -1236,11 +1229,14 @@
1236
1229
  }
1237
1230
  }
1238
1231
  function setupCanvas() {
1239
- if ("string" === typeof settings.canvas) {
1232
+ if (settings.canvas) {
1233
+ DEV: assert(
1234
+ "string" === typeof settings.canvas,
1235
+ `Litecanvas' option "canvas" should be a string (a selector)`
1236
+ );
1240
1237
  _canvas = document.querySelector(settings.canvas);
1241
- } else {
1242
- _canvas = settings.canvas || document.createElement("canvas");
1243
1238
  }
1239
+ _canvas = _canvas || document.createElement("canvas");
1244
1240
  DEV: assert(
1245
1241
  _canvas && _canvas.tagName === "CANVAS",
1246
1242
  "Invalid canvas element"
package/dist/dist.js CHANGED
@@ -39,7 +39,6 @@
39
39
  canvas: null,
40
40
  global: true,
41
41
  loop: null,
42
- pauseOnBlur: true,
43
42
  tapEvents: true,
44
43
  keyboardEvents: true,
45
44
  animate: true
@@ -611,6 +610,7 @@
611
610
  * @returns {Function} a function to remove the listener
612
611
  */
613
612
  listen(eventName, callback) {
613
+ eventName = eventName.toLowerCase();
614
614
  _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
615
615
  _events[eventName].add(callback);
616
616
  return () => _events[eventName].delete(callback);
@@ -626,6 +626,7 @@
626
626
  */
627
627
  emit(eventName, arg1, arg2, arg3, arg4) {
628
628
  if (_initialized) {
629
+ eventName = eventName.toLowerCase();
629
630
  triggerEvent("before:" + eventName, arg1, arg2, arg3, arg4);
630
631
  triggerEvent(eventName, arg1, arg2, arg3, arg4);
631
632
  triggerEvent("after:" + eventName, arg1, arg2, arg3, arg4);
@@ -702,13 +703,16 @@
702
703
  // 11
703
704
  _fontFamily
704
705
  ];
705
- return list[n];
706
+ const data = { index: n, value: list[n] };
707
+ instance.emit("stat", data);
708
+ return data.value;
706
709
  },
707
710
  /**
708
711
  * Stops the litecanvas instance and remove all event listeners.
709
712
  */
710
713
  quit() {
711
714
  cancelAnimationFrame(_rafid);
715
+ _rafid = 0;
712
716
  instance.emit("quit");
713
717
  for (const removeListener of _browserEventListeners) {
714
718
  removeListener();
@@ -869,25 +873,24 @@
869
873
  });
870
874
  }
871
875
  if (settings.keyboardEvents) {
872
- const toLowerCase = (s) => s.toLowerCase();
873
876
  const _keysDown = /* @__PURE__ */ new Set();
874
877
  const _keysPress = /* @__PURE__ */ new Set();
875
- const keyCheck = (keysSet, key) => {
876
- return !key ? keysSet.size > 0 : keysSet.has(
877
- "space" === toLowerCase(key) ? " " : toLowerCase(key)
878
- );
878
+ const keyCheck = (keySet, key = "") => {
879
+ key = key.toLowerCase();
880
+ return !key ? keySet.size > 0 : keySet.has("space" === key ? " " : key);
879
881
  };
880
882
  on(root, "keydown", (event) => {
881
- if (!_keysDown.has(toLowerCase(event.key))) {
882
- _keysDown.add(toLowerCase(event.key));
883
- _keysPress.add(toLowerCase(event.key));
883
+ const key = event.key.toLowerCase();
884
+ if (!_keysDown.has(key)) {
885
+ _keysDown.add(key);
886
+ _keysPress.add(key);
884
887
  }
885
888
  });
886
889
  on(root, "keyup", (event) => {
887
- _keysDown.delete(toLowerCase(event.key));
890
+ _keysDown.delete(event.key.toLowerCase());
888
891
  });
889
892
  on(root, "blur", () => _keysDown.clear());
890
- instance.listen("after:draw", () => _keysPress.clear());
893
+ instance.listen("after:update", () => _keysPress.clear());
891
894
  instance.def(
892
895
  "iskeydown",
893
896
  /**
@@ -915,17 +918,6 @@
915
918
  }
916
919
  );
917
920
  }
918
- if (settings.pauseOnBlur) {
919
- on(root, "blur", () => {
920
- _rafid = cancelAnimationFrame(_rafid);
921
- });
922
- on(root, "focus", () => {
923
- if (!_rafid) {
924
- _accumulated = 0;
925
- _rafid = raf(drawFrame);
926
- }
927
- });
928
- }
929
921
  _initialized = true;
930
922
  instance.emit("init", instance);
931
923
  _lastFrameTime = performance.now();
@@ -935,17 +927,17 @@
935
927
  let updated = 0, frameTime = (now - _lastFrameTime) / 1e3;
936
928
  _lastFrameTime = now;
937
929
  if (settings.animate) {
938
- _rafid = raf(drawFrame);
939
930
  if (frameTime > 0.3) {
940
- return void 0;
941
- }
942
- _accumulated += frameTime;
943
- while (_accumulated >= _deltaTime) {
944
- instance.emit("update", _deltaTime * _timeScale);
945
- instance.def("T", instance.T + _deltaTime * _timeScale);
946
- updated++;
947
- _accumulated -= _deltaTime;
931
+ } else {
932
+ _accumulated += frameTime;
933
+ while (_accumulated >= _deltaTime) {
934
+ updated++;
935
+ instance.emit("update", _deltaTime * _timeScale, updated);
936
+ instance.def("T", instance.T + _deltaTime * _timeScale);
937
+ _accumulated -= _deltaTime;
938
+ }
948
939
  }
940
+ if (_rafid) _rafid = raf(drawFrame);
949
941
  } else {
950
942
  updated = 1;
951
943
  }
@@ -955,11 +947,10 @@
955
947
  }
956
948
  }
957
949
  function setupCanvas() {
958
- if ("string" === typeof settings.canvas) {
950
+ if (settings.canvas) {
959
951
  _canvas = document.querySelector(settings.canvas);
960
- } else {
961
- _canvas = settings.canvas || document.createElement("canvas");
962
952
  }
953
+ _canvas = _canvas || document.createElement("canvas");
963
954
  instance.def("CANVAS", _canvas);
964
955
  _ctx = _canvas.getContext("2d");
965
956
  on(_canvas, "click", () => root.focus());
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,r=.1,o=0,s=1,f=0,c=0,d=0,u=0,p=0,h=0,g=0,m=0,b=0,v=1,w=0,x=0,y=0)=>{let k=Math,E=2*k.PI,T=f*=500*E/44100/44100,A=l*=(1-a+2*a*k.random(a=[]))*E/44100,C=0,z=0,I=0,P=1,S=0,X=0,Y=0,M=y<0?-1:1,N=E*M*y*2/44100,H=k.cos(N),W=k.sin,B=W(N)/4,D=1+B,F=-2*H/D,L=(1-B)/D,O=(1+M*H)/2/D,V=-(M+H)/D,q=0,R=0,G=0,$=0;for(n=44100*n+9,w*=44100,i*=44100,r*=44100,b*=44100,c*=500*E/85766121e6,g*=E/44100,d*=E/44100,u*=44100,p=44100*p|0,t*=.3*(globalThis.zzfxV||1),M=n+w+i+r+b|0;I<M;a[I++]=Y*t)++X%(100*m|0)||(Y=o?1<o?2<o?3<o?W(C*C):k.max(k.min(k.tan(C),1),-1):1-(2*C/E%2+2)%2:1-4*k.abs(k.round(C/E)-C/E):W(C),Y=(p?1-x+x*W(E*I/p):1)*(Y<0?-1:1)*k.abs(Y)**s*(I<n?I/n:I<n+w?1-(I-n)/w*(1-v):I<n+w+i?v:I<M-b?(M-I-b)/r*v:0),Y=b?Y/2+(b>I?0:(I<M-b?1:(M-I)/b)*a[I-b|0]/2/t):Y,y&&(Y=$=O*q+V*(q=R)+O*(R=Y)-L*G-F*(G=$))),C+=(N=(l+=f+=c)*k.cos(g*z++))+N*h*W(I**5),P&&++P>u&&(l+=d,A+=d,P=0),!p||++S%p||(l=A,f=T,P=P||1);(t=e.createBuffer(1,M,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math,i=2*n.PI,r=requestAnimationFrame,o=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let f=!1,c=[],d,u=1,p,h=.5,g=1,m,b=1/60,v=0,w,x="sans-serif",y=20,k=Date.now(),E=a,T=[.5,0,1750,,,.3,1,,,,600,.1],A={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},C={CANVAS:null,W:0,H:0,T:0,CX:0,CY:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:n.PI/2,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,l,n,i){let r=(e-t)/(a-t)*(n-l)+l;return i?C.clamp(r,l,n):r},norm:(e,t,a)=>C.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(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?p.clearRect(0,0,p.canvas.width,p.canvas.height):C.rectfill(0,0,p.canvas.width,p.canvas.height,e)},rect(e,t,a,l,n,i){p.beginPath(),p[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),C.stroke(n)},rectfill(e,t,a,l,n,i){p.beginPath(),p[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),C.fill(n)},circ(e,t,a,l){p.beginPath(),p.arc(~~e,~~t,~~a,0,i),C.stroke(l)},circfill(e,t,a,l){p.beginPath(),p.arc(~~e,~~t,~~a,0,i),C.fill(l)},line(e,t,a,l,n){p.beginPath();let i=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~l);p.moveTo(~~e+i,~~t+r),p.lineTo(~~a+i,~~l+r),C.stroke(n)},linewidth(e){p.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){p.setLineDash(e),p.lineDashOffset=t},text(e,t,a,l=3,n="normal"){p.font=`${n} ${y}px ${x}`,p.fillStyle=E[~~l%E.length],p.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(p.textAlign=e),t&&(p.textBaseline=t)},image(e,t,a){p.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,r=p;if(n.width=e*i,n.height=t*i,(p=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(p.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&C.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(p);return p=r,n.transferToImageBitmap()},ctx:e=>(e&&(p=e),p),push:()=>p.save(),pop:()=>p.restore(),translate:(e,t)=>p.translate(~~e,~~t),scale:(e,t)=>p.scale(e,t||e),rotate:e=>p.rotate(e),alpha(e){p.globalAlpha=C.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){p.fillStyle=E[~~e%E.length],t?p.fill(t):p.fill()},stroke(e,t){p.strokeStyle=E[~~e%E.length],t?p.stroke(t):p.stroke()},clip(e){p.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||T,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},use(e,t={}){f?X(e,t):c.push([e,t])},listen:(e,t)=>(A[e]=A[e]||new Set,A[e].add(t),()=>A[e].delete(t)),emit(e,t,a,l,n){f&&(S("before:"+e,t,a,l,n),S(e,t,a,l,n),S("after:"+e,t,a,l,n))},pal(e=a){E=e},def(t,a){C[t]=a,e.global&&(l[t]=a)},timescale(e){g=e},framerate(e){b=1/~~e},stat:t=>[e,f,w,u,A,E,T,g,l.zzfxV||1,k,y,x][t],quit(){for(let e of(cancelAnimationFrame(w),C.emit("quit"),o))e();if(A={},e.global){for(let e in C)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(","))C[e]=n[e];function z(){let t=e.loop?e.loop:l;for(let e in A)t[e]&&C.listen(e,t[e]);for(let[e,t]of c)X(e,t);if(e.autoscale&&s(l,"resize",P),e.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/u,(t-d.offsetTop)/u],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),o=!1;s(d,"mousedown",t=>{if(0===t.button){r(t);let[l,n]=e(t.pageX,t.pageY);C.emit("tap",l,n,0),a(0,l,n),o=!0}}),s(d,"mouseup",a=>{if(0===a.button){r(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&C.emit("tapped",l.startX,l.startY,0),C.emit("untap",n,s,0),t.delete(0),o=!1}}),s(d,"mousemove",t=>{r(t);let[a,l]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",l),o&&(C.emit("tapping",a,l,0),n(0,a,l))}),s(d,"touchstart",t=>{for(let l of(r(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);C.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);C.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});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,l]of t)a.includes(e)||(i(l)&&C.emit("tapped",l.startX,l.startY,e),C.emit("untap",l.x,l.y,e),t.delete(e))};s(d,"touchend",f),s(d,"touchcancel",f),s(l,"blur",()=>{for(let[e,a]of(o=!1,t))C.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=e=>e.toLowerCase(),t=new Set,a=new Set,n=(t,a)=>a?t.has("space"===e(a)?" ":e(a)):t.size>0;s(l,"keydown",l=>{t.has(e(l.key))||(t.add(e(l.key)),a.add(e(l.key)))}),s(l,"keyup",a=>{t.delete(e(a.key))}),s(l,"blur",()=>t.clear()),C.listen("after:draw",()=>a.clear()),C.def("iskeydown",e=>n(t,e)),C.def("iskeypressed",e=>n(a,e))}e.pauseOnBlur&&(s(l,"blur",()=>{w=cancelAnimationFrame(w)}),s(l,"focus",()=>{w||(v=0,w=r(I))})),f=!0,C.emit("init",C),m=performance.now(),w=r(I)}function I(t){let a=0,l=(t-m)/1e3;if(m=t,e.animate){if(w=r(I),l>.3)return;for(v+=l;v>=b;)C.emit("update",b*g),C.def("T",C.T+b*g),a++,v-=b}else a=1;a&&(C.textalign("start","top"),C.emit("draw"))}function P(){let t=e.width||l.innerWidth,a=e.height||e.width||l.innerHeight;C.def("W",d.width=t),C.def("H",d.height=a),C.def("CX",C.W/2),C.def("CY",C.H/2),e.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),u=n.min(l.innerWidth/C.W,l.innerHeight/C.H),u=(e.pixelart?~~u:u)||1,d.style.width=C.W*u+"px",d.style.height=C.H*u+"px"),(!e.antialias||e.pixelart)&&(p.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),C.emit("resized",u),C.cls(0),e.animate||r(I)}function S(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function X(e,t){let a=e(C,t);for(let e in a)C.def(e,a[e])}if(e.global){if(l.ENGINE)throw Error("two global litecanvas detected");Object.assign(l,C),l.ENGINE=C}return d="string"==typeof e.canvas?document.querySelector(e.canvas):e.canvas||document.createElement("canvas"),C.def("CANVAS",d),p=d.getContext("2d"),s(d,"click",()=>l.focus()),d.style="",P(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>r(z)):r(z),C}})();
1
+ (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,o=.1,r=0,s=1,f=0,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,b=1,v=0,x=0,y=0)=>{let C=Math,k=2*C.PI,E=f*=500*k/44100/44100,T=l*=(1-a+2*a*C.random(a=[]))*k/44100,z=0,A=0,I=0,P=1,S=0,X=0,Y=0,L=y<0?-1:1,M=k*L*y*2/44100,N=C.cos(M),H=C.sin,W=H(M)/4,D=1+W,F=-2*N/D,V=(1-W)/D,q=(1+L*N)/2/D,B=-(L+N)/D,O=0,R=0,G=0,$=0;for(n=44100*n+9,v*=44100,i*=44100,o*=44100,w*=44100,c*=500*k/85766121e6,g*=k/44100,d*=k/44100,p*=44100,u=44100*u|0,t*=.3*(globalThis.zzfxV||1),L=n+v+i+o+w|0;I<L;a[I++]=Y*t)++X%(100*m|0)||(Y=r?1<r?2<r?3<r?H(z*z):C.max(C.min(C.tan(z),1),-1):1-(2*z/k%2+2)%2:1-4*C.abs(C.round(z/k)-z/k):H(z),Y=(u?1-x+x*H(k*I/u):1)*(Y<0?-1:1)*C.abs(Y)**s*(I<n?I/n:I<n+v?1-(I-n)/v*(1-b):I<n+v+i?b:I<L-w?(L-I-w)/o*b:0),Y=w?Y/2+(w>I?0:(I<L-w?1:(L-I)/w)*a[I-w|0]/2/t):Y,y&&(Y=$=q*O+B*(O=R)+q*(R=Y)-V*G-F*(G=$))),z+=(M=(l+=f+=c)*C.cos(g*A++))+M*h*H(I**5),P&&++P>p&&(l+=d,T+=d,P=0),!u||++S%u||(l=T,f=E,P=P||1);(t=e.createBuffer(1,L,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math,i=2*n.PI,o=requestAnimationFrame,r=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,b=0,v,x="sans-serif",y=20,C=Date.now(),k=a,E=[.5,0,1750,,,.3,1,,,,600,.1],T={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},z={CANVAS:null,W:0,H:0,T:0,CX:0,CY:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:n.PI/2,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,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?z.clamp(o,l,n):o},norm:(e,t,a)=>z.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(z.rand(e,t+1)),rseed(e){C=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):z.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),z.stroke(n)},rectfill(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),z.fill(n)},circ(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),z.stroke(l)},circfill(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),z.fill(l)},line(e,t,a,l,n){u.beginPath();let i=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~l);u.moveTo(~~e+i,~~t+o),u.lineTo(~~a+i,~~l+o),z.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,l=3,n="normal"){u.font=`${n} ${y}px ${x}`,u.fillStyle=k[~~l%k.length],u.fillText(a,~~e,~~t)},textfont(e){x=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,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=u;if(n.width=e*i,n.height=t*i,(u=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(u.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&z.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=o,n.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=z.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=k[~~e%k.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=k[~~e%k.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||E,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},use(e,t={}){f?X(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,l,n){f&&(S("before:"+(e=e.toLowerCase()),t,a,l,n),S(e,t,a,l,n),S("after:"+e,t,a,l,n))},pal(e=a){k=e},def(t,a){z[t]=a,e.global&&(l[t]=a)},timescale(e){g=e},framerate(e){w=1/~~e},stat(t){let a={index:t,value:[e,f,v,p,T,k,E,g,l.zzfxV||1,C,y,x][t]};return z.emit("stat",a),a.value},quit(){for(let e of(cancelAnimationFrame(v),v=0,z.emit("quit"),r))e();if(T={},e.global){for(let e in z)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(","))z[e]=n[e];function A(){let t=e.loop?e.loop:l;for(let e in T)t[e]&&z.listen(e,t[e]);for(let[e,t]of c)X(e,t);if(e.autoscale&&s(l,"resize",P),e.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),r=!1;s(d,"mousedown",t=>{if(0===t.button){o(t);let[l,n]=e(t.pageX,t.pageY);z.emit("tap",l,n,0),a(0,l,n),r=!0}}),s(d,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&z.emit("tapped",l.startX,l.startY,0),z.emit("untap",n,s,0),t.delete(0),r=!1}}),s(d,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);z.def("MX",a),z.def("MY",l),r&&(z.emit("tapping",a,l,0),n(0,a,l))}),s(d,"touchstart",t=>{for(let l of(o(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);z.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(d,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);z.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let f=e=>{o(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&z.emit("tapped",l.startX,l.startY,e),z.emit("untap",l.x,l.y,e),t.delete(e))};s(d,"touchend",f),s(d,"touchcancel",f),s(l,"blur",()=>{for(let[e,a]of(r=!1,t))z.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;s(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),s(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),s(l,"blur",()=>e.clear()),z.listen("after:update",()=>t.clear()),z.def("iskeydown",t=>a(e,t)),z.def("iskeypressed",e=>a(t,e))}f=!0,z.emit("init",z),m=performance.now(),v=o(I)}function I(t){let a=0,l=(t-m)/1e3;if(m=t,e.animate){if(l>.3);else for(b+=l;b>=w;)a++,z.emit("update",w*g,a),z.def("T",z.T+w*g),b-=w;v&&(v=o(I))}else a=1;a&&(z.textalign("start","top"),z.emit("draw"))}function P(){let t=e.width||l.innerWidth,a=e.height||e.width||l.innerHeight;z.def("W",d.width=t),z.def("H",d.height=a),z.def("CX",z.W/2),z.def("CY",z.H/2),e.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(l.innerWidth/z.W,l.innerHeight/z.H),p=(e.pixelart?~~p:p)||1,d.style.width=z.W*p+"px",d.style.height=z.H*p+"px"),(!e.antialias||e.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),z.emit("resized",p),z.cls(0),e.animate||o(I)}function S(e,t,a,l,n){if(T[e])for(let i of T[e])i(t,a,l,n)}function X(e,t){let a=e(z,t);for(let e in a)z.def(e,a[e])}if(e.global){if(l.ENGINE)throw Error("two global litecanvas detected");Object.assign(l,z),l.ENGINE=z}return e.canvas&&(d=document.querySelector(e.canvas)),d=d||document.createElement("canvas"),z.def("CANVAS",d),u=d.getContext("2d"),s(d,"click",()=>l.focus()),d.style="",P(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>o(A)):o(A),z}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.81.2",
3
+ "version": "0.83.0",
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>",
@@ -24,7 +24,7 @@
24
24
  "main": "src/index.js",
25
25
  "types": "types/index.d.ts",
26
26
  "scripts": {
27
- "test": "npm run build && ava",
27
+ "test": "ava",
28
28
  "dev:test": "ava --watch",
29
29
  "dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
30
30
  "build": "node script/build.js",
package/src/index.js CHANGED
@@ -35,7 +35,6 @@ export default function litecanvas(settings = {}) {
35
35
  canvas: null,
36
36
  global: true,
37
37
  loop: null,
38
- pauseOnBlur: true,
39
38
  tapEvents: true,
40
39
  keyboardEvents: true,
41
40
  animate: true,
@@ -1014,6 +1013,8 @@ export default function litecanvas(settings = {}) {
1014
1013
  'listen: 2nd param must be a function'
1015
1014
  )
1016
1015
 
1016
+ eventName = eventName.toLowerCase()
1017
+
1017
1018
  _events[eventName] = _events[eventName] || new Set()
1018
1019
  _events[eventName].add(callback)
1019
1020
 
@@ -1036,6 +1037,8 @@ export default function litecanvas(settings = {}) {
1036
1037
  'emit: 1st param must be a string'
1037
1038
  )
1038
1039
  if (_initialized) {
1040
+ eventName = eventName.toLowerCase()
1041
+
1039
1042
  triggerEvent('before:' + eventName, arg1, arg2, arg3, arg4)
1040
1043
  triggerEvent(eventName, arg1, arg2, arg3, arg4)
1041
1044
  triggerEvent('after:' + eventName, arg1, arg2, arg3, arg4)
@@ -1146,7 +1149,13 @@ export default function litecanvas(settings = {}) {
1146
1149
  // 11
1147
1150
  _fontFamily,
1148
1151
  ]
1149
- return list[n]
1152
+
1153
+ const data = { index: n, value: list[n] }
1154
+
1155
+ // plugins can modify or create stat values
1156
+ instance.emit('stat', data)
1157
+
1158
+ return data.value
1150
1159
  },
1151
1160
 
1152
1161
  /**
@@ -1155,6 +1164,7 @@ export default function litecanvas(settings = {}) {
1155
1164
  quit() {
1156
1165
  // stop the game loop (update & draw)
1157
1166
  cancelAnimationFrame(_rafid)
1167
+ _rafid = 0
1158
1168
 
1159
1169
  // emit "quit" event to manual clean ups
1160
1170
  instance.emit('quit')
@@ -1361,8 +1371,6 @@ export default function litecanvas(settings = {}) {
1361
1371
  }
1362
1372
 
1363
1373
  if (settings.keyboardEvents) {
1364
- const toLowerCase = (/** @type {string} */ s) => s.toLowerCase()
1365
-
1366
1374
  /** @type {Set<string>} */
1367
1375
  const _keysDown = new Set()
1368
1376
 
@@ -1370,34 +1378,34 @@ export default function litecanvas(settings = {}) {
1370
1378
  const _keysPress = new Set()
1371
1379
 
1372
1380
  /**
1373
- * @param {Set<string>} keysSet
1381
+ * @param {Set<string>} keySet
1374
1382
  * @param {string} [key]
1375
1383
  * @returns {boolean}
1376
1384
  */
1377
- const keyCheck = (keysSet, key) => {
1385
+ const keyCheck = (keySet, key = '') => {
1386
+ key = key.toLowerCase()
1378
1387
  return !key
1379
- ? keysSet.size > 0
1380
- : keysSet.has(
1381
- 'space' === toLowerCase(key) ? ' ' : toLowerCase(key)
1382
- )
1388
+ ? keySet.size > 0
1389
+ : keySet.has('space' === key ? ' ' : key)
1383
1390
  }
1384
1391
 
1385
1392
  // @ts-ignore
1386
1393
  on(root, 'keydown', (/** @type {KeyboardEvent} */ event) => {
1387
- if (!_keysDown.has(toLowerCase(event.key))) {
1388
- _keysDown.add(toLowerCase(event.key))
1389
- _keysPress.add(toLowerCase(event.key))
1394
+ const key = event.key.toLowerCase()
1395
+ if (!_keysDown.has(key)) {
1396
+ _keysDown.add(key)
1397
+ _keysPress.add(key)
1390
1398
  }
1391
1399
  })
1392
1400
 
1393
1401
  // @ts-ignore
1394
1402
  on(root, 'keyup', (/** @type {KeyboardEvent} */ event) => {
1395
- _keysDown.delete(toLowerCase(event.key))
1403
+ _keysDown.delete(event.key.toLowerCase())
1396
1404
  })
1397
1405
 
1398
1406
  // @ts-ignore
1399
1407
  on(root, 'blur', () => _keysDown.clear())
1400
- instance.listen('after:draw', () => _keysPress.clear())
1408
+ instance.listen('after:update', () => _keysPress.clear())
1401
1409
 
1402
1410
  instance.def(
1403
1411
  'iskeydown',
@@ -1436,23 +1444,6 @@ export default function litecanvas(settings = {}) {
1436
1444
  )
1437
1445
  }
1438
1446
 
1439
- // listen browser focus/blur events and pause the update/draw loop
1440
- if (settings.pauseOnBlur) {
1441
- // @ts-ignore
1442
- on(root, 'blur', () => {
1443
- // @ts-ignore
1444
- _rafid = cancelAnimationFrame(_rafid)
1445
- })
1446
-
1447
- // @ts-ignore
1448
- on(root, 'focus', () => {
1449
- if (!_rafid) {
1450
- _accumulated = 0
1451
- _rafid = raf(drawFrame)
1452
- }
1453
- })
1454
- }
1455
-
1456
1447
  _initialized = true
1457
1448
 
1458
1449
  // start the game loop
@@ -1472,24 +1463,26 @@ export default function litecanvas(settings = {}) {
1472
1463
  _lastFrameTime = now
1473
1464
 
1474
1465
  if (settings.animate) {
1475
- // request the next frame
1476
- _rafid = raf(drawFrame)
1477
-
1478
1466
  if (frameTime > 0.3) {
1479
- return console.warn('skipping too long frame')
1480
- }
1481
-
1482
- _accumulated += frameTime
1467
+ console.warn('skipping too long frame')
1468
+ } else {
1469
+ _accumulated += frameTime
1483
1470
 
1484
- while (_accumulated >= _deltaTime) {
1485
- instance.emit('update', _deltaTime * _timeScale)
1486
- instance.def('T', instance.T + _deltaTime * _timeScale)
1487
- updated++
1488
- _accumulated -= _deltaTime
1471
+ while (_accumulated >= _deltaTime) {
1472
+ updated++
1473
+ instance.emit('update', _deltaTime * _timeScale, updated)
1474
+ instance.def('T', instance.T + _deltaTime * _timeScale)
1475
+ _accumulated -= _deltaTime
1476
+ }
1489
1477
  }
1478
+
1479
+ // request the next frame
1480
+ // check if the last ID exists, because
1481
+ // quit() delete it (sets to zero)
1482
+ if (_rafid) _rafid = raf(drawFrame)
1490
1483
  } else {
1491
1484
  // when the canvas is not animated
1492
- // we for one frame when a redraw is triggered
1485
+ // we force one frame when redraws are triggered
1493
1486
  updated = 1
1494
1487
  }
1495
1488
 
@@ -1502,12 +1495,16 @@ export default function litecanvas(settings = {}) {
1502
1495
  }
1503
1496
 
1504
1497
  function setupCanvas() {
1505
- if ('string' === typeof settings.canvas) {
1498
+ if (settings.canvas) {
1499
+ DEV: assert(
1500
+ 'string' === typeof settings.canvas,
1501
+ 'Litecanvas\' option "canvas" should be a string (a selector)'
1502
+ )
1506
1503
  _canvas = document.querySelector(settings.canvas)
1507
- } else {
1508
- _canvas = settings.canvas || document.createElement('canvas')
1509
1504
  }
1510
1505
 
1506
+ _canvas = _canvas || document.createElement('canvas')
1507
+
1511
1508
  DEV: assert(
1512
1509
  _canvas && _canvas.tagName === 'CANVAS',
1513
1510
  'Invalid canvas element'
package/types/types.d.ts CHANGED
@@ -591,10 +591,9 @@ type LitecanvasOptions = {
591
591
  */
592
592
  height?: number
593
593
  /**
594
- * Determines whether the game loop should be paused when the "blur" event happens.
595
- * Default: `true`
594
+ * Used to specify the selector of a custom canvas element
596
595
  */
597
- pauseOnBlur?: boolean
596
+ canvas?: string
598
597
  /**
599
598
  * If `true` (default) scales the canvas to fill the screen, but preserving the aspect ratio.
600
599
  * Only works if a game screen width was specified.
@@ -608,10 +607,7 @@ type LitecanvasOptions = {
608
607
  * If `false` (default), disable the canvas antialias.
609
608
  */
610
609
  antialias?: boolean
611
- /**
612
- * Used to specify the selector of a custom canvas element
613
- */
614
- canvas?: string
610
+
615
611
  /**
616
612
  * If `true` (default), all methods and properties of the engine will be exposed to the global scope (window).
617
613
  */