litecanvas 0.98.4 → 0.99.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/README.md CHANGED
@@ -205,6 +205,7 @@ const japan = paint(
205
205
 
206
206
  function draw() {
207
207
  cls(0)
208
+
208
209
  // now the japan variable holds a image
209
210
  image(W/2 - japan.width/2, H/2 - japan.height/2, japan)
210
211
  }
@@ -212,7 +213,7 @@ function draw() {
212
213
 
213
214
  It's very useful when you need to draw something the same way every time. This way, you create an image of that drawing, working as a kind of cache.
214
215
 
215
- You can also use the image() function to draw PNG/JPG images, but you'll need to load them first:
216
+ You can also use the `image()` function to draw PNG/JPG images, but you'll need to load them first:
216
217
 
217
218
  ```js
218
219
  litecanvas()
package/dist/dist.dev.js CHANGED
@@ -32,7 +32,7 @@
32
32
  };
33
33
 
34
34
  // src/version.js
35
- var version = "0.98.4";
35
+ var version = "0.99.0";
36
36
 
37
37
  // src/index.js
38
38
  function litecanvas(settings = {}) {
@@ -50,7 +50,7 @@
50
50
  keyboardEvents: true
51
51
  };
52
52
  settings = Object.assign(defaults, settings);
53
- let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _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 = {};
53
+ let _initialized = false, _paused = true, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _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 = {};
54
54
  const instance = {
55
55
  /** @type {number} */
56
56
  W: 0,
@@ -995,7 +995,7 @@
995
995
  _fpsInterval = 1e3 / ~~value;
996
996
  },
997
997
  /**
998
- * Returns information about that engine instance.
998
+ * Returns information about the engine instance.
999
999
  *
1000
1000
  * @param {number|string} index
1001
1001
  * @returns {any}
@@ -1028,43 +1028,32 @@
1028
1028
  _rngSeed,
1029
1029
  // 10
1030
1030
  _fontSize,
1031
- // 11
1032
- _fontFamily
1031
+ // 11
1032
+ _fontFamily,
1033
+ // 12
1034
+ _colorPaletteState
1033
1035
  ];
1034
1036
  const data = { index, value: internals[index] };
1035
1037
  instance.emit("stat", data);
1036
1038
  return data.value;
1037
1039
  },
1038
- /**
1039
- * Stops the litecanvas instance and remove all event listeners.
1040
- */
1041
- quit() {
1042
- instance.pause();
1043
- instance.emit("quit");
1044
- _eventListeners = {};
1045
- for (const removeListener of _browserEventListeners) {
1046
- removeListener();
1047
- }
1048
- if (settings.global) {
1049
- for (const key in instance) {
1050
- delete root[key];
1051
- }
1052
- delete root.ENGINE;
1053
- }
1054
- _initialized = false;
1055
- },
1056
1040
  /**
1057
1041
  * Pauses the engine loop (update & draw).
1058
1042
  */
1059
1043
  pause() {
1044
+ _paused = true;
1060
1045
  cancelAnimationFrame(_rafid);
1061
- _rafid = 0;
1062
1046
  },
1063
1047
  /**
1064
1048
  * Resumes (if paused) the engine loop.
1065
1049
  */
1066
1050
  resume() {
1067
- if (_initialized && !_rafid) {
1051
+ DEV: assert(
1052
+ _initialized,
1053
+ '[litecanvas] resume() cannot be called before the "init" event and neither after the quit() function'
1054
+ );
1055
+ if (_initialized && _paused) {
1056
+ _paused = false;
1068
1057
  _accumulated = _fpsInterval;
1069
1058
  _lastFrameTime = Date.now();
1070
1059
  _rafid = raf(drawFrame);
@@ -1076,7 +1065,26 @@
1076
1065
  * @returns {boolean}
1077
1066
  */
1078
1067
  paused() {
1079
- return !_rafid;
1068
+ return _paused;
1069
+ },
1070
+ /**
1071
+ * Shutdown the litecanvas instance and remove all event listeners.
1072
+ */
1073
+ quit() {
1074
+ instance.emit("quit");
1075
+ instance.pause();
1076
+ _initialized = false;
1077
+ _eventListeners = {};
1078
+ for (const removeListener of _browserEventListeners) {
1079
+ removeListener();
1080
+ }
1081
+ if (settings.global) {
1082
+ for (const key in instance) {
1083
+ delete root[key];
1084
+ }
1085
+ delete root.ENGINE;
1086
+ }
1087
+ DEV: console.warn("[litecanvas] quit() terminated a Litecanvas instance.");
1080
1088
  }
1081
1089
  };
1082
1090
  for (const k of _mathFunctions.split(",")) {
@@ -1428,7 +1436,7 @@
1428
1436
  if ("loading" === document.readyState) {
1429
1437
  on(root, "DOMContentLoaded", () => raf(init));
1430
1438
  } else {
1431
- raf(init);
1439
+ _rafid = raf(init);
1432
1440
  }
1433
1441
  return instance;
1434
1442
  }
package/dist/dist.js CHANGED
@@ -42,7 +42,7 @@
42
42
  keyboardEvents: true
43
43
  };
44
44
  settings = Object.assign(defaults, settings);
45
- let _initialized = false, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _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 = {};
45
+ let _initialized = false, _paused = true, _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colorPalette = defaultPalette, _colorPaletteState = [], _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 = {};
46
46
  const instance = {
47
47
  /** @type {number} */
48
48
  W: 0,
@@ -687,7 +687,7 @@
687
687
  _fpsInterval = 1e3 / ~~value;
688
688
  },
689
689
  /**
690
- * Returns information about that engine instance.
690
+ * Returns information about the engine instance.
691
691
  *
692
692
  * @param {number|string} index
693
693
  * @returns {any}
@@ -716,43 +716,28 @@
716
716
  _rngSeed,
717
717
  // 10
718
718
  _fontSize,
719
- // 11
720
- _fontFamily
719
+ // 11
720
+ _fontFamily,
721
+ // 12
722
+ _colorPaletteState
721
723
  ];
722
724
  const data = { index, value: internals[index] };
723
725
  instance.emit("stat", data);
724
726
  return data.value;
725
727
  },
726
- /**
727
- * Stops the litecanvas instance and remove all event listeners.
728
- */
729
- quit() {
730
- instance.pause();
731
- instance.emit("quit");
732
- _eventListeners = {};
733
- for (const removeListener of _browserEventListeners) {
734
- removeListener();
735
- }
736
- if (settings.global) {
737
- for (const key in instance) {
738
- delete root[key];
739
- }
740
- delete root.ENGINE;
741
- }
742
- _initialized = false;
743
- },
744
728
  /**
745
729
  * Pauses the engine loop (update & draw).
746
730
  */
747
731
  pause() {
732
+ _paused = true;
748
733
  cancelAnimationFrame(_rafid);
749
- _rafid = 0;
750
734
  },
751
735
  /**
752
736
  * Resumes (if paused) the engine loop.
753
737
  */
754
738
  resume() {
755
- if (_initialized && !_rafid) {
739
+ if (_initialized && _paused) {
740
+ _paused = false;
756
741
  _accumulated = _fpsInterval;
757
742
  _lastFrameTime = Date.now();
758
743
  _rafid = raf(drawFrame);
@@ -764,7 +749,25 @@
764
749
  * @returns {boolean}
765
750
  */
766
751
  paused() {
767
- return !_rafid;
752
+ return _paused;
753
+ },
754
+ /**
755
+ * Shutdown the litecanvas instance and remove all event listeners.
756
+ */
757
+ quit() {
758
+ instance.emit("quit");
759
+ instance.pause();
760
+ _initialized = false;
761
+ _eventListeners = {};
762
+ for (const removeListener of _browserEventListeners) {
763
+ removeListener();
764
+ }
765
+ if (settings.global) {
766
+ for (const key in instance) {
767
+ delete root[key];
768
+ }
769
+ delete root.ENGINE;
770
+ }
768
771
  }
769
772
  };
770
773
  for (const k of _mathFunctions.split(",")) {
@@ -1076,7 +1079,7 @@
1076
1079
  if ("loading" === document.readyState) {
1077
1080
  on(root, "DOMContentLoaded", () => raf(init));
1078
1081
  } else {
1079
- raf(init);
1082
+ _rafid = raf(init);
1080
1083
  }
1081
1084
  return instance;
1082
1085
  }
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,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=>e.toLowerCase(),c=e=>e.preventDefault(),f=e=>e.beginPath(),d=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,c=1,f=0,d=0,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=f*=500*z/44100/44100,I=n*=(1-l+2*l*E.random(l=[]))*z/44100,D=0,S=0,A=0,M=1,C=0,L=0,N=0,P=k<0?-1:1,F=z*P*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+P*q)/2/O,G=-(P+q)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,h=44100*h|0,a*=.3*e.zzfxV,P=i+y+o+r+w|0;A<P;l[A++]=N*a)++L%(100*v|0)||(N=s?1<s?2<s?3<s?B(D*D):E.max(E.min(E.tan(D),1),-1):1-(2*D/z%2+2)%2:1-4*E.abs(E.round(D/z)-D/z):B(D),N=(h?1-b+b*B(z*A/h):1)*(N<0?-1:1)*E.abs(N)**c*(A<i?A/i:A<i+y?1-(A-i)/y*(1-x):A<i+y+o?x:A<P-w?(P-A-w)/r*x:0),N=w?N/2+(w>A?0:(A<P-w?1:(P-A)/w)*l[A-w|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),D+=(F=(n+=f+=d)*E.cos(g*S++))+F*m*B(A**5),M&&++M>p&&(n+=u,I+=u,M=0),!h||++C%h||(n=I,f=T,M=M||1);(a=t.createBuffer(1,P,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,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let u=!1,p,h=1,m,g=.5,v=1,w,x=1e3/60,y,b,k="sans-serif",E=20,z=Date.now(),T=e,I=[],D=[.5,0,1750,,,.3,1,,,,600,.1],S={},A={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?A.clamp(o,l,n):o},norm:(e,t,a)=>A.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)=>(z=(1664525*z+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(A.rand(e,t+1)),rseed(e){z=~~e},cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):A.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~l+2*g,i),A.stroke(n)},rectfill(e,t,a,l,n,i){f(m),m[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),A.fill(n)},circ(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.stroke(l)},circfill(e,t,a,l){f(m),m.arc(~~e,~~t,~~a,0,n),A.fill(l)},oval(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.stroke(i)},ovalfill(e,t,a,l,i){f(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,n),A.fill(i)},line(e,t,a,l,n){f(m);let i=.5*(0!==g&&~~e==~~a),o=.5*(0!==g&&~~t==~~l);m.moveTo(~~e+i,~~t+o),m.lineTo(~~a+i,~~l+o),A.stroke(n)},linewidth(e){m.lineWidth=~~e,g=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,l=3,n="normal"){m.font=`${n} ${E}px ${k}`,m.fillStyle=P(l),m.fillText(a,~~e,~~t)},textfont(e){k=e},textsize(e){E=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},spr(e,t,a,l,n){let i=n.replace(/\s/g,"");for(let n=0;n<a;n++)for(let o=0;o<l;o++){let l=i[a*o+n]||".";"."!==l&&A.rectfill(e+n,t+o,1,1,parseInt(l,36)||0)}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=m;return n.width=e*i,n.height=t*i,(m=n.getContext("2d")).scale(i,i),a(m),m=o,n.transferToImageBitmap()},ctx:e=>(e&&(m=e),m),push(){m.save()},pop(){m.restore()},translate(e,t){m.translate(~~e,~~t)},scale(e,t){m.scale(e,t||e)},rotate(e){m.rotate(e)},alpha(e){m.globalAlpha=A.clamp(e,0,1)},fill(e){m.fillStyle=P(e),m.fill()},stroke(e){m.strokeStyle=P(e),m.stroke()},clip(e){f(m),e(m),m.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||D,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>p,use(e,t={}){var a=e,l=t;let n=a(A,l);for(let e in n)A.def(e,n[e])},listen:(e,t)=>(S[e=s(e)]=S[e]||new Set,S[e].add(t),()=>S&&S[e].delete(t)),emit(e,t,a,l,n){u&&(N("before:"+(e=s(e)),t,a,l,n),N(e,t,a,l,n),N("after:"+e,t,a,l,n))},pal(t=e){T=t,I=[]},palc(e,t){null==e?I=[]:I[e]=t},def(e,l){A[e]=l,t.global&&(a[e]=l)},timescale(e){v=e},framerate(e){x=1e3/~~e},stat(e){let l={index:e,value:[t,u,x/1e3,h,S,T,D,v,a.zzfxV,z,E,k][e]};return A.emit("stat",l),l.value},quit(){for(let e of(A.pause(),A.emit("quit"),S={},o))e();if(t.global){for(let e in A)delete a[e];delete a.ENGINE}u=!1},pause(){cancelAnimationFrame(b),b=0},resume(){u&&!b&&(y=x,w=Date.now(),b=i(C))},paused:()=>!b};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))A[e]=l[e];function M(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&A.listen(t,e[t]);if(t.autoscale&&r(a,"resize",L),t.tapEvents){let e=e=>[(e.pageX-p.offsetLeft)/h,(e.pageY-p.offsetTop)/h],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:Date.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&&Date.now()-e.t<=300,o=!1;r(p,"mousedown",t=>{if(0===t.button){c(t);let[a,n]=e(t);A.emit("tap",a,n,0),l(0,a,n),o=!0}}),r(p,"mouseup",a=>{if(0===a.button){c(a);let l=t.get(0),[n,r]=e(a);i(l)&&A.emit("tapped",l.xi,l.yi,0),A.emit("untap",n,r,0),t.delete(0),o=!1}}),r(a,"mousemove",t=>{c(t);let[a,l]=e(t);A.def("MX",a),A.def("MY",l),o&&(A.emit("tapping",a,l,0),n(0,a,l))}),r(p,"touchstart",t=>{for(let a of(c(t),t.changedTouches)){let[t,n]=e(a);A.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(p,"touchmove",t=>{for(let a of(c(t),t.changedTouches)){let[t,l]=e(a);A.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{c(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)&&A.emit("tapped",l.xi,l.yi,e),A.emit("untap",l.x,l.y,e),t.delete(e))};r(p,"touchend",s),r(p,"touchcancel",s),r(a,"blur",()=>{for(let[e,a]of(o=!1,t))A.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=s(t))?e.has("space"===t?" ":t):e.size>0,n="";r(a,"keydown",a=>{let l=s(a.key);e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),r(a,"keyup",t=>{e.delete(s(t.key))}),r(a,"blur",()=>e.clear()),A.listen("after:update",()=>t.clear()),A.def("iskeydown",t=>l(e,t)),A.def("iskeypressed",e=>l(t,e)),A.def("lastkey",()=>n)}u=!0,A.emit("init",A),A.resume()}function C(){b=i(C);let e=Date.now(),t=0,a=e-w;for(w=e,y+=a<100?a:x;y>=x;){t++,y-=x;let e=x/1e3*v;A.emit("update",e,t),A.def("T",A.T+e)}t&&(A.emit("draw",m),t>1&&(y=0))}function L(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(A.def("W",e),A.def("H",a),p.width=e,p.height=a,t.autoscale){let n=+t.autoscale;p.style.display||(p.style.display="block",p.style.margin="auto"),h=l.min(innerWidth/e,innerHeight/a),h=n>1&&h>n?n:h,p.style.width=e*h+"px",p.style.height=a*h+"px"}m.imageSmoothingEnabled=!1,A.textalign("start","top"),A.emit("resized",h)}function N(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function P(e){return T[~~(I[e]??e)%T.length]}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,A),a.ENGINE=A}return m=(p=(p="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(p,"click",()=>focus()),L(),p.parentNode||document.body.appendChild(p),p.style.imageRendering="pixelated",p.oncontextmenu=()=>!1,"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(M)):i(M),A}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,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=>e.toLowerCase(),c=e=>e.preventDefault(),f=e=>e.beginPath(),d=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,n=220,i=0,o=0,r=.1,s=0,c=1,f=0,d=0,u=0,p=0,h=0,m=0,g=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=f*=500*z/44100/44100,I=n*=(1-l+2*l*E.random(l=[]))*z/44100,D=0,S=0,A=0,M=1,C=0,L=0,N=0,P=k<0?-1:1,F=z*P*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+P*q)/2/O,G=-(P+q)/O,X=0,Y=0,$=0,j=0;for(i=44100*i+9,y*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,h=44100*h|0,a*=.3*e.zzfxV,P=i+y+o+r+w|0;A<P;l[A++]=N*a)++L%(100*v|0)||(N=s?1<s?2<s?3<s?B(D*D):E.max(E.min(E.tan(D),1),-1):1-(2*D/z%2+2)%2:1-4*E.abs(E.round(D/z)-D/z):B(D),N=(h?1-b+b*B(z*A/h):1)*(N<0?-1:1)*E.abs(N)**c*(A<i?A/i:A<i+y?1-(A-i)/y*(1-x):A<i+y+o?x:A<P-w?(P-A-w)/r*x:0),N=w?N/2+(w>A?0:(A<P-w?1:(P-A)/w)*l[A-w|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),D+=(F=(n+=f+=d)*E.cos(g*S++))+F*m*B(A**5),M&&++M>p&&(n+=u,I+=u,M=0),!h||++C%h||(n=I,f=T,M=M||1);(a=t.createBuffer(1,P,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,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t);let u=!1,p=!0,h,m=1,g,v=.5,w=1,x,y=1e3/60,b,k,E="sans-serif",z=20,T=Date.now(),I=e,D=[],S=[.5,0,1750,,,.3,1,,,,600,.1],A={},M={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?M.clamp(o,l,n):o},norm:(e,t,a)=>M.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)=>(T=(1664525*T+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(M.rand(e,t+1)),rseed(e){T=~~e},cls(e){null==e?g.clearRect(0,0,g.canvas.width,g.canvas.height):M.rectfill(0,0,g.canvas.width,g.canvas.height,e)},rect(e,t,a,l,n,i){f(g),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),M.stroke(n)},rectfill(e,t,a,l,n,i){f(g),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),M.fill(n)},circ(e,t,a,l){f(g),g.arc(~~e,~~t,~~a,0,n),M.stroke(l)},circfill(e,t,a,l){f(g),g.arc(~~e,~~t,~~a,0,n),M.fill(l)},oval(e,t,a,l,i){f(g),g.ellipse(~~e,~~t,~~a,~~l,0,0,n),M.stroke(i)},ovalfill(e,t,a,l,i){f(g),g.ellipse(~~e,~~t,~~a,~~l,0,0,n),M.fill(i)},line(e,t,a,l,n){f(g);let i=.5*(0!==v&&~~e==~~a),o=.5*(0!==v&&~~t==~~l);g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),M.stroke(n)},linewidth(e){g.lineWidth=~~e,v=.5*(0!=~~e%2)},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=3,n="normal"){g.font=`${n} ${z}px ${E}`,g.fillStyle=F(l),g.fillText(a,~~e,~~t)},textfont(e){E=e},textsize(e){z=e},textalign(e,t){e&&(g.textAlign=e),t&&(g.textBaseline=t)},image(e,t,a){g.drawImage(a,~~e,~~t)},spr(e,t,a,l,n){let i=n.replace(/\s/g,"");for(let n=0;n<a;n++)for(let o=0;o<l;o++){let l=i[a*o+n]||".";"."!==l&&M.rectfill(e+n,t+o,1,1,parseInt(l,36)||0)}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(){g.save()},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t){g.scale(e,t||e)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=M.clamp(e,0,1)},fill(e){g.fillStyle=F(e),g.fill()},stroke(e){g.strokeStyle=F(e),g.stroke()},clip(e){f(g),e(g),g.clip()},sfx:(e,t=0,l=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||S,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>h,use(e,t={}){var a=e,l=t;let n=a(M,l);for(let e in n)M.def(e,n[e])},listen:(e,t)=>(A[e=s(e)]=A[e]||new Set,A[e].add(t),()=>A&&A[e].delete(t)),emit(e,t,a,l,n){u&&(P("before:"+(e=s(e)),t,a,l,n),P(e,t,a,l,n),P("after:"+e,t,a,l,n))},pal(t=e){I=t,D=[]},palc(e,t){null==e?D=[]:D[e]=t},def(e,l){M[e]=l,t.global&&(a[e]=l)},timescale(e){w=e},framerate(e){y=1e3/~~e},stat(e){let l={index:e,value:[t,u,y/1e3,m,A,I,S,w,a.zzfxV,T,z,E,D][e]};return M.emit("stat",l),l.value},pause(){p=!0,cancelAnimationFrame(k)},resume(){u&&p&&(p=!1,b=y,x=Date.now(),k=i(L))},paused:()=>p,quit(){for(let e of(M.emit("quit"),M.pause(),u=!1,A={},o))e();if(t.global){for(let e in M)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(","))M[e]=l[e];function C(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&M.listen(t,e[t]);if(t.autoscale&&r(a,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,l=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:Date.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&&Date.now()-e.t<=300,o=!1;r(h,"mousedown",t=>{if(0===t.button){c(t);let[a,n]=e(t);M.emit("tap",a,n,0),l(0,a,n),o=!0}}),r(h,"mouseup",a=>{if(0===a.button){c(a);let l=t.get(0),[n,r]=e(a);i(l)&&M.emit("tapped",l.xi,l.yi,0),M.emit("untap",n,r,0),t.delete(0),o=!1}}),r(a,"mousemove",t=>{c(t);let[a,l]=e(t);M.def("MX",a),M.def("MY",l),o&&(M.emit("tapping",a,l,0),n(0,a,l))}),r(h,"touchstart",t=>{for(let a of(c(t),t.changedTouches)){let[t,n]=e(a);M.emit("tap",t,n,a.identifier+1),l(a.identifier+1,t,n)}}),r(h,"touchmove",t=>{for(let a of(c(t),t.changedTouches)){let[t,l]=e(a);M.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{c(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)&&M.emit("tapped",l.xi,l.yi,e),M.emit("untap",l.x,l.y,e),t.delete(e))};r(h,"touchend",s),r(h,"touchcancel",s),r(a,"blur",()=>{for(let[e,a]of(o=!1,t))M.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=s(t))?e.has("space"===t?" ":t):e.size>0,n="";r(a,"keydown",a=>{let l=s(a.key);e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),r(a,"keyup",t=>{e.delete(s(t.key))}),r(a,"blur",()=>e.clear()),M.listen("after:update",()=>t.clear()),M.def("iskeydown",t=>l(e,t)),M.def("iskeypressed",e=>l(t,e)),M.def("lastkey",()=>n)}u=!0,M.emit("init",M),M.resume()}function L(){k=i(L);let e=Date.now(),t=0,a=e-x;for(x=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*w;M.emit("update",e,t),M.def("T",M.T+e)}t&&(M.emit("draw",g),t>1&&(b=0))}function N(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(M.def("W",e),M.def("H",a),h.width=e,h.height=a,t.autoscale){let n=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=l.min(innerWidth/e,innerHeight/a),m=n>1&&m>n?n:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,M.textalign("start","top"),M.emit("resized",m)}function P(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function F(e){return I[~~(D[e]??e)%I.length]}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,M),a.ENGINE=M}return g=(h=(h="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(h,"click",()=>focus()),N(),h.parentNode||document.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(C)):k=i(C),M}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.98.4",
3
+ "version": "0.99.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and p5.js/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
package/src/index.js CHANGED
@@ -48,6 +48,8 @@ export default function litecanvas(settings = {}) {
48
48
 
49
49
  let /** @type {boolean} */
50
50
  _initialized = false,
51
+ /** @type {boolean} */
52
+ _paused = true,
51
53
  /** @type {HTMLCanvasElement} _canvas */
52
54
  _canvas,
53
55
  /** @type {number} */
@@ -1056,6 +1058,7 @@ export default function litecanvas(settings = {}) {
1056
1058
  'string' === typeof eventName,
1057
1059
  '[litecanvas] emit() 1st param must be a string'
1058
1060
  )
1061
+
1059
1062
  if (_initialized) {
1060
1063
  eventName = lowerCase(eventName)
1061
1064
 
@@ -1156,7 +1159,7 @@ export default function litecanvas(settings = {}) {
1156
1159
  },
1157
1160
 
1158
1161
  /**
1159
- * Returns information about that engine instance.
1162
+ * Returns information about the engine instance.
1160
1163
  *
1161
1164
  * @param {number|string} index
1162
1165
  * @returns {any}
@@ -1190,8 +1193,10 @@ export default function litecanvas(settings = {}) {
1190
1193
  _rngSeed,
1191
1194
  // 10
1192
1195
  _fontSize,
1193
- // 11
1196
+ // 11
1194
1197
  _fontFamily,
1198
+ // 12
1199
+ _colorPaletteState,
1195
1200
  ]
1196
1201
 
1197
1202
  const data = { index, value: internals[index] }
@@ -1202,49 +1207,24 @@ export default function litecanvas(settings = {}) {
1202
1207
  return data.value
1203
1208
  },
1204
1209
 
1205
- /**
1206
- * Stops the litecanvas instance and remove all event listeners.
1207
- */
1208
- quit() {
1209
- // stop the game loop (update & draw)
1210
- instance.pause()
1211
-
1212
- // emit "quit" event to manual clean ups
1213
- instance.emit('quit')
1214
-
1215
- // clear all engine event listeners
1216
- _eventListeners = {}
1217
-
1218
- // clear all browser event listeners
1219
- for (const removeListener of _browserEventListeners) {
1220
- removeListener()
1221
- }
1222
-
1223
- // maybe clear global context
1224
- if (settings.global) {
1225
- for (const key in instance) {
1226
- delete root[key]
1227
- }
1228
- delete root.ENGINE
1229
- }
1230
-
1231
- // unset that flag
1232
- _initialized = false
1233
- },
1234
-
1235
1210
  /**
1236
1211
  * Pauses the engine loop (update & draw).
1237
1212
  */
1238
1213
  pause() {
1214
+ _paused = true
1239
1215
  cancelAnimationFrame(_rafid)
1240
- _rafid = 0
1241
1216
  },
1242
1217
 
1243
1218
  /**
1244
1219
  * Resumes (if paused) the engine loop.
1245
1220
  */
1246
1221
  resume() {
1247
- if (_initialized && !_rafid) {
1222
+ DEV: assert(
1223
+ _initialized,
1224
+ '[litecanvas] resume() cannot be called before the "init" event and neither after the quit() function'
1225
+ )
1226
+ if (_initialized && _paused) {
1227
+ _paused = false
1248
1228
  _accumulated = _fpsInterval
1249
1229
  _lastFrameTime = Date.now()
1250
1230
  _rafid = raf(drawFrame)
@@ -1257,7 +1237,39 @@ export default function litecanvas(settings = {}) {
1257
1237
  * @returns {boolean}
1258
1238
  */
1259
1239
  paused() {
1260
- return !_rafid
1240
+ return _paused
1241
+ },
1242
+
1243
+ /**
1244
+ * Shutdown the litecanvas instance and remove all event listeners.
1245
+ */
1246
+ quit() {
1247
+ // emit "quit" event to manual clean ups
1248
+ instance.emit('quit')
1249
+
1250
+ // stop the game loop (update & draw)
1251
+ instance.pause()
1252
+
1253
+ // deinitialize the engine
1254
+ _initialized = false
1255
+
1256
+ // clear all engine event listeners
1257
+ _eventListeners = {}
1258
+
1259
+ // clear all browser event listeners
1260
+ for (const removeListener of _browserEventListeners) {
1261
+ removeListener()
1262
+ }
1263
+
1264
+ // maybe clear global context
1265
+ if (settings.global) {
1266
+ for (const key in instance) {
1267
+ delete root[key]
1268
+ }
1269
+ delete root.ENGINE
1270
+ }
1271
+
1272
+ DEV: console.warn('[litecanvas] quit() terminated a Litecanvas instance.')
1261
1273
  },
1262
1274
  }
1263
1275
 
@@ -1713,7 +1725,7 @@ export default function litecanvas(settings = {}) {
1713
1725
  if ('loading' === document.readyState) {
1714
1726
  on(root, 'DOMContentLoaded', () => raf(init))
1715
1727
  } else {
1716
- raf(init)
1728
+ _rafid = raf(init)
1717
1729
  }
1718
1730
 
1719
1731
  return instance
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.98.4'
2
+ export const version = '0.99.0'
package/types/global.d.ts CHANGED
@@ -583,9 +583,9 @@ declare global {
583
583
  */
584
584
  function framerate(fps: number): void
585
585
  /**
586
- * Returns information about that engine instance.
586
+ * Returns information about the engine instance.
587
587
  *
588
- * - n = 0: the settings passed to that instance
588
+ * - n = 0: the settings passed to this instance
589
589
  * - n = 1: returns true if the "init" event has already been emitted
590
590
  * - n = 2: the current delta time (dt)
591
591
  * - n = 3: the current canvas element scale (not the context 2D scale)
@@ -597,15 +597,12 @@ declare global {
597
597
  * - n = 9: the current RNG state
598
598
  * - n = 10: the current font size
599
599
  * - n = 11: the current font family
600
+ * - n = 12: the current state of the color palette
600
601
  * - n = *any other value*: probably returns undefined
601
602
  *
602
- * @param n
603
+ * @param index
603
604
  */
604
- function stat(n: number): any
605
- /**
606
- * Shutdown the litecanvas instance and remove all event listeners.
607
- */
608
- function quit(): void
605
+ function stat(index: number | string): any
609
606
  /**
610
607
  * Pauses the engine loop (update & draw).
611
608
  */
@@ -618,4 +615,8 @@ declare global {
618
615
  * Returns `true` if the engine loop is paused.
619
616
  */
620
617
  function paused(): boolean
618
+ /**
619
+ * Shutdown the litecanvas instance and remove all event listeners.
620
+ */
621
+ function quit(): void
621
622
  }
package/types/types.d.ts CHANGED
@@ -573,9 +573,9 @@ type LitecanvasInstance = {
573
573
  */
574
574
  framerate(fps: number): void
575
575
  /**
576
- * Returns information about that engine instance.
576
+ * Returns information about the engine instance.
577
577
  *
578
- * - n = 0: the settings passed to that instance
578
+ * - n = 0: the settings passed to this instance
579
579
  * - n = 1: returns true if the "init" event has already been emitted
580
580
  * - n = 2: the current delta time (dt)
581
581
  * - n = 3: the current canvas element scale (not the context 2D scale)
@@ -587,15 +587,12 @@ type LitecanvasInstance = {
587
587
  * - n = 9: the current RNG state
588
588
  * - n = 10: the current font size
589
589
  * - n = 11: the current font family
590
+ * - n = 12: the current state of the color palette
590
591
  * - n = *any other value*: probably returns undefined
591
592
  *
592
- * @param n
593
+ * @param index
593
594
  */
594
- stat(n: number): any
595
- /**
596
- * Stops the litecanvas instance and remove all event listeners.
597
- */
598
- quit(): void
595
+ stat(index: number | string): any
599
596
  /**
600
597
  * Pauses the engine loop (update & draw).
601
598
  */
@@ -608,6 +605,10 @@ type LitecanvasInstance = {
608
605
  * Returns `true` if the engine loop is paused.
609
606
  */
610
607
  paused(): boolean
608
+ /**
609
+ * Shutdown the litecanvas instance and remove all event listeners.
610
+ */
611
+ quit(): void
611
612
  }
612
613
 
613
614
  type LitecanvasOptions = {