litecanvas 0.80.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.js CHANGED
@@ -45,39 +45,33 @@
45
45
  animate: true
46
46
  };
47
47
  settings = Object.assign(defaults, settings);
48
- let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rng_seed = Date.now(), _colors = defaultPalette, _events = {
49
- init: false,
50
- update: false,
51
- draw: false,
52
- resized: false,
53
- tap: false,
54
- untap: false,
55
- tapping: false,
56
- tapped: false
57
- }, _helpers = {
58
- settings: Object.assign({}, settings)
48
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rng_seed = Date.now(), _colors = defaultPalette, _default_sound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _events = {
49
+ init: null,
50
+ update: null,
51
+ draw: null,
52
+ resized: null,
53
+ tap: null,
54
+ untap: null,
55
+ tapping: null,
56
+ tapped: null
59
57
  };
60
58
  const instance = {
59
+ /** @type {HTMLCanvasElement} */
60
+ CANVAS: null,
61
61
  /** @type {number} */
62
- WIDTH: 0,
62
+ W: 0,
63
63
  /** @type {number} */
64
- HEIGHT: 0,
65
- /** @type {HTMLCanvasElement} */
66
- CANVAS: false,
64
+ H: 0,
67
65
  /** @type {number} */
68
- ELAPSED: 0,
66
+ T: 0,
69
67
  /** @type {number} */
70
- CENTERX: 0,
68
+ CX: 0,
71
69
  /** @type {number} */
72
- CENTERY: 0,
70
+ CY: 0,
73
71
  /** @type {number} */
74
- MOUSEX: -1,
72
+ MX: -1,
75
73
  /** @type {number} */
76
- MOUSEY: -1,
77
- /** @type {number[]} */
78
- DEFAULT_SFX: [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
79
- /** @type {string[]} */
80
- COLORS: _colors,
74
+ MY: -1,
81
75
  /** MATH API */
82
76
  /**
83
77
  * Twice the value of the mathematical constant PI (π).
@@ -194,6 +188,15 @@
194
188
  norm: (value, start, stop) => {
195
189
  return instance.map(value, start, stop, 0, 1);
196
190
  },
191
+ /**
192
+ * Interpolate between 2 values using a periodic function.
193
+ *
194
+ * @param {number} from - the lower bound
195
+ * @param {number} to - the higher bound
196
+ * @param {number} t - the amount
197
+ * @param {(n: number) => number} fn - the periodic function (which default to `Math.sin`)
198
+ */
199
+ wave: (from, to, t, fn = Math.sin) => from + (fn(t) + 1) / 2 * (to - from),
197
200
  /** RNG API */
198
201
  /**
199
202
  * Generates a pseudorandom float between min (inclusive) and max (exclusive)
@@ -221,14 +224,14 @@
221
224
  return math.floor(instance.rand(min, max + 1));
222
225
  },
223
226
  /**
224
- * If a value is passed, initializes the random number generator with an explicit seed value.
225
- * Otherwise, returns the current seed state.
227
+ * Initializes the random number generator with an explicit seed value.
228
+ *
229
+ * Note: The seed should be a integer number greater than or equal to zero.
226
230
  *
227
231
  * @param {number} value
228
- * @returns {number} the seed state
229
232
  */
230
- seed: (value) => {
231
- return null == value ? _rng_seed : _rng_seed = ~~value;
233
+ rseed(value) {
234
+ _rng_seed = ~~value;
232
235
  },
233
236
  /** BASIC GRAPHICS API */
234
237
  /**
@@ -368,7 +371,7 @@
368
371
  */
369
372
  text(x, y, message, color = 3, fontStyle = "normal") {
370
373
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`;
371
- _ctx.fillStyle = instance.getcolor(color);
374
+ _ctx.fillStyle = _colors[~~color % _colors.length];
372
375
  _ctx.fillText(message, ~~x, ~~y);
373
376
  },
374
377
  /**
@@ -390,8 +393,8 @@
390
393
  /**
391
394
  * Sets the alignment used when drawing texts
392
395
  *
393
- * @param {string} align the horizontal alignment. Possible values: "left", "right", "center", "start" or "end"
394
- * @param {string} baseline the vertical alignment. Possible values: "top", "bottom", "middle", "hanging" or "ideographic"
396
+ * @param {CanvasTextAlign} align the horizontal alignment. Possible values: "left", "right", "center", "start" or "end"
397
+ * @param {CanvasTextBaseline} baseline the vertical alignment. Possible values: "top", "bottom", "middle", "hanging" or "ideographic"
395
398
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textBaseline
396
399
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textAlign
397
400
  */
@@ -418,7 +421,7 @@
418
421
  * @param {string[]|drawCallback} drawing
419
422
  * @param {object} [options]
420
423
  * @param {number} [options.scale=1]
421
- * @param {OffscreenCanvas | HTMLCanvasElement} [options.canvas]
424
+ * @param {OffscreenCanvas} [options.canvas]
422
425
  * @returns {ImageBitmap}
423
426
  * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
424
427
  */
@@ -527,7 +530,7 @@
527
530
  * @param {Path2D} [path]
528
531
  */
529
532
  fill(color, path) {
530
- _ctx.fillStyle = instance.getcolor(color);
533
+ _ctx.fillStyle = _colors[~~color % _colors.length];
531
534
  if (path) {
532
535
  _ctx.fill(path);
533
536
  } else {
@@ -541,7 +544,7 @@
541
544
  * @param {Path2D} [path]
542
545
  */
543
546
  stroke(color, path) {
544
- _ctx.strokeStyle = instance.getcolor(color);
547
+ _ctx.strokeStyle = _colors[~~color % _colors.length];
545
548
  if (path) {
546
549
  _ctx.stroke(path);
547
550
  } else {
@@ -573,7 +576,7 @@
573
576
  if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
574
577
  return false;
575
578
  }
576
- zzfxParams = zzfxParams || instance.DEFAULT_SFX;
579
+ zzfxParams = zzfxParams || _default_sound;
577
580
  if (pitchSlide !== 0 || volumeFactor !== 1) {
578
581
  zzfxParams = zzfxParams.slice();
579
582
  zzfxParams[0] = volumeFactor * (zzfxParams[0] || 1);
@@ -635,26 +638,14 @@
635
638
  */
636
639
  pal(colors = defaultPalette) {
637
640
  _colors = colors;
638
- instance.setvar("COLORS", _colors);
639
- },
640
- /**
641
- * Get a color by index
642
- *
643
- * @param {number} [index=0] The color number
644
- * @returns {string} the color code
645
- */
646
- getcolor: (index) => {
647
- return _colors[~~index % _colors.length];
648
641
  },
649
642
  /**
650
- * Create or update a instance variable
643
+ * Define or update a instance property.
651
644
  *
652
645
  * @param {string} key
653
646
  * @param {*} value
654
647
  */
655
- setvar(key, value) {
656
- if (null == value) {
657
- }
648
+ def(key, value) {
658
649
  instance[key] = value;
659
650
  if (settings.global) {
660
651
  root[key] = value;
@@ -671,23 +662,67 @@
671
662
  _timeScale = value;
672
663
  },
673
664
  /**
674
- * Set the target FPS at runtime.
665
+ * Set the target FPS (frames per second).
675
666
  *
676
667
  * @param {number} value
677
668
  */
678
- setfps(value) {
669
+ framerate(value) {
679
670
  _deltaTime = 1 / ~~value;
680
671
  },
672
+ /**
673
+ * Returns information about that engine instance.
674
+ *
675
+ * n = 0: the settings passed to that instance
676
+ * n = 1: returns true if the "init" event has already been emitted
677
+ * n = 2: the current ID returned by last requestAnimationFrame
678
+ * n = 3: the current canvas element scale (not the context 2D scale)
679
+ * n = 4: the attached event callbacks
680
+ * n = 5: the current color palette
681
+ * n = 6: the default sound used by `sfx()`
682
+ * n = 7: the current time scale
683
+ * n = 8: the current volume used by ZzFX
684
+ * n = 9: the current RNG state
685
+ *
686
+ * n = any other value: returns undefined
687
+ *
688
+ * @param {number} n
689
+ * @returns {any}
690
+ */
691
+ stat(n) {
692
+ const list = [
693
+ // 0
694
+ settings,
695
+ // 1
696
+ _initialized,
697
+ // 2
698
+ _rafid,
699
+ // 3
700
+ _scale,
701
+ // 4
702
+ _events,
703
+ // 5
704
+ _colors,
705
+ // 6
706
+ _default_sound,
707
+ // 7
708
+ _timeScale,
709
+ // 8
710
+ root.zzfxV || 1,
711
+ // 9
712
+ _rng_seed
713
+ ];
714
+ return list[n];
715
+ },
681
716
  /**
682
717
  * Stops the litecanvas instance and remove all event listeners.
683
718
  */
684
719
  quit() {
685
720
  cancelAnimationFrame(_rafid);
686
721
  instance.emit("quit");
687
- _events = [];
688
722
  for (const removeListener of _browserEventListeners) {
689
723
  removeListener();
690
724
  }
725
+ _events = {};
691
726
  if (settings.global) {
692
727
  for (const key in instance) {
693
728
  delete root[key];
@@ -700,7 +735,6 @@
700
735
  instance[k] = math[k];
701
736
  }
702
737
  function init() {
703
- _initialized = true;
704
738
  const source = settings.loop ? settings.loop : root;
705
739
  for (const event in _events) {
706
740
  if (source[event]) instance.listen(event, source[event]);
@@ -732,55 +766,90 @@
732
766
  tap.y = y;
733
767
  }, _checkTapped = (tap) => tap && performance.now() - tap.ts <= 300, preventDefault = (ev) => ev.preventDefault();
734
768
  let _pressingMouse = false;
735
- on(_canvas, "mousedown", (ev) => {
736
- if (ev.button === 0) {
769
+ on(
770
+ _canvas,
771
+ "mousedown",
772
+ /**
773
+ * @param {MouseEvent} ev
774
+ */
775
+ (ev) => {
776
+ if (ev.button === 0) {
777
+ preventDefault(ev);
778
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
779
+ instance.emit("tap", x, y, 0);
780
+ _registerTap(0, x, y);
781
+ _pressingMouse = true;
782
+ }
783
+ }
784
+ );
785
+ on(
786
+ _canvas,
787
+ "mouseup",
788
+ /**
789
+ * @param {MouseEvent} ev
790
+ */
791
+ (ev) => {
792
+ if (ev.button === 0) {
793
+ preventDefault(ev);
794
+ const tap = _taps.get(0);
795
+ const [x, y] = _getXY(ev.pageX, ev.pageY);
796
+ if (_checkTapped(tap)) {
797
+ instance.emit("tapped", tap.startX, tap.startY, 0);
798
+ }
799
+ instance.emit("untap", x, y, 0);
800
+ _taps.delete(0);
801
+ _pressingMouse = false;
802
+ }
803
+ }
804
+ );
805
+ on(
806
+ _canvas,
807
+ "mousemove",
808
+ /**
809
+ * @param {MouseEvent} ev
810
+ */
811
+ (ev) => {
737
812
  preventDefault(ev);
738
813
  const [x, y] = _getXY(ev.pageX, ev.pageY);
739
- instance.emit("tap", x, y, 0);
740
- _registerTap(0, x, y);
741
- _pressingMouse = true;
814
+ instance.def("MX", x);
815
+ instance.def("MY", y);
816
+ if (!_pressingMouse) return;
817
+ instance.emit("tapping", x, y, 0);
818
+ _updateTap(0, x, y);
742
819
  }
743
- });
744
- on(_canvas, "mouseup", (ev) => {
745
- if (ev.button === 0) {
820
+ );
821
+ on(
822
+ _canvas,
823
+ "touchstart",
824
+ /**
825
+ * @param {TouchEvent} ev
826
+ */
827
+ (ev) => {
746
828
  preventDefault(ev);
747
- const tap = _taps.get(0);
748
- const [x, y] = _getXY(ev.pageX, ev.pageY);
749
- if (_checkTapped(tap)) {
750
- instance.emit("tapped", tap.startX, tap.startY, 0);
829
+ const touches = ev.changedTouches;
830
+ for (const touch of touches) {
831
+ const [x, y] = _getXY(touch.pageX, touch.pageY);
832
+ instance.emit("tap", x, y, touch.identifier + 1);
833
+ _registerTap(touch.identifier + 1, x, y);
751
834
  }
752
- instance.emit("untap", x, y, 0);
753
- _taps.delete(0);
754
- _pressingMouse = false;
755
- }
756
- });
757
- on(_canvas, "mousemove", (ev) => {
758
- preventDefault(ev);
759
- const [x, y] = _getXY(ev.pageX, ev.pageY);
760
- instance.setvar("MOUSEX", x);
761
- instance.setvar("MOUSEY", y);
762
- if (!_pressingMouse) return;
763
- instance.emit("tapping", x, y, 0);
764
- _updateTap(0, x, y);
765
- });
766
- on(_canvas, "touchstart", (ev) => {
767
- preventDefault(ev);
768
- const touches = ev.changedTouches;
769
- for (const touch of touches) {
770
- const [x, y] = _getXY(touch.pageX, touch.pageY);
771
- instance.emit("tap", x, y, touch.identifier + 1);
772
- _registerTap(touch.identifier + 1, x, y);
773
835
  }
774
- });
775
- on(_canvas, "touchmove", (ev) => {
776
- preventDefault(ev);
777
- const touches = ev.changedTouches;
778
- for (const touch of touches) {
779
- const [x, y] = _getXY(touch.pageX, touch.pageY);
780
- instance.emit("tapping", x, y, touch.identifier + 1);
781
- _updateTap(touch.identifier + 1, x, y);
836
+ );
837
+ on(
838
+ _canvas,
839
+ "touchmove",
840
+ /**
841
+ * @param {TouchEvent} ev
842
+ */
843
+ (ev) => {
844
+ preventDefault(ev);
845
+ const touches = ev.changedTouches;
846
+ for (const touch of touches) {
847
+ const [x, y] = _getXY(touch.pageX, touch.pageY);
848
+ instance.emit("tapping", x, y, touch.identifier + 1);
849
+ _updateTap(touch.identifier + 1, x, y);
850
+ }
782
851
  }
783
- });
852
+ );
784
853
  const _touchEndHandler = (ev) => {
785
854
  preventDefault(ev);
786
855
  const existing = [];
@@ -828,7 +897,7 @@
828
897
  });
829
898
  on(root, "blur", () => _keysDown.clear());
830
899
  instance.listen("after:draw", () => _keysPress.clear());
831
- instance.setvar(
900
+ instance.def(
832
901
  "iskeydown",
833
902
  /**
834
903
  * Checks if a which key is pressed (down) on the keyboard.
@@ -841,7 +910,7 @@
841
910
  return keyCheck(_keysDown, key);
842
911
  }
843
912
  );
844
- instance.setvar(
913
+ instance.def(
845
914
  "iskeypressed",
846
915
  /**
847
916
  * Checks if a which key just got pressed on the keyboard.
@@ -866,6 +935,7 @@
866
935
  }
867
936
  });
868
937
  }
938
+ _initialized = true;
869
939
  instance.emit("init", instance);
870
940
  _lastFrameTime = performance.now();
871
941
  _rafid = raf(drawFrame);
@@ -881,10 +951,7 @@
881
951
  _accumulated += frameTime;
882
952
  while (_accumulated >= _deltaTime) {
883
953
  instance.emit("update", _deltaTime * _timeScale);
884
- instance.setvar(
885
- "ELAPSED",
886
- instance.ELAPSED + _deltaTime * _timeScale
887
- );
954
+ instance.def("T", instance.T + _deltaTime * _timeScale);
888
955
  updated++;
889
956
  _accumulated -= _deltaTime;
890
957
  }
@@ -897,39 +964,45 @@
897
964
  }
898
965
  }
899
966
  function setupCanvas() {
900
- _canvas = settings.canvas || document.createElement("canvas");
901
- _canvas = "string" === typeof _canvas ? document.querySelector(_canvas) : _canvas;
902
- instance.setvar("CANVAS", _canvas);
967
+ if ("string" === typeof settings.canvas) {
968
+ _canvas = document.querySelector(settings.canvas);
969
+ } else {
970
+ _canvas = settings.canvas || document.createElement("canvas");
971
+ }
972
+ instance.def("CANVAS", _canvas);
903
973
  _ctx = _canvas.getContext("2d");
904
974
  on(_canvas, "click", () => root.focus());
905
975
  _canvas.style = "";
906
976
  resizeCanvas();
907
- if (!_canvas.parentNode) document.body.appendChild(_canvas);
977
+ if (!_canvas.parentNode) {
978
+ document.body.appendChild(_canvas);
979
+ }
908
980
  }
909
981
  function resizeCanvas() {
910
982
  const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
911
- instance.setvar("WIDTH", _canvas.width = width);
912
- instance.setvar("HEIGHT", _canvas.height = height);
913
- instance.setvar("CENTERX", instance.WIDTH / 2);
914
- instance.setvar("CENTERY", instance.HEIGHT / 2);
983
+ instance.def("W", _canvas.width = width);
984
+ instance.def("H", _canvas.height = height);
985
+ instance.def("CX", instance.W / 2);
986
+ instance.def("CY", instance.H / 2);
915
987
  if (settings.autoscale) {
916
988
  if (!_canvas.style.display) {
917
989
  _canvas.style.display = "block";
918
990
  _canvas.style.margin = "auto";
919
991
  }
920
992
  _scale = math.min(
921
- root.innerWidth / instance.WIDTH,
922
- root.innerHeight / instance.HEIGHT
993
+ root.innerWidth / instance.W,
994
+ root.innerHeight / instance.H
923
995
  );
924
996
  _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
925
- _canvas.style.width = instance.WIDTH * _scale + "px";
926
- _canvas.style.height = instance.HEIGHT * _scale + "px";
997
+ _canvas.style.width = instance.W * _scale + "px";
998
+ _canvas.style.height = instance.H * _scale + "px";
927
999
  }
928
1000
  if (!settings.antialias || settings.pixelart) {
929
1001
  _ctx.imageSmoothingEnabled = false;
930
1002
  _canvas.style.imageRendering = "pixelated";
931
1003
  }
932
1004
  instance.emit("resized", _scale);
1005
+ instance.cls(0);
933
1006
  if (!settings.animate) {
934
1007
  raf(drawFrame);
935
1008
  }
@@ -941,9 +1014,9 @@
941
1014
  }
942
1015
  }
943
1016
  function loadPlugin(callback, config) {
944
- const pluginData = callback(instance, _helpers, config);
1017
+ const pluginData = callback(instance, config);
945
1018
  for (const key in pluginData) {
946
- instance.setvar(key, pluginData[key]);
1019
+ instance.def(key, pluginData[key]);
947
1020
  }
948
1021
  }
949
1022
  if (settings.global) {
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=new AudioContext,t=(t=1,a=.05,n=220,i=0,l=0,r=.1,o=0,s=1,c=0,f=0,d=0,p=0,u=0,g=0,h=0,m=0,v=0,E=1,b=0,w=0,x=0)=>{let y=Math,T=2*y.PI,S=c*=500*T/44100/44100,I=n*=(1-a+2*a*y.random(a=[]))*T/44100,k=0,A=0,H=0,D=1,C=0,O=0,P=0,L=x<0?-1:1,X=T*L*x*2/44100,N=y.cos(X),Y=y.sin,z=Y(X)/4,F=1+z,R=-2*N/F,G=(1-z)/F,M=(1+L*N)/2/F,W=-(L+N)/F,B=0,U=0,q=0,V=0;for(i=44100*i+9,b*=44100,l*=44100,r*=44100,v*=44100,f*=500*T/85766121e6,h*=T/44100,d*=T/44100,p*=44100,u=44100*u|0,t*=.3*(globalThis.zzfxV||1),L=i+b+l+r+v|0;H<L;a[H++]=P*t)++O%(100*m|0)||(P=o?1<o?2<o?3<o?Y(k*k):y.max(y.min(y.tan(k),1),-1):1-(2*k/T%2+2)%2:1-4*y.abs(y.round(k/T)-k/T):Y(k),P=(u?1-w+w*Y(T*H/u):1)*(P<0?-1:1)*y.abs(P)**s*(H<i?H/i:H<i+b?1-(H-i)/b*(1-E):H<i+b+l?E:H<L-v?(L-H-v)/r*E:0),P=v?P/2+(v>H?0:(H<L-v?1:(L-H)/v)*a[H-v|0]/2/t):P,x&&(P=V=M*B+W*(B=U)+M*(U=P)-G*q-R*(q=V))),k+=(X=(n+=c+=f)*y.cos(h*A++))+X*g*Y(H**5),D&&++D>p&&(n+=d,I+=d,D=0),!u||++C%u||(n=I,c=S,D=D||1);(t=e.createBuffer(1,L,44100)).getChannelData(0).set(a),(n=e.createBufferSource()).buffer=t,n.connect(e.destination),n.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let n=globalThis,i=Math,l=2*i.PI,r=requestAnimationFrame,o=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let c=!1,f=[],d,p=1,u,g=.5,h=1,m,v=1/60,E=0,b,w="sans-serif",x=20,y=Date.now(),T=a,S={init:!1,update:!1,draw:!1,resized:!1,tap:!1,untap:!1,tapping:!1,tapped:!1},I={settings:Object.assign({},e)},k={WIDTH:0,HEIGHT:0,CANVAS:!1,ELAPSED:0,CENTERX:0,CENTERY:0,MOUSEX:-1,MOUSEY:-1,DEFAULT_SFX:[.5,0,1750,,,.3,1,,,,600,.1],COLORS:T,TWO_PI:l,HALF_PI:i.PI/2,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>i.PI/180*e,rad2deg:e=>180/i.PI*e,round:(e,t=0)=>{if(!t)return i.round(e);let a=10**t;return i.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,n,i,l){let r=(e-t)/(a-t)*(i-n)+n;return l?k.clamp(r,n,i):r},norm:(e,t,a)=>k.map(e,t,a,0,1),rand:(e=0,t=1)=>(y=(1664525*y+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>i.floor(k.rand(e,t+1)),seed:e=>null==e?y:y=~~e,cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):k.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~n+2*g,l),k.stroke(i)},rectfill(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e,~~t,~~a,~~n,l),k.fill(i)},circ(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),k.stroke(n)},circfill(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),k.fill(n)},line(e,t,a,n,i){u.beginPath();let l=.5*(0!==g&&~~e==~~a),r=.5*(0!==g&&~~t==~~n);u.moveTo(~~e+l,~~t+r),u.lineTo(~~a+l,~~n+r),k.stroke(i)},linewidth(e){u.lineWidth=~~e,g=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,n=3,i="normal"){u.font=`${i} ${x}px ${w}`,u.fillStyle=k.getcolor(n),u.fillText(a,~~e,~~t)},textfont(e){w=e},textsize(e){x=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,n={}){let i=n.canvas||new OffscreenCanvas(1,1),l=n.scale||1,r=u;if(i.width=e*l,i.height=t*l,(u=i.getContext("2d")).scale(l,l),a.push){let e=0,t=0;for(let n of(u.imageSmoothingEnabled=!1,a)){for(let a of n)" "!==a&&"."!==a&&k.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,i.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=k.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=k.getcolor(e),t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=k.getcolor(e),t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,a=0,i=1)=>!(n.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||k.DEFAULT_SFX,(0!==a||1!==i)&&((e=e.slice())[0]=i*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){n.zzfxV=e},use(e,t={}){c?O(e,t):f.push([e,t])},listen:(e,t)=>(S[e]=S[e]||new Set,S[e].add(t),()=>S[e].delete(t)),emit(e,t,a,n,i){c&&(C("before:"+e,t,a,n,i),C(e,t,a,n,i),C("after:"+e,t,a,n,i))},pal(e=a){T=e,k.setvar("COLORS",T)},getcolor:e=>T[~~e%T.length],setvar(t,a){k[t]=a,e.global&&(n[t]=a)},timescale(e){h=e},setfps(e){v=1/~~e},quit(){for(let e of(cancelAnimationFrame(b),k.emit("quit"),S=[],o))e();if(e.global){for(let e in k)delete n[e];delete n.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))k[e]=i[e];function A(){c=!0;let t=e.loop?e.loop:n;for(let e in S)t[e]&&k.listen(e,t[e]);for(let[e,t]of f)O(e,t);if(e.autoscale&&s(n,"resize",D),e.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,a=(e,a,n)=>{let i={x:a,y:n,startX:a,startY:n,ts:performance.now()};return t.set(e,i),i},i=(e,n,i)=>{let l=t.get(e)||a(e);l.x=n,l.y=i},l=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),o=!1;s(d,"mousedown",t=>{if(0===t.button){r(t);let[n,i]=e(t.pageX,t.pageY);k.emit("tap",n,i,0),a(0,n,i),o=!0}}),s(d,"mouseup",a=>{if(0===a.button){r(a);let n=t.get(0),[i,s]=e(a.pageX,a.pageY);l(n)&&k.emit("tapped",n.startX,n.startY,0),k.emit("untap",i,s,0),t.delete(0),o=!1}}),s(d,"mousemove",t=>{r(t);let[a,n]=e(t.pageX,t.pageY);k.setvar("MOUSEX",a),k.setvar("MOUSEY",n),o&&(k.emit("tapping",a,n,0),i(0,a,n))}),s(d,"touchstart",t=>{for(let n of(r(t),t.changedTouches)){let[t,i]=e(n.pageX,n.pageY);k.emit("tap",t,i,n.identifier+1),a(n.identifier+1,t,i)}}),s(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);k.emit("tapping",t,n,a.identifier+1),i(a.identifier+1,t,n)}});let c=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,n]of t)a.includes(e)||(l(n)&&k.emit("tapped",n.startX,n.startY,e),k.emit("untap",n.x,n.y,e),t.delete(e))};s(d,"touchend",c),s(d,"touchcancel",c),s(n,"blur",()=>{for(let[e,a]of(o=!1,t))k.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=e=>e.toLowerCase(),t=new Set,a=new Set,i=(t,a)=>a?t.has("space"===e(a)?" ":e(a)):t.size>0;s(n,"keydown",n=>{t.has(e(n.key))||(t.add(e(n.key)),a.add(e(n.key)))}),s(n,"keyup",a=>{t.delete(e(a.key))}),s(n,"blur",()=>t.clear()),k.listen("after:draw",()=>a.clear()),k.setvar("iskeydown",e=>i(t,e)),k.setvar("iskeypressed",e=>i(a,e))}e.pauseOnBlur&&(s(n,"blur",()=>{b=cancelAnimationFrame(b)}),s(n,"focus",()=>{b||(E=0,b=r(H))})),k.emit("init",k),m=performance.now(),b=r(H)}function H(t){let a=0,n=(t-m)/1e3;if(m=t,e.animate){if(b=r(H),n>.3)return;for(E+=n;E>=v;)k.emit("update",v*h),k.setvar("ELAPSED",k.ELAPSED+v*h),a++,E-=v}else a=1;a&&(k.textalign("start","top"),k.emit("draw"))}function D(){let t=e.width||n.innerWidth,a=e.height||e.width||n.innerHeight;k.setvar("WIDTH",d.width=t),k.setvar("HEIGHT",d.height=a),k.setvar("CENTERX",k.WIDTH/2),k.setvar("CENTERY",k.HEIGHT/2),e.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=i.min(n.innerWidth/k.WIDTH,n.innerHeight/k.HEIGHT),p=(e.pixelart?~~p:p)||1,d.style.width=k.WIDTH*p+"px",d.style.height=k.HEIGHT*p+"px"),(!e.antialias||e.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),k.emit("resized",p),e.animate||r(H)}function C(e,t,a,n,i){if(S[e])for(let l of S[e])l(t,a,n,i)}function O(e,t){let a=e(k,I,t);for(let e in a)k.setvar(e,a[e])}if(e.global){if(n.ENGINE)throw Error("two global litecanvas detected");Object.assign(n,k),n.ENGINE=k}return d="string"==typeof(d=e.canvas||document.createElement("canvas"))?document.querySelector(d):d,k.setvar("CANVAS",d),u=d.getContext("2d"),s(d,"click",()=>n.focus()),d.style="",D(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?s(n,"DOMContentLoaded",()=>r(A)):r(A),k}})();
1
+ (()=>{var e=new AudioContext,t=(t=1,a=.05,l=220,n=0,i=0,r=.1,o=0,s=1,f=0,c=0,d=0,u=0,p=0,h=0,g=0,m=0,b=0,v=1,w=0,x=0,y=0)=>{let k=Math,E=2*k.PI,T=f*=500*E/44100/44100,A=l*=(1-a+2*a*k.random(a=[]))*E/44100,C=0,z=0,I=0,P=1,S=0,X=0,Y=0,M=y<0?-1:1,N=E*M*y*2/44100,H=k.cos(N),W=k.sin,B=W(N)/4,D=1+B,F=-2*H/D,L=(1-B)/D,O=(1+M*H)/2/D,V=-(M+H)/D,q=0,R=0,G=0,$=0;for(n=44100*n+9,w*=44100,i*=44100,r*=44100,b*=44100,c*=500*E/85766121e6,g*=E/44100,d*=E/44100,u*=44100,p=44100*p|0,t*=.3*(globalThis.zzfxV||1),M=n+w+i+r+b|0;I<M;a[I++]=Y*t)++X%(100*m|0)||(Y=o?1<o?2<o?3<o?W(C*C):k.max(k.min(k.tan(C),1),-1):1-(2*C/E%2+2)%2:1-4*k.abs(k.round(C/E)-C/E):W(C),Y=(p?1-x+x*W(E*I/p):1)*(Y<0?-1:1)*k.abs(Y)**s*(I<n?I/n:I<n+w?1-(I-n)/w*(1-v):I<n+w+i?v:I<M-b?(M-I-b)/r*v:0),Y=b?Y/2+(b>I?0:(I<M-b?1:(M-I)/b)*a[I-b|0]/2/t):Y,y&&(Y=$=O*q+V*(q=R)+O*(R=Y)-L*G-F*(G=$))),C+=(N=(l+=f+=c)*k.cos(g*z++))+N*h*W(I**5),P&&++P>u&&(l+=d,A+=d,P=0),!p||++S%p||(l=A,f=T,P=P||1);(t=e.createBuffer(1,M,44100)).getChannelData(0).set(a),(l=e.createBufferSource()).buffer=t,l.connect(e.destination),l.start()},a=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(e={}){let l=globalThis,n=Math,i=2*n.PI,r=requestAnimationFrame,o=[],s=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))};e=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,pauseOnBlur:!0,tapEvents:!0,keyboardEvents:!0,animate:!0},e);let f=!1,c=[],d,u=1,p,h=.5,g=1,m,b=1/60,v=0,w,x="sans-serif",y=20,k=Date.now(),E=a,T=[.5,0,1750,,,.3,1,,,,600,.1],A={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},C={CANVAS:null,W:0,H:0,T:0,CX:0,CY:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:n.PI/2,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let r=(e-t)/(a-t)*(n-l)+l;return i?C.clamp(r,l,n):r},norm:(e,t,a)=>C.map(e,t,a,0,1),wave:(e,t,a,l=Math.sin)=>e+(l(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(C.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?p.clearRect(0,0,p.canvas.width,p.canvas.height):C.rectfill(0,0,p.canvas.width,p.canvas.height,e)},rect(e,t,a,l,n,i){p.beginPath(),p[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~l+2*h,i),C.stroke(n)},rectfill(e,t,a,l,n,i){p.beginPath(),p[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),C.fill(n)},circ(e,t,a,l){p.beginPath(),p.arc(~~e,~~t,~~a,0,i),C.stroke(l)},circfill(e,t,a,l){p.beginPath(),p.arc(~~e,~~t,~~a,0,i),C.fill(l)},line(e,t,a,l,n){p.beginPath();let i=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~l);p.moveTo(~~e+i,~~t+r),p.lineTo(~~a+i,~~l+r),C.stroke(n)},linewidth(e){p.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){p.setLineDash(e),p.lineDashOffset=t},text(e,t,a,l=3,n="normal"){p.font=`${n} ${y}px ${x}`,p.fillStyle=E[~~l%E.length],p.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(p.textAlign=e),t&&(p.textBaseline=t)},image(e,t,a){p.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,r=p;if(n.width=e*i,n.height=t*i,(p=n.getContext("2d")).scale(i,i),a.push){let e=0,t=0;for(let l of(p.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&C.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(p);return p=r,n.transferToImageBitmap()},ctx:e=>(e&&(p=e),p),push:()=>p.save(),pop:()=>p.restore(),translate:(e,t)=>p.translate(~~e,~~t),scale:(e,t)=>p.scale(e,t||e),rotate:e=>p.rotate(e),alpha(e){p.globalAlpha=C.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){p.fillStyle=E[~~e%E.length],t?p.fill(t):p.fill()},stroke(e,t){p.strokeStyle=E[~~e%E.length],t?p.stroke(t):p.stroke()},clip(e){p.clip(e)},sfx:(e,a=0,n=1)=>!(l.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||T,(0!==a||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+a),t.apply(0,e),e),volume(e){l.zzfxV=e},use(e,t={}){f?X(e,t):c.push([e,t])},listen:(e,t)=>(A[e]=A[e]||new Set,A[e].add(t),()=>A[e].delete(t)),emit(e,t,a,l,n){f&&(S("before:"+e,t,a,l,n),S(e,t,a,l,n),S("after:"+e,t,a,l,n))},pal(e=a){E=e},def(t,a){C[t]=a,e.global&&(l[t]=a)},timescale(e){g=e},framerate(e){b=1/~~e},stat:t=>[e,f,w,u,A,E,T,g,l.zzfxV||1,k][t],quit(){for(let e of(cancelAnimationFrame(w),C.emit("quit"),o))e();if(A={},e.global){for(let e in C)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))C[e]=n[e];function z(){let t=e.loop?e.loop:l;for(let e in A)t[e]&&C.listen(e,t[e]);for(let[e,t]of c)X(e,t);if(e.autoscale&&s(l,"resize",P),e.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/u,(t-d.offsetTop)/u],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},i=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),o=!1;s(d,"mousedown",t=>{if(0===t.button){r(t);let[l,n]=e(t.pageX,t.pageY);C.emit("tap",l,n,0),a(0,l,n),o=!0}}),s(d,"mouseup",a=>{if(0===a.button){r(a);let l=t.get(0),[n,s]=e(a.pageX,a.pageY);i(l)&&C.emit("tapped",l.startX,l.startY,0),C.emit("untap",n,s,0),t.delete(0),o=!1}}),s(d,"mousemove",t=>{r(t);let[a,l]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",l),o&&(C.emit("tapping",a,l,0),n(0,a,l))}),s(d,"touchstart",t=>{for(let l of(r(t),t.changedTouches)){let[t,n]=e(l.pageX,l.pageY);C.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),s(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);C.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(i(l)&&C.emit("tapped",l.startX,l.startY,e),C.emit("untap",l.x,l.y,e),t.delete(e))};s(d,"touchend",f),s(d,"touchcancel",f),s(l,"blur",()=>{for(let[e,a]of(o=!1,t))C.emit("untap",a.x,a.y,e),t.delete(e)})}if(e.keyboardEvents){let e=e=>e.toLowerCase(),t=new Set,a=new Set,n=(t,a)=>a?t.has("space"===e(a)?" ":e(a)):t.size>0;s(l,"keydown",l=>{t.has(e(l.key))||(t.add(e(l.key)),a.add(e(l.key)))}),s(l,"keyup",a=>{t.delete(e(a.key))}),s(l,"blur",()=>t.clear()),C.listen("after:draw",()=>a.clear()),C.def("iskeydown",e=>n(t,e)),C.def("iskeypressed",e=>n(a,e))}e.pauseOnBlur&&(s(l,"blur",()=>{w=cancelAnimationFrame(w)}),s(l,"focus",()=>{w||(v=0,w=r(I))})),f=!0,C.emit("init",C),m=performance.now(),w=r(I)}function I(t){let a=0,l=(t-m)/1e3;if(m=t,e.animate){if(w=r(I),l>.3)return;for(v+=l;v>=b;)C.emit("update",b*g),C.def("T",C.T+b*g),a++,v-=b}else a=1;a&&(C.textalign("start","top"),C.emit("draw"))}function P(){let t=e.width||l.innerWidth,a=e.height||e.width||l.innerHeight;C.def("W",d.width=t),C.def("H",d.height=a),C.def("CX",C.W/2),C.def("CY",C.H/2),e.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),u=n.min(l.innerWidth/C.W,l.innerHeight/C.H),u=(e.pixelart?~~u:u)||1,d.style.width=C.W*u+"px",d.style.height=C.H*u+"px"),(!e.antialias||e.pixelart)&&(p.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),C.emit("resized",u),C.cls(0),e.animate||r(I)}function S(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function X(e,t){let a=e(C,t);for(let e in a)C.def(e,a[e])}if(e.global){if(l.ENGINE)throw Error("two global litecanvas detected");Object.assign(l,C),l.ENGINE=C}return d="string"==typeof e.canvas?document.querySelector(e.canvas):e.canvas||document.createElement("canvas"),C.def("CANVAS",d),p=d.getContext("2d"),s(d,"click",()=>l.focus()),d.style="",P(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?s(l,"DOMContentLoaded",()=>r(z)):r(z),C}})();
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.80.0",
3
+ "version": "0.81.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
7
7
  "contributors": [],
8
8
  "devDependencies": {
9
- "@swc/core": "^1.11.29",
10
- "ava": "^6.3.0",
9
+ "@swc/core": "^1.11.31",
10
+ "ava": "^6.4.0",
11
11
  "esbuild": "^0.25.5",
12
12
  "gzip-size": "^7.0.0",
13
13
  "prettier": "^3.5.3"