litecanvas 0.97.1 → 0.98.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.dev.js CHANGED
@@ -32,7 +32,7 @@
32
32
  };
33
33
 
34
34
  // src/version.js
35
- var version = "0.97.1";
35
+ var version = "0.98.1";
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, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _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, _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,
@@ -565,7 +565,7 @@
565
565
  "[litecanvas] text() 5th param must be a string"
566
566
  );
567
567
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`;
568
- _ctx.fillStyle = _colors[~~color % _colors.length];
568
+ _ctx.fillStyle = getColor(color);
569
569
  _ctx.fillText(message, ~~x, ~~y);
570
570
  },
571
571
  /**
@@ -772,7 +772,7 @@
772
772
  null == color || isNumber(color) && color >= 0,
773
773
  "[litecanvas] fill() 1st param must be a positive number or zero"
774
774
  );
775
- _ctx.fillStyle = _colors[~~color % _colors.length];
775
+ _ctx.fillStyle = getColor(color);
776
776
  _ctx.fill();
777
777
  },
778
778
  /**
@@ -785,7 +785,7 @@
785
785
  null == color || isNumber(color) && color >= 0,
786
786
  "[litecanvas] stroke() 1st param must be a positive number or zero"
787
787
  );
788
- _ctx.strokeStyle = _colors[~~color % _colors.length];
788
+ _ctx.strokeStyle = getColor(color);
789
789
  _ctx.stroke();
790
790
  },
791
791
  /**
@@ -868,11 +868,7 @@
868
868
  "object" === typeof config,
869
869
  "[litecanvas] use() 2nd param must be an object"
870
870
  );
871
- if (_initialized) {
872
- loadPlugin(callback, config);
873
- } else {
874
- _plugins.push([callback, config]);
875
- }
871
+ loadPlugin(callback, config);
876
872
  },
877
873
  /**
878
874
  * Add a game event listener
@@ -917,7 +913,7 @@
917
913
  }
918
914
  },
919
915
  /**
920
- * Set or reset the color palette.
916
+ * Set new palette colors or restore the default palette.
921
917
  *
922
918
  * @param {string[]} [colors]
923
919
  */
@@ -926,14 +922,16 @@
926
922
  Array.isArray(colors) && colors.length > 0,
927
923
  "[litecanvas] pal() 1st param must be a array of strings"
928
924
  );
929
- _colors = colors;
930
- _currentPalette = [...colors];
925
+ _colorPalette = colors;
926
+ _colorPaletteState = [];
931
927
  },
932
928
  /**
933
- * Swap two colors of the current palette.
929
+ * Replace the color "a" with color "b".
934
930
  *
935
931
  * If called without arguments, reset the current palette.
936
932
  *
933
+ * Note: `palc()` don't affect drawings made with `image()`.
934
+ *
937
935
  * @param {number?} a
938
936
  * @param {number?} b
939
937
  */
@@ -947,10 +945,9 @@
947
945
  "[litecanvas] palc() 2nd param must be a positive number"
948
946
  );
949
947
  if (a == null) {
950
- _colors = [..._currentPalette];
948
+ _colorPaletteState = [];
951
949
  } else {
952
- ;
953
- [_colors[a], _colors[b]] = [_colors[b], _colors[a]];
950
+ _colorPaletteState[a] = b;
954
951
  }
955
952
  },
956
953
  /**
@@ -1020,7 +1017,7 @@
1020
1017
  // 4
1021
1018
  _eventListeners,
1022
1019
  // 5
1023
- _colors,
1020
+ _colorPalette,
1024
1021
  // 6
1025
1022
  _defaultSound,
1026
1023
  // 7
@@ -1093,9 +1090,6 @@
1093
1090
  }
1094
1091
  if (source[event]) instance.listen(event, source[event]);
1095
1092
  }
1096
- for (const [callback, config] of _plugins) {
1097
- loadPlugin(callback, config);
1098
- }
1099
1093
  if (settings.autoscale) {
1100
1094
  on(root, "resize", resizeCanvas);
1101
1095
  }
@@ -1417,6 +1411,10 @@
1417
1411
  instance.def(key, pluginData[key]);
1418
1412
  }
1419
1413
  }
1414
+ function getColor(index) {
1415
+ const i = _colorPaletteState[index] ?? index;
1416
+ return _colorPalette[~~i % _colorPalette.length];
1417
+ }
1420
1418
  if (settings.global) {
1421
1419
  if (root.ENGINE) {
1422
1420
  throw new Error("only one global litecanvas is allowed");
@@ -1427,7 +1425,6 @@
1427
1425
  DEV: console.info(`[litecanvas] version ${version} started`);
1428
1426
  DEV: console.debug(`[litecanvas] litecanvas() options =`, settings);
1429
1427
  setupCanvas();
1430
- instance.pal();
1431
1428
  if ("loading" === document.readyState) {
1432
1429
  on(root, "DOMContentLoaded", () => raf(init));
1433
1430
  } else {
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, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _currentPalette, _colors, _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, _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,
@@ -373,7 +373,7 @@
373
373
  */
374
374
  text(x, y, message, color = 3, fontStyle = "normal") {
375
375
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`;
376
- _ctx.fillStyle = _colors[~~color % _colors.length];
376
+ _ctx.fillStyle = getColor(color);
377
377
  _ctx.fillText(message, ~~x, ~~y);
378
378
  },
379
379
  /**
@@ -528,7 +528,7 @@
528
528
  * @param {number} [color=0]
529
529
  */
530
530
  fill(color) {
531
- _ctx.fillStyle = _colors[~~color % _colors.length];
531
+ _ctx.fillStyle = getColor(color);
532
532
  _ctx.fill();
533
533
  },
534
534
  /**
@@ -537,7 +537,7 @@
537
537
  * @param {number} [color=0]
538
538
  */
539
539
  stroke(color) {
540
- _ctx.strokeStyle = _colors[~~color % _colors.length];
540
+ _ctx.strokeStyle = getColor(color);
541
541
  _ctx.stroke();
542
542
  },
543
543
  /**
@@ -598,11 +598,7 @@
598
598
  * @param {pluginCallback} callback
599
599
  */
600
600
  use(callback, config = {}) {
601
- if (_initialized) {
602
- loadPlugin(callback, config);
603
- } else {
604
- _plugins.push([callback, config]);
605
- }
601
+ loadPlugin(callback, config);
606
602
  },
607
603
  /**
608
604
  * Add a game event listener
@@ -635,28 +631,29 @@
635
631
  }
636
632
  },
637
633
  /**
638
- * Set or reset the color palette.
634
+ * Set new palette colors or restore the default palette.
639
635
  *
640
636
  * @param {string[]} [colors]
641
637
  */
642
638
  pal(colors = defaultPalette) {
643
- _colors = colors;
644
- _currentPalette = [...colors];
639
+ _colorPalette = colors;
640
+ _colorPaletteState = [];
645
641
  },
646
642
  /**
647
- * Swap two colors of the current palette.
643
+ * Replace the color "a" with color "b".
648
644
  *
649
645
  * If called without arguments, reset the current palette.
650
646
  *
647
+ * Note: `palc()` don't affect drawings made with `image()`.
648
+ *
651
649
  * @param {number?} a
652
650
  * @param {number?} b
653
651
  */
654
652
  palc(a, b) {
655
653
  if (a == null) {
656
- _colors = [..._currentPalette];
654
+ _colorPaletteState = [];
657
655
  } else {
658
- ;
659
- [_colors[a], _colors[b]] = [_colors[b], _colors[a]];
656
+ _colorPaletteState[a] = b;
660
657
  }
661
658
  },
662
659
  /**
@@ -708,7 +705,7 @@
708
705
  // 4
709
706
  _eventListeners,
710
707
  // 5
711
- _colors,
708
+ _colorPalette,
712
709
  // 6
713
710
  _defaultSound,
714
711
  // 7
@@ -778,9 +775,6 @@
778
775
  for (const event of _coreEvents.split(",")) {
779
776
  if (source[event]) instance.listen(event, source[event]);
780
777
  }
781
- for (const [callback, config] of _plugins) {
782
- loadPlugin(callback, config);
783
- }
784
778
  if (settings.autoscale) {
785
779
  on(root, "resize", resizeCanvas);
786
780
  }
@@ -1067,6 +1061,10 @@
1067
1061
  instance.def(key, pluginData[key]);
1068
1062
  }
1069
1063
  }
1064
+ function getColor(index) {
1065
+ const i = _colorPaletteState[index] ?? index;
1066
+ return _colorPalette[~~i % _colorPalette.length];
1067
+ }
1070
1068
  if (settings.global) {
1071
1069
  if (root.ENGINE) {
1072
1070
  throw new Error("only one global litecanvas is allowed");
@@ -1075,7 +1073,6 @@
1075
1073
  root.ENGINE = instance;
1076
1074
  }
1077
1075
  setupCanvas();
1078
- instance.pal();
1079
1076
  if ("loading" === document.readyState) {
1080
1077
  on(root, "DOMContentLoaded", () => raf(init));
1081
1078
  } else {
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,m=1,g,v=.5,w=1,x,y=1e3/60,b,k,E="sans-serif",z=20,T=Date.now(),I,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=D[~~l%D.length],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 a=0;a<l;a++){let o=i[l*a+n]||".";"."!==o&&M.rectfill(e+n,t+a,1,1,parseInt(o,16)||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=D[~~e%D.length],g.fill()},stroke(e){g.strokeStyle=D[~~e%D.length],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={}){u?F(e,t):p.push([e,t])},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){D=t,I=[...t]},palc(e,t){null==e?D=[...I]:[D[e],D[t]]=[D[t],D[e]]},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,D,S,w,a.zzfxV,T,z,E][e]};return M.emit("stat",l),l.value},quit(){for(let e of(M.pause(),M.emit("quit"),A={},o))e();if(t.global){for(let e in M)delete a[e];delete a.ENGINE}u=!1},pause(){cancelAnimationFrame(k),k=0},resume(){u&&!k&&(b=y,x=Date.now(),k=i(L))},paused:()=>!k};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]);for(let[e,t]of p)F(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,t){let a=e(M,t);for(let e in a)M.def(e,a[e])}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,M.pal(),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>i(C)):i(C),M}})();
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 a=0;a<l;a++){let o=i[l*a+n]||".";"."!==o&&A.rectfill(e+n,t+a,1,1,parseInt(o,16)||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}})();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.97.1",
4
- "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
3
+ "version": "0.98.1",
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>",
7
7
  "contributors": [],
package/src/index.js CHANGED
@@ -48,8 +48,6 @@ export default function litecanvas(settings = {}) {
48
48
 
49
49
  let /** @type {boolean} */
50
50
  _initialized = false,
51
- /** @type {any[]} */
52
- _plugins = [],
53
51
  /** @type {HTMLCanvasElement} _canvas */
54
52
  _canvas,
55
53
  /** @type {number} */
@@ -75,9 +73,9 @@ export default function litecanvas(settings = {}) {
75
73
  /** @type {number} */
76
74
  _rngSeed = Date.now(),
77
75
  /** @type {string[]} */
78
- _currentPalette,
79
- /** @type {string[]} */
80
- _colors,
76
+ _colorPalette = defaultPalette,
77
+ /** @type {number[]} */
78
+ _colorPaletteState = [],
81
79
  /** @type {number[]} */
82
80
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
83
81
  /** @type {string} */
@@ -661,7 +659,7 @@ export default function litecanvas(settings = {}) {
661
659
  )
662
660
 
663
661
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`
664
- _ctx.fillStyle = _colors[~~color % _colors.length]
662
+ _ctx.fillStyle = getColor(color)
665
663
  _ctx.fillText(message, ~~x, ~~y)
666
664
  },
667
665
 
@@ -901,7 +899,7 @@ export default function litecanvas(settings = {}) {
901
899
  '[litecanvas] fill() 1st param must be a positive number or zero'
902
900
  )
903
901
 
904
- _ctx.fillStyle = _colors[~~color % _colors.length]
902
+ _ctx.fillStyle = getColor(color)
905
903
  _ctx.fill()
906
904
  },
907
905
 
@@ -916,7 +914,7 @@ export default function litecanvas(settings = {}) {
916
914
  '[litecanvas] stroke() 1st param must be a positive number or zero'
917
915
  )
918
916
 
919
- _ctx.strokeStyle = _colors[~~color % _colors.length]
917
+ _ctx.strokeStyle = getColor(color)
920
918
  _ctx.stroke()
921
919
  },
922
920
 
@@ -1016,13 +1014,7 @@ export default function litecanvas(settings = {}) {
1016
1014
  '[litecanvas] use() 2nd param must be an object'
1017
1015
  )
1018
1016
 
1019
- if (_initialized) {
1020
- // load the plugin now
1021
- loadPlugin(callback, config)
1022
- } else {
1023
- // schedule to load the plugin right before the "init" event
1024
- _plugins.push([callback, config])
1025
- }
1017
+ loadPlugin(callback, config)
1026
1018
  },
1027
1019
 
1028
1020
  /**
@@ -1075,7 +1067,7 @@ export default function litecanvas(settings = {}) {
1075
1067
  },
1076
1068
 
1077
1069
  /**
1078
- * Set or reset the color palette.
1070
+ * Set new palette colors or restore the default palette.
1079
1071
  *
1080
1072
  * @param {string[]} [colors]
1081
1073
  */
@@ -1084,15 +1076,17 @@ export default function litecanvas(settings = {}) {
1084
1076
  Array.isArray(colors) && colors.length > 0,
1085
1077
  '[litecanvas] pal() 1st param must be a array of strings'
1086
1078
  )
1087
- _colors = colors
1088
- _currentPalette = [...colors]
1079
+ _colorPalette = colors
1080
+ _colorPaletteState = []
1089
1081
  },
1090
1082
 
1091
1083
  /**
1092
- * Swap two colors of the current palette.
1084
+ * Replace the color "a" with color "b".
1093
1085
  *
1094
1086
  * If called without arguments, reset the current palette.
1095
1087
  *
1088
+ * Note: `palc()` don't affect drawings made with `image()`.
1089
+ *
1096
1090
  * @param {number?} a
1097
1091
  * @param {number?} b
1098
1092
  */
@@ -1106,9 +1100,9 @@ export default function litecanvas(settings = {}) {
1106
1100
  '[litecanvas] palc() 2nd param must be a positive number'
1107
1101
  )
1108
1102
  if (a == null) {
1109
- _colors = [..._currentPalette]
1103
+ _colorPaletteState = []
1110
1104
  } else {
1111
- ;[_colors[a], _colors[b]] = [_colors[b], _colors[a]]
1105
+ _colorPaletteState[a] = b
1112
1106
  }
1113
1107
  },
1114
1108
 
@@ -1186,7 +1180,7 @@ export default function litecanvas(settings = {}) {
1186
1180
  // 4
1187
1181
  _eventListeners,
1188
1182
  // 5
1189
- _colors,
1183
+ _colorPalette,
1190
1184
  // 6
1191
1185
  _defaultSound,
1192
1186
  // 7
@@ -1284,11 +1278,6 @@ export default function litecanvas(settings = {}) {
1284
1278
  if (source[event]) instance.listen(event, source[event])
1285
1279
  }
1286
1280
 
1287
- // load plugins
1288
- for (const [callback, config] of _plugins) {
1289
- loadPlugin(callback, config)
1290
- }
1291
-
1292
1281
  // listen window resize event when "autoscale" is enabled
1293
1282
  if (settings.autoscale) {
1294
1283
  on(root, 'resize', resizeCanvas)
@@ -1700,6 +1689,15 @@ export default function litecanvas(settings = {}) {
1700
1689
  }
1701
1690
  }
1702
1691
 
1692
+ /**
1693
+ * @param {number} index
1694
+ * @returns {string}
1695
+ */
1696
+ function getColor(index) {
1697
+ const i = _colorPaletteState[index] ?? index
1698
+ return _colorPalette[~~i % _colorPalette.length]
1699
+ }
1700
+
1703
1701
  if (settings.global) {
1704
1702
  if (root.ENGINE) {
1705
1703
  throw new Error('only one global litecanvas is allowed')
@@ -1713,9 +1711,6 @@ export default function litecanvas(settings = {}) {
1713
1711
 
1714
1712
  setupCanvas()
1715
1713
 
1716
- // init the color palette
1717
- instance.pal()
1718
-
1719
1714
  if ('loading' === document.readyState) {
1720
1715
  on(root, 'DOMContentLoaded', () => raf(init))
1721
1716
  } else {
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.97.1'
2
+ export const version = '0.98.1'
package/types/global.d.ts CHANGED
@@ -545,13 +545,13 @@ declare global {
545
545
  */
546
546
  function emit(event: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any): void
547
547
  /**
548
- * Set or reset the color palette.
548
+ * Set new palette colors or restore the default palette.
549
549
  *
550
550
  * @param [colors]
551
551
  */
552
552
  function pal(colors?: string[]): void
553
553
  /**
554
- * Swap two colors of the current palette.
554
+ * Replace the color "a" with color "b".
555
555
  *
556
556
  * If called without arguments, reset the current palette.
557
557
  *
package/types/types.d.ts CHANGED
@@ -542,13 +542,13 @@ type LitecanvasInstance = {
542
542
  */
543
543
  def(key: string, value: any): void
544
544
  /**
545
- * Set or reset the color palette.
545
+ * Set new palette colors or restore the default palette.
546
546
  *
547
547
  * @param [colors]
548
548
  */
549
549
  pal(colors?: string[]): void
550
550
  /**
551
- * Swap two colors of the current palette.
551
+ * Replace the color "a" with color "b".
552
552
  *
553
553
  * If called without arguments, reset the current palette.
554
554
  *