litecanvas 0.84.1 → 0.84.2

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
@@ -33,10 +33,10 @@
33
33
 
34
34
  // src/index.js
35
35
  function litecanvas(settings = {}) {
36
- const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
36
+ const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
37
37
  elem.addEventListener(evt, callback, false);
38
38
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
39
- }, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
39
+ }, isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
40
40
  width: null,
41
41
  height: null,
42
42
  autoscale: true,
@@ -556,8 +556,11 @@
556
556
  * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
557
557
  */
558
558
  paint(width, height, drawing, options = {}) {
559
- DEV: assert(isNumber(width), "paint: 1st param must be a number");
560
- DEV: assert(isNumber(height), "paint: 2nd param must be a number");
559
+ DEV: assert(isNumber(width) && width >= 1, "paint: 1st param must be a positive number");
560
+ DEV: assert(
561
+ isNumber(height) && height >= 1,
562
+ "paint: 2nd param must be a positive number"
563
+ );
561
564
  DEV: assert(
562
565
  "function" === typeof drawing || Array.isArray(drawing),
563
566
  "paint: 3rd param must be a function or array"
@@ -566,12 +569,16 @@
566
569
  options && !options.scale || isNumber(options.scale),
567
570
  "paint: 4th param (options.scale) must be a number"
568
571
  );
572
+ DEV: assert(
573
+ options && !options.canvas || options.canvas instanceof OffscreenCanvas,
574
+ "paint: 4th param (options.canvas) must be an OffscreenCanvas"
575
+ );
569
576
  const canvas = options.canvas || new OffscreenCanvas(1, 1), scale = options.scale || 1, contextOriginal = _ctx;
570
577
  canvas.width = width * scale;
571
578
  canvas.height = height * scale;
572
579
  _ctx = canvas.getContext("2d");
573
580
  _ctx.scale(scale, scale);
574
- if (drawing.push) {
581
+ if (Array.isArray(drawing)) {
575
582
  let x = 0, y = 0;
576
583
  _ctx.imageSmoothingEnabled = false;
577
584
  for (const str of drawing) {
@@ -748,7 +755,10 @@
748
755
  );
749
756
  DEV: assert(isNumber(pitchSlide), "sfx: 2nd param must be a number");
750
757
  DEV: assert(isNumber(volumeFactor), "sfx: 3rd param must be a number");
751
- if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
758
+ if (
759
+ // @ts-ignore
760
+ root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive
761
+ ) {
752
762
  return false;
753
763
  }
754
764
  zzfxParams = zzfxParams || _defaultSound;
@@ -776,9 +786,7 @@
776
786
  *
777
787
  * @returns {HTMLCanvasElement}
778
788
  */
779
- canvas() {
780
- return _canvas;
781
- },
789
+ canvas: () => _canvas,
782
790
  /**
783
791
  * Prepares a plugin to be loaded
784
792
  *
@@ -887,7 +895,7 @@
887
895
  * @returns {any}
888
896
  */
889
897
  stat(n) {
890
- DEV: assert(isNumber(n) && n >= 0, "stat: 1st param must be a positive number");
898
+ DEV: assert(isNumber(n) && n >= 0, "stat: 1st param must be a number");
891
899
  const list = [
892
900
  // 0
893
901
  settings,
@@ -906,6 +914,7 @@
906
914
  // 7
907
915
  _timeScale,
908
916
  // 8
917
+ // @ts-ignore
909
918
  root.zzfxV || 1,
910
919
  // 9
911
920
  _rngSeed,
@@ -935,6 +944,7 @@
935
944
  }
936
945
  delete root.ENGINE;
937
946
  }
947
+ _initialized = false;
938
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(",")) {
package/dist/dist.js CHANGED
@@ -28,10 +28,10 @@
28
28
 
29
29
  // src/index.js
30
30
  function litecanvas(settings = {}) {
31
- const root = globalThis, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
31
+ const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
32
32
  elem.addEventListener(evt, callback, false);
33
33
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
34
- }, zzfx = setupZzFX(root), isNumber = Number.isFinite, defaults = {
34
+ }, isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
35
35
  width: null,
36
36
  height: null,
37
37
  autoscale: true,
@@ -417,7 +417,7 @@
417
417
  canvas.height = height * scale;
418
418
  _ctx = canvas.getContext("2d");
419
419
  _ctx.scale(scale, scale);
420
- if (drawing.push) {
420
+ if (Array.isArray(drawing)) {
421
421
  let x = 0, y = 0;
422
422
  _ctx.imageSmoothingEnabled = false;
423
423
  for (const str of drawing) {
@@ -561,7 +561,10 @@
561
561
  * @see https://github.com/KilledByAPixel/ZzFX
562
562
  */
563
563
  sfx(zzfxParams, pitchSlide = 0, volumeFactor = 1) {
564
- if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
564
+ if (
565
+ // @ts-ignore
566
+ root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive
567
+ ) {
565
568
  return false;
566
569
  }
567
570
  zzfxParams = zzfxParams || _defaultSound;
@@ -588,9 +591,7 @@
588
591
  *
589
592
  * @returns {HTMLCanvasElement}
590
593
  */
591
- canvas() {
592
- return _canvas;
593
- },
594
+ canvas: () => _canvas,
594
595
  /**
595
596
  * Prepares a plugin to be loaded
596
597
  *
@@ -696,6 +697,7 @@
696
697
  // 7
697
698
  _timeScale,
698
699
  // 8
700
+ // @ts-ignore
699
701
  root.zzfxV || 1,
700
702
  // 9
701
703
  _rngSeed,
@@ -725,6 +727,7 @@
725
727
  }
726
728
  delete root.ENGINE;
727
729
  }
730
+ _initialized = false;
728
731
  }
729
732
  };
730
733
  for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
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=globalThis,l=Math,n=2*l.PI,i=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.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=n*=(1-l+2*l*E.random(l=[]))*z/44100,I=0,P=0,L=0,S=1,X=0,Y=0,A=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,o*=44100,r*=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+o+r+v|0;L<M;l[L++]=A*a)++Y%(100*w|0)||(A=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),A=(h?1-y+y*N(z*L/h):1)*(A<0?-1:1)*E.abs(A)**f*(L<i?L/i:L<i+b?1-(L-i)/b*(1-x):L<i+b+o?x:L<M-v?(M-L-v)/r*x:0),A=v?A/2+(v>L?0:(L<M-v?1:(M-L)/v)*l[L-v|0]/2/a):A,k&&(A=j=O*R+V*(R=G)+O*(G=A)-B*$-q*($=j))),I+=(D=(n+=c+=d)*E.cos(m*P++))+D*g*N(L**5),S&&++S>u&&(n+=p,C+=p,S=0),!h||++X%h||(n=C,c=T,S=S||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(l),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.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:n,HALF_PI:n/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>l.PI/180*e,rad2deg:e=>180/l.PI*e,round:(e,t=0)=>{if(!t)return l.round(e);let a=10**t;return l.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*l.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?C.clamp(o,l,n):o},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)=>l.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,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),C.stroke(n)},rectfill(e,t,a,l,n,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),C.fill(n)},circ(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),C.stroke(l)},circfill(e,t,a,l){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),C.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),C.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 ${b}`,u.fillStyle=E[~~l%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,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&&C.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=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,l=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,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(t=e){E=t},def(e,l){C[e]=l,t.global&&(a[e]=l)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let l={index:e,value:[t,f,x,p,T,E,z,g,a.zzfxV||1,k,y,b][e]};return C.emit("stat",l),l.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,C.emit("quit"),o))e();if(T={},t.global){for(let e in C)delete a[e];delete a.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))C[e]=l[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)X(e,t);if(t.autoscale&&r(a,"resize",L),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,a,n)=>{let i=t.get(e)||l(e);i.x=a,i.y=n},i=e=>e&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),s=!1;r(d,"mousedown",t=>{if(0===t.button){o(t);let[a,n]=e(t.pageX,t.pageY);C.emit("tap",a,n,0),l(0,a,n),s=!0}}),r(d,"mouseup",a=>{if(0===a.button){o(a);let l=t.get(0),[n,r]=e(a.pageX,a.pageY);i(l)&&C.emit("tapped",l.startX,l.startY,0),C.emit("untap",n,r,0),t.delete(0),s=!1}}),r(d,"mousemove",t=>{o(t);let[a,l]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",l),s&&(C.emit("tapping",a,l,0),n(0,a,l))}),r(d,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);C.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(d,"touchmove",t=>{for(let a of(o(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=>{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)&&C.emit("tapped",l.startX,l.startY,e),C.emit("untap",l.x,l.y,e),t.delete(e))};r(d,"touchend",f),r(d,"touchcancel",f),r(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,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),C.listen("after:update",()=>t.clear()),C.def("iskeydown",t=>l(e,t)),C.def("iskeypressed",e=>l(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+=l.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 L(){let e=t.width||a.innerWidth,n=t.height||t.width||a.innerHeight;C.def("W",d.width=e),C.def("H",d.height=n),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=l.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 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(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"),r(d,"click",()=>a.focus()),d.style="",L(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?r(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,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}})();
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.84.1",
3
+ "version": "0.84.2",
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
- "@swc/core": "^1.12.5",
9
+ "@litecanvas/jsdom-extras": "^1.1.0",
10
+ "@swc/core": "^1.12.6",
11
+ "@types/jsdom": "^21.1.7",
10
12
  "ava": "^6.4.0",
11
13
  "esbuild": "^0.25.5",
12
14
  "gzip-size": "^7.0.0",
13
- "prettier": "^3.5.3",
14
- "tap-min": "^3.0.0",
15
- "@types/jsdom": "^21.1.7",
16
- "jsdom": "^26.1.0"
15
+ "jsdom": "^26.1.0",
16
+ "prettier": "^3.6.0",
17
+ "tap-min": "^3.0.0"
17
18
  },
18
19
  "homepage": "https://litecanvas.github.io/about.html",
19
20
  "repository": {
@@ -56,8 +57,10 @@
56
57
  ],
57
58
  "ava": {
58
59
  "files": [
59
- "tests/**/*",
60
- "!tests/_mocks"
60
+ "tests/**/*.js"
61
61
  ]
62
- }
62
+ },
63
+ "trustedDependencies": [
64
+ "@swc/core"
65
+ ]
63
66
  }
package/src/index.js CHANGED
@@ -10,20 +10,20 @@ import { assert } from './dev.js'
10
10
  * @returns {LitecanvasInstance}
11
11
  */
12
12
  export default function litecanvas(settings = {}) {
13
- const /** @type {typeof globalThis} */
14
- root = globalThis,
13
+ const /** @type {Window} */
14
+ root = window,
15
15
  math = Math,
16
16
  TWO_PI = math.PI * 2,
17
17
  raf = requestAnimationFrame,
18
18
  /** @type {Function[]} */
19
19
  _browserEventListeners = [],
20
- /** @type {(elem:HTMLElement, evt:string, callback:(event:Event)=>void)=>void} */
20
+ /** @type {(elem: EventTarget, evt: string, callback: (event: Event) => void) => void} */
21
21
  on = (elem, evt, callback) => {
22
22
  elem.addEventListener(evt, callback, false)
23
23
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
24
24
  },
25
- zzfx = setupZzFX(root),
26
25
  isNumber = Number.isFinite,
26
+ zzfx = setupZzFX(root),
27
27
  /** @type {LitecanvasOptions} */
28
28
  defaults = {
29
29
  width: null,
@@ -649,8 +649,11 @@ export default function litecanvas(settings = {}) {
649
649
  * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
650
650
  */
651
651
  paint(width, height, drawing, options = {}) {
652
- DEV: assert(isNumber(width), 'paint: 1st param must be a number')
653
- DEV: assert(isNumber(height), 'paint: 2nd param must be a number')
652
+ DEV: assert(isNumber(width) && width >= 1, 'paint: 1st param must be a positive number')
653
+ DEV: assert(
654
+ isNumber(height) && height >= 1,
655
+ 'paint: 2nd param must be a positive number'
656
+ )
654
657
  DEV: assert(
655
658
  'function' === typeof drawing || Array.isArray(drawing),
656
659
  'paint: 3rd param must be a function or array'
@@ -659,6 +662,10 @@ export default function litecanvas(settings = {}) {
659
662
  (options && !options.scale) || isNumber(options.scale),
660
663
  'paint: 4th param (options.scale) must be a number'
661
664
  )
665
+ DEV: assert(
666
+ (options && !options.canvas) || options.canvas instanceof OffscreenCanvas,
667
+ 'paint: 4th param (options.canvas) must be an OffscreenCanvas'
668
+ )
662
669
 
663
670
  const /** @type {OffscreenCanvas} */
664
671
  canvas = options.canvas || new OffscreenCanvas(1, 1),
@@ -667,19 +674,17 @@ export default function litecanvas(settings = {}) {
667
674
 
668
675
  canvas.width = width * scale
669
676
  canvas.height = height * scale
670
- _ctx = canvas.getContext('2d')
671
677
 
678
+ _ctx = canvas.getContext('2d')
672
679
  _ctx.scale(scale, scale)
673
680
 
674
681
  // draw pixel art if `draw` is a array
675
- // @ts-ignore
676
- if (drawing.push) {
682
+ if (Array.isArray(drawing)) {
677
683
  let x = 0,
678
684
  y = 0
679
685
 
680
686
  _ctx.imageSmoothingEnabled = false
681
687
 
682
- // @ts-ignore
683
688
  for (const str of drawing) {
684
689
  for (const color of str) {
685
690
  if (' ' !== color && '.' !== color) {
@@ -692,7 +697,6 @@ export default function litecanvas(settings = {}) {
692
697
  x = 0
693
698
  }
694
699
  } else {
695
- // @ts-ignore
696
700
  drawing(_ctx)
697
701
  }
698
702
 
@@ -880,6 +884,7 @@ export default function litecanvas(settings = {}) {
880
884
  DEV: assert(isNumber(volumeFactor), 'sfx: 3rd param must be a number')
881
885
 
882
886
  if (
887
+ // @ts-ignore
883
888
  root.zzfxV <= 0 ||
884
889
  (navigator.userActivation && !navigator.userActivation.hasBeenActive)
885
890
  ) {
@@ -909,6 +914,7 @@ export default function litecanvas(settings = {}) {
909
914
  volume(value) {
910
915
  DEV: assert(isNumber(value), 'volume: 1st param must be a number')
911
916
 
917
+ // @ts-ignore
912
918
  root.zzfxV = value
913
919
  },
914
920
 
@@ -918,9 +924,7 @@ export default function litecanvas(settings = {}) {
918
924
  *
919
925
  * @returns {HTMLCanvasElement}
920
926
  */
921
- canvas() {
922
- return _canvas
923
- },
927
+ canvas: () => _canvas,
924
928
  /**
925
929
  * Prepares a plugin to be loaded
926
930
  *
@@ -1047,7 +1051,7 @@ export default function litecanvas(settings = {}) {
1047
1051
  * @returns {any}
1048
1052
  */
1049
1053
  stat(n) {
1050
- DEV: assert(isNumber(n) && n >= 0, 'stat: 1st param must be a positive number')
1054
+ DEV: assert(isNumber(n) && n >= 0, 'stat: 1st param must be a number')
1051
1055
 
1052
1056
  const list = [
1053
1057
  // 0
@@ -1067,6 +1071,7 @@ export default function litecanvas(settings = {}) {
1067
1071
  // 7
1068
1072
  _timeScale,
1069
1073
  // 8
1074
+ // @ts-ignore
1070
1075
  root.zzfxV || 1,
1071
1076
  // 9
1072
1077
  _rngSeed,
@@ -1090,6 +1095,7 @@ export default function litecanvas(settings = {}) {
1090
1095
  quit() {
1091
1096
  // stop the game loop (update & draw)
1092
1097
  cancelAnimationFrame(_rafid)
1098
+
1093
1099
  _rafid = 0
1094
1100
 
1095
1101
  // emit "quit" event to manual clean ups
@@ -1108,8 +1114,12 @@ export default function litecanvas(settings = {}) {
1108
1114
  for (const key in instance) {
1109
1115
  delete root[key]
1110
1116
  }
1117
+ // @ts-ignore
1111
1118
  delete root.ENGINE
1112
1119
  }
1120
+
1121
+ // unset that flag
1122
+ _initialized = false
1113
1123
  },
1114
1124
  }
1115
1125
 
@@ -1133,7 +1143,6 @@ export default function litecanvas(settings = {}) {
1133
1143
 
1134
1144
  // listen window resize event when "autoscale" is enabled
1135
1145
  if (settings.autoscale) {
1136
- // @ts-ignore
1137
1146
  on(root, 'resize', resizeCanvas)
1138
1147
  }
1139
1148
 
@@ -1306,7 +1315,6 @@ export default function litecanvas(settings = {}) {
1306
1315
  on(_canvas, 'touchend', _touchEndHandler)
1307
1316
  on(_canvas, 'touchcancel', _touchEndHandler)
1308
1317
 
1309
- // @ts-ignore
1310
1318
  on(root, 'blur', () => {
1311
1319
  _pressingMouse = false
1312
1320
  for (const [id, tap] of _taps) {
@@ -1333,7 +1341,6 @@ export default function litecanvas(settings = {}) {
1333
1341
  return !key ? keySet.size > 0 : keySet.has('space' === key ? ' ' : key)
1334
1342
  }
1335
1343
 
1336
- // @ts-ignore
1337
1344
  on(root, 'keydown', (/** @type {KeyboardEvent} */ event) => {
1338
1345
  const key = event.key.toLowerCase()
1339
1346
  if (!_keysDown.has(key)) {
@@ -1342,12 +1349,10 @@ export default function litecanvas(settings = {}) {
1342
1349
  }
1343
1350
  })
1344
1351
 
1345
- // @ts-ignore
1346
1352
  on(root, 'keyup', (/** @type {KeyboardEvent} */ event) => {
1347
1353
  _keysDown.delete(event.key.toLowerCase())
1348
1354
  })
1349
1355
 
1350
- // @ts-ignore
1351
1356
  on(root, 'blur', () => _keysDown.clear())
1352
1357
  instance.listen('after:update', () => _keysPress.clear())
1353
1358
 
@@ -1521,7 +1526,6 @@ export default function litecanvas(settings = {}) {
1521
1526
  */
1522
1527
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1523
1528
  if (!_events[eventName]) return
1524
- // @ts-ignore
1525
1529
  for (const callback of _events[eventName]) {
1526
1530
  callback(arg1, arg2, arg3, arg4)
1527
1531
  }
@@ -1546,17 +1550,18 @@ export default function litecanvas(settings = {}) {
1546
1550
  }
1547
1551
 
1548
1552
  if (settings.global) {
1553
+ // @ts-ignore
1549
1554
  if (root.ENGINE) {
1550
1555
  throw new Error('two global litecanvas detected')
1551
1556
  }
1552
1557
  Object.assign(root, instance)
1558
+ // @ts-ignore
1553
1559
  root.ENGINE = instance
1554
1560
  }
1555
1561
 
1556
1562
  setupCanvas()
1557
1563
 
1558
1564
  if ('loading' === document.readyState) {
1559
- // @ts-ignore
1560
1565
  on(root, 'DOMContentLoaded', () => raf(init))
1561
1566
  } else {
1562
1567
  raf(init)
package/src/zzfx.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.0 by Frank Force | https://github.com/KilledByAPixel/ZzFX
2
2
  // prettier-ignore
3
3
  /**
4
- * @param {typeof globalThis} global
4
+ * @param {Window} global
5
5
  * @returns {Function} the `zzfx()` function
6
6
  */
7
7
  export const setupZzFX = (global) => {
package/types/index.d.ts CHANGED
@@ -515,7 +515,7 @@ declare global {
515
515
  * @param callback the function that is called when the event occurs
516
516
  * @returns a function to remove the listener
517
517
  */
518
- function listen(event: string, callback: Function): Function | null
518
+ function listen(event: string, callback: Function): Function
519
519
  /**
520
520
  * Call all listeners attached to a game event
521
521
  *
package/types/types.d.ts CHANGED
@@ -502,7 +502,7 @@ type LitecanvasInstance = {
502
502
  * @param callback the function that is called when the event occurs
503
503
  * @returns a function to remove the listener
504
504
  */
505
- listen(event: string, callback: Function): Function | null
505
+ listen(event: string, callback: Function): Function
506
506
  /**
507
507
  * Call all listeners attached to a game event
508
508
  *
@@ -645,6 +645,6 @@ type LitecanvasGameLoop = {
645
645
  tapping?: (tapX: number, tapY: number, tapId: number) => void
646
646
  }
647
647
 
648
- type drawCallback = (context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) => void
648
+ type drawCallback = (context: OffscreenCanvasRenderingContext2D) => void
649
649
 
650
650
  type pluginCallback = (instance: LitecanvasInstance, config?: any) => any