litecanvas 0.87.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.dev.js CHANGED
@@ -32,14 +32,14 @@
32
32
  };
33
33
 
34
34
  // version.js
35
- var version = "0.87.0";
35
+ var version = "0.88.0";
36
36
 
37
37
  // src/index.js
38
38
  function litecanvas(settings = {}) {
39
39
  const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
40
40
  elem.addEventListener(evt, callback, false);
41
41
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
42
- }, isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
42
+ }, beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
43
43
  width: null,
44
44
  height: null,
45
45
  autoscale: true,
@@ -339,7 +339,7 @@
339
339
  null == radii || isNumber(radii) || Array.isArray(radii) && radii.length >= 1,
340
340
  "[litecanvas] rect() 6th param must be a number or array of numbers"
341
341
  );
342
- _ctx.beginPath();
342
+ beginPath(_ctx);
343
343
  _ctx[radii ? "roundRect" : "rect"](
344
344
  ~~x - _outline_fix,
345
345
  ~~y - _outline_fix,
@@ -378,7 +378,7 @@
378
378
  null == radii || isNumber(radii) || Array.isArray(radii) && radii.length >= 1,
379
379
  "[litecanvas] rectfill() 6th param must be a number or array of at least 2 numbers"
380
380
  );
381
- _ctx.beginPath();
381
+ beginPath(_ctx);
382
382
  _ctx[radii ? "roundRect" : "rect"](~~x, ~~y, ~~width, ~~height, radii);
383
383
  instance.fill(color);
384
384
  },
@@ -401,7 +401,7 @@
401
401
  null == color || isNumber(color) && color >= 0,
402
402
  "[litecanvas] circ() 4th param must be a positive number or zero"
403
403
  );
404
- _ctx.beginPath();
404
+ beginPath(_ctx);
405
405
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
406
406
  instance.stroke(color);
407
407
  },
@@ -424,7 +424,7 @@
424
424
  null == color || isNumber(color) && color >= 0,
425
425
  "[litecanvas] circfill() 4th param must be a positive number or zero"
426
426
  );
427
- _ctx.beginPath();
427
+ beginPath(_ctx);
428
428
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
429
429
  instance.fill(color);
430
430
  },
@@ -452,7 +452,7 @@
452
452
  null == color || isNumber(color) && color >= 0,
453
453
  "[litecanvas] oval() 5th param must be a positive number or zero"
454
454
  );
455
- _ctx.beginPath();
455
+ beginPath(_ctx);
456
456
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
457
457
  instance.stroke(color);
458
458
  },
@@ -480,7 +480,7 @@
480
480
  null == color || isNumber(color) && color >= 0,
481
481
  "[litecanvas] ovalfill() 5th param must be a positive number or zero"
482
482
  );
483
- _ctx.beginPath();
483
+ beginPath(_ctx);
484
484
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
485
485
  instance.fill(color);
486
486
  },
@@ -508,7 +508,7 @@
508
508
  null == color || isNumber(color) && color >= 0,
509
509
  "[litecanvas] line() 5th param must be a positive number or zero"
510
510
  );
511
- _ctx.beginPath();
511
+ beginPath(_ctx);
512
512
  let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0;
513
513
  let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0;
514
514
  _ctx.moveTo(~~x1 + xfix, ~~y1 + yfix);
@@ -752,79 +752,45 @@
752
752
  _ctx.globalAlpha = instance.clamp(value, 0, 1);
753
753
  },
754
754
  /**
755
- * Returns a newly instantiated Path2D object, optionally with another
756
- * path as an argument (creates a copy), or optionally with a string
757
- * consisting of SVG path data.
758
- *
759
- * @param {Path2D|string} [arg]
760
- * @returns Path2D
761
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
762
- */
763
- path: (arg) => {
764
- DEV: assert(
765
- null == arg || "string" === typeof arg || arg instanceof Path2D,
766
- "[litecanvas] path() 1st param must be a string or a Path2D instance"
767
- );
768
- return new Path2D(arg);
769
- },
770
- /**
771
- * Fills the current or given path with a given color.
755
+ * Fills the current path with a given color.
772
756
  *
773
757
  * @param {number} [color=0]
774
- * @param {Path2D} [path]
775
758
  */
776
- fill(color, path) {
759
+ fill(color) {
777
760
  DEV: assert(
778
761
  null == color || isNumber(color) && color >= 0,
779
762
  "[litecanvas] fill() 1st param must be a positive number or zero"
780
763
  );
781
- DEV: assert(
782
- null == path || path instanceof Path2D,
783
- "[litecanvas] fill() 2nd param must be a Path2D instance"
784
- );
785
764
  _ctx.fillStyle = _colors[~~color % _colors.length];
786
- if (path) {
787
- _ctx.fill(path);
788
- } else {
789
- _ctx.fill();
790
- }
765
+ _ctx.fill();
791
766
  },
792
767
  /**
793
- * Outlines the current or given path with a given color.
768
+ * Outlines the current path with a given color.
794
769
  *
795
770
  * @param {number} [color=0]
796
- * @param {Path2D} [path]
797
771
  */
798
- stroke(color, path) {
772
+ stroke(color) {
799
773
  DEV: assert(
800
774
  null == color || isNumber(color) && color >= 0,
801
775
  "[litecanvas] stroke() 1st param must be a positive number or zero"
802
776
  );
803
- DEV: assert(
804
- null == path || path instanceof Path2D,
805
- "[litecanvas] stroke() 2nd param must be a Path2D instance"
806
- );
807
777
  _ctx.strokeStyle = _colors[~~color % _colors.length];
808
- if (path) {
809
- _ctx.stroke(path);
810
- } else {
811
- _ctx.stroke();
812
- }
778
+ _ctx.stroke();
813
779
  },
814
780
  /**
815
- * Turn given path into a clipping region.
816
- *
817
- * Note: always call `push()` before and `pop()` after.
781
+ * Turns a path (in the callback) into the current clipping region.
818
782
  *
819
- * @param {Path2D} path
783
+ * @param {clipCallback} callback
820
784
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
821
785
  */
822
- clip(path) {
786
+ clip(callback) {
823
787
  DEV: assert(
824
- path instanceof Path2D,
825
- "[litecanvas] clip() 1st param must be a Path2D instance"
788
+ "function" === typeof callback,
789
+ "[litecanvas] clip() 1st param must be a function"
826
790
  );
827
- _ctx.clip(path);
791
+ beginPath(_ctx);
792
+ callback(_ctx);
793
+ _ctx.clip();
828
794
  },
829
795
  /** SOUND API */
830
796
  /**
@@ -1007,7 +973,7 @@
1007
973
  // 1
1008
974
  _initialized,
1009
975
  // 2
1010
- _rafid,
976
+ _deltaTime,
1011
977
  // 3
1012
978
  _scale,
1013
979
  // 4
@@ -1036,8 +1002,7 @@
1036
1002
  * Stops the litecanvas instance and remove all event listeners.
1037
1003
  */
1038
1004
  quit() {
1039
- cancelAnimationFrame(_rafid);
1040
- _rafid = 0;
1005
+ instance.pause();
1041
1006
  instance.emit("quit");
1042
1007
  _eventListeners = {};
1043
1008
  for (const removeListener of _browserEventListeners) {
@@ -1050,6 +1015,29 @@
1050
1015
  delete root.ENGINE;
1051
1016
  }
1052
1017
  _initialized = false;
1018
+ },
1019
+ /**
1020
+ * Pauses the engine loop (update & draw).
1021
+ */
1022
+ pause() {
1023
+ cancelAnimationFrame(_rafid);
1024
+ _rafid = 0;
1025
+ },
1026
+ /**
1027
+ * Resumes (if paused) the engine loop.
1028
+ */
1029
+ resume() {
1030
+ if (!_rafid && _initialized) {
1031
+ _rafid = raf(drawFrame);
1032
+ }
1033
+ },
1034
+ /**
1035
+ * Returns `true` if the engine loop is paused.
1036
+ *
1037
+ * @returns {boolean}
1038
+ */
1039
+ paused() {
1040
+ return !_rafid;
1053
1041
  }
1054
1042
  };
1055
1043
  for (const k of _mathFunctions.split(",")) {
@@ -1282,30 +1270,32 @@
1282
1270
  }
1283
1271
  _initialized = true;
1284
1272
  instance.emit("init", instance);
1273
+ instance.textalign("start", "top");
1285
1274
  _lastFrameTime = performance.now();
1286
- _rafid = raf(drawFrame);
1275
+ instance.resume();
1287
1276
  }
1288
1277
  function drawFrame(now) {
1278
+ if (!settings.animate) {
1279
+ return instance.emit("draw");
1280
+ }
1289
1281
  let updated = 0;
1290
- if (settings.animate) {
1291
- _accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
1292
- _lastFrameTime = now;
1282
+ let frameTime = (now - _lastFrameTime) / 1e3;
1283
+ _lastFrameTime = now;
1284
+ if (frameTime < 0.1) {
1285
+ _accumulated += frameTime;
1293
1286
  while (_accumulated >= _deltaTime) {
1294
1287
  updated++;
1295
1288
  instance.emit("update", _deltaTime * _timeScale, updated);
1296
1289
  instance.def("T", instance.T + _deltaTime * _timeScale);
1297
1290
  _accumulated -= _deltaTime;
1298
1291
  }
1299
- if (_rafid) {
1300
- _rafid = raf(drawFrame);
1301
- }
1302
- } else {
1303
- updated = 1;
1304
1292
  }
1305
1293
  if (updated) {
1306
- instance.textalign("start", "top");
1307
1294
  instance.emit("draw");
1308
1295
  }
1296
+ if (_rafid) {
1297
+ _rafid = raf(drawFrame);
1298
+ }
1309
1299
  }
1310
1300
  function setupCanvas() {
1311
1301
  if ("string" === typeof settings.canvas) {
package/dist/dist.js CHANGED
@@ -31,7 +31,7 @@
31
31
  const root = window, math = Math, TWO_PI = math.PI * 2, raf = requestAnimationFrame, _browserEventListeners = [], on = (elem, evt, callback) => {
32
32
  elem.addEventListener(evt, callback, false);
33
33
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false));
34
- }, isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
34
+ }, beginPath = (c) => c.beginPath(), isNumber = Number.isFinite, zzfx = setupZzFX(root), defaults = {
35
35
  width: null,
36
36
  height: null,
37
37
  autoscale: true,
@@ -246,7 +246,7 @@
246
246
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect
247
247
  */
248
248
  rect(x, y, width, height, color, radii) {
249
- _ctx.beginPath();
249
+ beginPath(_ctx);
250
250
  _ctx[radii ? "roundRect" : "rect"](
251
251
  ~~x - _outline_fix,
252
252
  ~~y - _outline_fix,
@@ -267,7 +267,7 @@
267
267
  * @param {number|number[]} [radii] A number or list specifying the radii used to draw a rounded-borders rectangle
268
268
  */
269
269
  rectfill(x, y, width, height, color, radii) {
270
- _ctx.beginPath();
270
+ beginPath(_ctx);
271
271
  _ctx[radii ? "roundRect" : "rect"](~~x, ~~y, ~~width, ~~height, radii);
272
272
  instance.fill(color);
273
273
  },
@@ -280,7 +280,7 @@
280
280
  * @param {number} [color=0] the color index
281
281
  */
282
282
  circ(x, y, radius, color) {
283
- _ctx.beginPath();
283
+ beginPath(_ctx);
284
284
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
285
285
  instance.stroke(color);
286
286
  },
@@ -293,7 +293,7 @@
293
293
  * @param {number} [color=0] the color index
294
294
  */
295
295
  circfill(x, y, radius, color) {
296
- _ctx.beginPath();
296
+ beginPath(_ctx);
297
297
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
298
298
  instance.fill(color);
299
299
  },
@@ -307,7 +307,7 @@
307
307
  * @param {number} [color=0] the color index
308
308
  */
309
309
  oval(x, y, radiusX, radiusY, color) {
310
- _ctx.beginPath();
310
+ beginPath(_ctx);
311
311
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
312
312
  instance.stroke(color);
313
313
  },
@@ -321,7 +321,7 @@
321
321
  * @param {number} [color=0] the color index
322
322
  */
323
323
  ovalfill(x, y, radiusX, radiusY, color) {
324
- _ctx.beginPath();
324
+ beginPath(_ctx);
325
325
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
326
326
  instance.fill(color);
327
327
  },
@@ -335,7 +335,7 @@
335
335
  * @param {number} [color=0] the color index
336
336
  */
337
337
  line(x1, y1, x2, y2, color) {
338
- _ctx.beginPath();
338
+ beginPath(_ctx);
339
339
  let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0;
340
340
  let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0;
341
341
  _ctx.moveTo(~~x1 + xfix, ~~y1 + yfix);
@@ -517,55 +517,33 @@
517
517
  _ctx.globalAlpha = instance.clamp(value, 0, 1);
518
518
  },
519
519
  /**
520
- * Returns a newly instantiated Path2D object, optionally with another
521
- * path as an argument (creates a copy), or optionally with a string
522
- * consisting of SVG path data.
523
- *
524
- * @param {Path2D|string} [arg]
525
- * @returns Path2D
526
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
527
- */
528
- path: (arg) => {
529
- return new Path2D(arg);
530
- },
531
- /**
532
- * Fills the current or given path with a given color.
520
+ * Fills the current path with a given color.
533
521
  *
534
522
  * @param {number} [color=0]
535
- * @param {Path2D} [path]
536
523
  */
537
- fill(color, path) {
524
+ fill(color) {
538
525
  _ctx.fillStyle = _colors[~~color % _colors.length];
539
- if (path) {
540
- _ctx.fill(path);
541
- } else {
542
- _ctx.fill();
543
- }
526
+ _ctx.fill();
544
527
  },
545
528
  /**
546
- * Outlines the current or given path with a given color.
529
+ * Outlines the current path with a given color.
547
530
  *
548
531
  * @param {number} [color=0]
549
- * @param {Path2D} [path]
550
532
  */
551
- stroke(color, path) {
533
+ stroke(color) {
552
534
  _ctx.strokeStyle = _colors[~~color % _colors.length];
553
- if (path) {
554
- _ctx.stroke(path);
555
- } else {
556
- _ctx.stroke();
557
- }
535
+ _ctx.stroke();
558
536
  },
559
537
  /**
560
- * Turn given path into a clipping region.
538
+ * Turns a path (in the callback) into the current clipping region.
561
539
  *
562
- * Note: always call `push()` before and `pop()` after.
563
- *
564
- * @param {Path2D} path
540
+ * @param {clipCallback} callback
565
541
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
566
542
  */
567
- clip(path) {
568
- _ctx.clip(path);
543
+ clip(callback) {
544
+ beginPath(_ctx);
545
+ callback(_ctx);
546
+ _ctx.clip();
569
547
  },
570
548
  /** SOUND API */
571
549
  /**
@@ -704,7 +682,7 @@
704
682
  // 1
705
683
  _initialized,
706
684
  // 2
707
- _rafid,
685
+ _deltaTime,
708
686
  // 3
709
687
  _scale,
710
688
  // 4
@@ -733,8 +711,7 @@
733
711
  * Stops the litecanvas instance and remove all event listeners.
734
712
  */
735
713
  quit() {
736
- cancelAnimationFrame(_rafid);
737
- _rafid = 0;
714
+ instance.pause();
738
715
  instance.emit("quit");
739
716
  _eventListeners = {};
740
717
  for (const removeListener of _browserEventListeners) {
@@ -747,6 +724,29 @@
747
724
  delete root.ENGINE;
748
725
  }
749
726
  _initialized = false;
727
+ },
728
+ /**
729
+ * Pauses the engine loop (update & draw).
730
+ */
731
+ pause() {
732
+ cancelAnimationFrame(_rafid);
733
+ _rafid = 0;
734
+ },
735
+ /**
736
+ * Resumes (if paused) the engine loop.
737
+ */
738
+ resume() {
739
+ if (!_rafid && _initialized) {
740
+ _rafid = raf(drawFrame);
741
+ }
742
+ },
743
+ /**
744
+ * Returns `true` if the engine loop is paused.
745
+ *
746
+ * @returns {boolean}
747
+ */
748
+ paused() {
749
+ return !_rafid;
750
750
  }
751
751
  };
752
752
  for (const k of _mathFunctions.split(",")) {
@@ -968,30 +968,32 @@
968
968
  }
969
969
  _initialized = true;
970
970
  instance.emit("init", instance);
971
+ instance.textalign("start", "top");
971
972
  _lastFrameTime = performance.now();
972
- _rafid = raf(drawFrame);
973
+ instance.resume();
973
974
  }
974
975
  function drawFrame(now) {
976
+ if (!settings.animate) {
977
+ return instance.emit("draw");
978
+ }
975
979
  let updated = 0;
976
- if (settings.animate) {
977
- _accumulated += math.min(0.1, (now - _lastFrameTime) / 1e3);
978
- _lastFrameTime = now;
980
+ let frameTime = (now - _lastFrameTime) / 1e3;
981
+ _lastFrameTime = now;
982
+ if (frameTime < 0.1) {
983
+ _accumulated += frameTime;
979
984
  while (_accumulated >= _deltaTime) {
980
985
  updated++;
981
986
  instance.emit("update", _deltaTime * _timeScale, updated);
982
987
  instance.def("T", instance.T + _deltaTime * _timeScale);
983
988
  _accumulated -= _deltaTime;
984
989
  }
985
- if (_rafid) {
986
- _rafid = raf(drawFrame);
987
- }
988
- } else {
989
- updated = 1;
990
990
  }
991
991
  if (updated) {
992
- instance.textalign("start", "top");
993
992
  instance.emit("draw");
994
993
  }
994
+ if (_rafid) {
995
+ _rafid = raf(drawFrame);
996
+ }
995
997
  }
996
998
  function setupCanvas() {
997
999
  if ("string" === typeof settings.canvas) {
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,n=Math,i=2*n.PI,l=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,n=.05,i=220,l=0,o=0,r=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,v=0,w=0,b=1,x=0,y=0,k=0)=>{let E=Math,z=2*E.PI,P=c*=500*z/44100/44100,T=i*=(1-n+2*n*E.random(n=[]))*z/44100,C=0,I=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,N=E.cos(D),F=E.sin,q=F(D)/4,B=1+q,H=-2*N/B,O=(1-q)/B,V=(1+M*N)/2/B,W=-(M+N)/B,R=0,G=0,$=0,j=0;for(l=44100*l+9,x*=44100,o*=44100,r*=44100,w*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=l+x+o+r+w|0;A<M;n[A++]=Y*a)++X%(100*v|0)||(Y=s?1<s?2<s?3<s?F(C*C):E.max(E.min(E.tan(C),1),-1):1-(2*C/z%2+2)%2:1-4*E.abs(E.round(C/z)-C/z):F(C),Y=(h?1-y+y*F(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<l?A/l:A<l+x?1-(A-l)/x*(1-b):A<l+x+o?b:A<M-w?(M-A-w)/r*b:0),Y=w?Y/2+(w>A?0:(A<M-w?1:(M-A)/w)*n[A-w|0]/2/a):Y,k&&(Y=j=V*R+W*(R=G)+V*(G=Y)-O*$-H*($=j))),C+=(D=(i+=c+=d)*E.cos(m*I++))+D*g*F(A**5),L&&++L>u&&(i+=p,T+=p,L=0),!h||++S%h||(i=T,c=P,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(n),(i=t.createBufferSource()).buffer=a,i.connect(t.destination),i.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,v=1/60,w=0,b,x="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],P={},T={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:i/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,n,i,l){let o=(e-t)/(a-t)*(i-n)+n;return l?T.clamp(o,n,i):o},norm:(e,t,a)=>T.map(e,t,a,0,1),wave:(e,t,a,n=Math.sin)=>e+(n(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(T.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):T.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~n+2*h,l),T.stroke(i)},rectfill(e,t,a,n,i,l){u.beginPath(),u[l?"roundRect":"rect"](~~e,~~t,~~a,~~n,l),T.fill(i)},circ(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),T.stroke(n)},circfill(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,i),T.fill(n)},oval(e,t,a,n,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~n,0,0,i),T.stroke(l)},ovalfill(e,t,a,n,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~n,0,0,i),T.fill(l)},line(e,t,a,n,i){u.beginPath();let l=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~n);u.moveTo(~~e+l,~~t+o),u.lineTo(~~a+l,~~n+o),T.stroke(i)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,n=3,i="normal"){u.font=`${i} ${y}px ${x}`,u.fillStyle=E[~~n%E.length],u.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,n={}){let i=n.canvas||new OffscreenCanvas(1,1),l=n.scale||1,o=u;if(i.width=e*l,i.height=t*l,(u=i.getContext("2d")).scale(l,l),Array.isArray(a)){let e=0,t=0;for(let n of(u.imageSmoothingEnabled=!1,a)){for(let a of n)" "!==a&&"."!==a&&T.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=o,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=T.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,n=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(P[e=e.toLowerCase()]=P[e]||new Set,P[e].add(t),()=>P&&P[e].delete(t)),emit(e,t,a,n,i){f&&(L("before:"+(e=e.toLowerCase()),t,a,n,i),L(e,t,a,n,i),L("after:"+e,t,a,n,i))},pal(t=e){E=t},def(e,n){T[e]=n,t.global&&(a[e]=n)},timescale(e){g=e},framerate(e){v=1/~~e},stat(e){let n={index:e,value:[t,f,b,p,P,E,z,g,a.zzfxV||1,k,y,x][e]};return T.emit("stat",n),n.value},quit(){for(let e of(cancelAnimationFrame(b),b=0,T.emit("quit"),P={},o))e();if(t.global){for(let e in T)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))T[e]=n[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]&&T.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&r(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,n=(e,a,n)=>{let i={x:a,y:n,startX:a,startY:n,ts:performance.now()};return t.set(e,i),i},i=(e,a,i)=>{let l=t.get(e)||n(e);l.x=a,l.y=i},l=e=>e&&performance.now()-e.ts<=300,o=e=>e.preventDefault(),s=!1;r(d,"mousedown",t=>{if(0===t.button){o(t);let[a,i]=e(t.pageX,t.pageY);T.emit("tap",a,i,0),n(0,a,i),s=!0}}),r(d,"mouseup",a=>{if(0===a.button){o(a);let n=t.get(0),[i,r]=e(a.pageX,a.pageY);l(n)&&T.emit("tapped",n.startX,n.startY,0),T.emit("untap",i,r,0),t.delete(0),s=!1}}),r(d,"mousemove",t=>{o(t);let[a,n]=e(t.pageX,t.pageY);T.def("MX",a),T.def("MY",n),s&&(T.emit("tapping",a,n,0),i(0,a,n))}),r(d,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,i]=e(a.pageX,a.pageY);T.emit("tap",t,i,a.identifier+1),n(a.identifier+1,t,i)}}),r(d,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);T.emit("tapping",t,n,a.identifier+1),i(a.identifier+1,t,n)}});let f=e=>{o(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,n]of t)a.includes(e)||(l(n)&&T.emit("tapped",n.startX,n.startY,e),T.emit("untap",n.x,n.y,e),t.delete(e))};r(d,"touchend",f),r(d,"touchcancel",f),r(a,"blur",()=>{for(let[e,a]of(s=!1,t))T.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,n=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),T.listen("after:update",()=>t.clear()),T.def("iskeydown",t=>n(e,t)),T.def("iskeypressed",e=>n(t,e))}f=!0,T.emit("init",T),m=performance.now(),b=l(I)}function I(e){let a=0;if(t.animate){for(w+=n.min(.1,(e-m)/1e3),m=e;w>=v;)a++,T.emit("update",v*g,a),T.def("T",T.T+v*g),w-=v;b&&(b=l(I))}else a=1;a&&(T.textalign("start","top"),T.emit("draw"))}function A(){let e=t.width||a.innerWidth,i=t.height||t.width||a.innerHeight;T.def("W",e),T.def("H",i),d.width=e,d.height=i,t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(a.innerWidth/e,a.innerHeight/i),p=(t.pixelart?~~p:p)||1,d.style.width=e*p+"px",d.style.height=i*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),T.emit("resized",p),T.cls(0),t.animate||l(I)}function L(e,t,a,n,i){if(P[e])for(let l of P[e])l(t,a,n,i)}function S(e,t){let a=e(T,t);for(let e in a)T.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,T),a.ENGINE=T}return u=(d=(d="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?r(a,"DOMContentLoaded",()=>l(C)):l(C),T}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,l=Math,i=2*l.PI,n=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.beginPath(),f=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,l=.05,i=220,n=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,m=0,g=0,h=0,v=0,w=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=i*=(1-l+2*l*E.random(l=[]))*z/44100,I=0,A=0,L=0,S=1,X=0,Y=0,M=0,N=k<0?-1:1,P=z*N*k*2/44100,D=E.cos(P),F=E.sin,q=F(P)/4,B=1+q,H=-2*D/B,O=(1-q)/B,V=(1+N*D)/2/B,W=-(N+D)/B,R=0,G=0,$=0,j=0;for(n=44100*n+9,y*=44100,r*=44100,o*=44100,w*=44100,d*=500*z/85766121e6,h*=z/44100,p*=z/44100,u*=44100,m=44100*m|0,a*=.3*e.zzfxV,N=n+y+r+o+w|0;L<N;l[L++]=M*a)++Y%(100*v|0)||(M=s?1<s?2<s?3<s?F(I*I):E.max(E.min(E.tan(I),1),-1):1-(2*I/z%2+2)%2:1-4*E.abs(E.round(I/z)-I/z):F(I),M=(m?1-b+b*F(z*L/m):1)*(M<0?-1:1)*E.abs(M)**f*(L<n?L/n:L<n+y?1-(L-n)/y*(1-x):L<n+y+r?x:L<N-w?(N-L-w)/o*x:0),M=w?M/2+(w>L?0:(L<N-w?1:(N-L)/w)*l[L-w|0]/2/a):M,k&&(M=j=V*R+W*(R=G)+V*(G=M)-O*$-H*($=j))),I+=(P=(i+=c+=d)*E.cos(h*A++))+P*g*F(L**5),S&&++S>u&&(i+=p,C+=p,S=0),!m||++X%m||(i=C,c=T,S=S||1);(a=t.createBuffer(1,N,44100)).getChannelData(0).set(l),(i=t.createBufferSource()).buffer=a,i.connect(t.destination),i.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let c=!1,d=[],p,u=1,m,g=.5,h=1,v,w=1/60,x=0,y,b="sans-serif",k=20,E=Date.now(),z=e,T=[.5,0,1750,,,.3,1,,,,600,.1],C={},I={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:i,HALF_PI:i/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,i,n){let r=(e-t)/(a-t)*(i-l)+l;return n?I.clamp(r,l,i):r},norm:(e,t,a)=>I.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)=>(E=(1664525*E+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>l.floor(I.rand(e,t+1)),rseed(e){E=~~e},cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):I.rectfill(0,0,m.canvas.width,m.canvas.height,e)},rect(e,t,a,l,i,n){s(m),m[n?"roundRect":"rect"](~~e-g,~~t-g,~~a+2*g,~~l+2*g,n),I.stroke(i)},rectfill(e,t,a,l,i,n){s(m),m[n?"roundRect":"rect"](~~e,~~t,~~a,~~l,n),I.fill(i)},circ(e,t,a,l){s(m),m.arc(~~e,~~t,~~a,0,i),I.stroke(l)},circfill(e,t,a,l){s(m),m.arc(~~e,~~t,~~a,0,i),I.fill(l)},oval(e,t,a,l,n){s(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,i),I.stroke(n)},ovalfill(e,t,a,l,n){s(m),m.ellipse(~~e,~~t,~~a,~~l,0,0,i),I.fill(n)},line(e,t,a,l,i){s(m);let n=.5*(0!==g&&~~e==~~a),r=.5*(0!==g&&~~t==~~l);m.moveTo(~~e+n,~~t+r),m.lineTo(~~a+n,~~l+r),I.stroke(i)},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,i="normal"){m.font=`${i} ${k}px ${b}`,m.fillStyle=z[~~l%z.length],m.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){k=e},textalign(e,t){e&&(m.textAlign=e),t&&(m.textBaseline=t)},image(e,t,a){m.drawImage(a,~~e,~~t)},paint(e,t,a,l={}){let i=l.canvas||new OffscreenCanvas(1,1),n=l.scale||1,r=m;if(i.width=e*n,i.height=t*n,(m=i.getContext("2d")).scale(n,n),Array.isArray(a)){let e=0,t=0;for(let l of(m.imageSmoothingEnabled=!1,a)){for(let a of l)" "!==a&&"."!==a&&I.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(m);return m=r,i.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=I.clamp(e,0,1)},fill(e){m.fillStyle=z[~~e%z.length],m.fill()},stroke(e){m.strokeStyle=z[~~e%z.length],m.stroke()},clip(e){s(m),e(m),m.clip()},sfx:(e,t=0,l=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||T,(0!==t||1!==l)&&((e=e.slice())[0]=l*(e[0]||1),e[10]=~~e[10]+t),f.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>p,use(e,t={}){c?Y(e,t):d.push([e,t])},listen:(e,t)=>(C[e=e.toLowerCase()]=C[e]||new Set,C[e].add(t),()=>C&&C[e].delete(t)),emit(e,t,a,l,i){c&&(X("before:"+(e=e.toLowerCase()),t,a,l,i),X(e,t,a,l,i),X("after:"+e,t,a,l,i))},pal(t=e){z=t},def(e,l){I[e]=l,t.global&&(a[e]=l)},timescale(e){h=e},framerate(e){w=1/~~e},stat(e){let l={index:e,value:[t,c,w,u,C,z,T,h,a.zzfxV||1,E,k,b][e]};return I.emit("stat",l),l.value},quit(){for(let e of(I.pause(),I.emit("quit"),C={},r))e();if(t.global){for(let e in I)delete a[e];delete a.ENGINE}c=!1},pause(){cancelAnimationFrame(y),y=0},resume(){!y&&c&&(y=n(L))},paused:()=>!y};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))I[e]=l[e];function A(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&I.listen(t,e[t]);for(let[e,t]of d)Y(e,t);if(t.autoscale&&o(a,"resize",S),t.tapEvents){let e=(e,t)=>[(e-p.offsetLeft)/u,(t-p.offsetTop)/u],t=new Map,l=(e,a,l)=>{let i={x:a,y:l,startX:a,startY:l,ts:performance.now()};return t.set(e,i),i},i=(e,a,i)=>{let n=t.get(e)||l(e);n.x=a,n.y=i},n=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(p,"mousedown",t=>{if(0===t.button){r(t);let[a,i]=e(t.pageX,t.pageY);I.emit("tap",a,i,0),l(0,a,i),s=!0}}),o(p,"mouseup",a=>{if(0===a.button){r(a);let l=t.get(0),[i,o]=e(a.pageX,a.pageY);n(l)&&I.emit("tapped",l.startX,l.startY,0),I.emit("untap",i,o,0),t.delete(0),s=!1}}),o(p,"mousemove",t=>{r(t);let[a,l]=e(t.pageX,t.pageY);I.def("MX",a),I.def("MY",l),s&&(I.emit("tapping",a,l,0),i(0,a,l))}),o(p,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,i]=e(a.pageX,a.pageY);I.emit("tap",t,i,a.identifier+1),l(a.identifier+1,t,i)}}),o(p,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);I.emit("tapping",t,l,a.identifier+1),i(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)||(n(l)&&I.emit("tapped",l.startX,l.startY,e),I.emit("untap",l.x,l.y,e),t.delete(e))};o(p,"touchend",f),o(p,"touchcancel",f),o(a,"blur",()=>{for(let[e,a]of(s=!1,t))I.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,l=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),I.listen("after:update",()=>t.clear()),I.def("iskeydown",t=>l(e,t)),I.def("iskeypressed",e=>l(t,e))}c=!0,I.emit("init",I),I.textalign("start","top"),v=performance.now(),I.resume()}function L(e){if(!t.animate)return I.emit("draw");let a=0,l=(e-v)/1e3;if(v=e,l<.1)for(x+=l;x>=w;)a++,I.emit("update",w*h,a),I.def("T",I.T+w*h),x-=w;a&&I.emit("draw"),y&&(y=n(L))}function S(){let e=t.width||a.innerWidth,i=t.height||t.width||a.innerHeight;I.def("W",e),I.def("H",i),p.width=e,p.height=i,t.autoscale&&(p.style.display||(p.style.display="block",p.style.margin="auto"),u=l.min(a.innerWidth/e,a.innerHeight/i),u=(t.pixelart?~~u:u)||1,p.style.width=e*u+"px",p.style.height=i*u+"px"),(!t.antialias||t.pixelart)&&(m.imageSmoothingEnabled=!1,p.style.imageRendering="pixelated"),I.emit("resized",u),I.cls(0),t.animate||n(L)}function X(e,t,a,l,i){if(C[e])for(let n of C[e])n(t,a,l,i)}function Y(e,t){let a=e(I,t);for(let e in a)I.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,I),a.ENGINE=I}return m=(p=(p="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),o(p,"click",()=>a.focus()),p.style="",S(),p.parentNode||document.body.appendChild(p),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>n(A)):n(A),I}})();
package/package.json CHANGED
@@ -1,23 +1,10 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.87.0",
3
+ "version": "0.88.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
- "devDependencies": {
9
- "@litecanvas/jsdom-extras": "^2.0.0",
10
- "@swc/core": "^1.12.9",
11
- "@types/jsdom": "^21.1.7",
12
- "ava": "^6.4.0",
13
- "esbuild": "^0.25.5",
14
- "genversion": "^3.2.0",
15
- "gzip-size": "^7.0.0",
16
- "jsdom": "^26.1.0",
17
- "prettier": "^3.6.2",
18
- "sinon": "^21.0.0",
19
- "tap-min": "^3.0.0"
20
- },
21
8
  "homepage": "https://litecanvas.github.io/about.html",
22
9
  "repository": {
23
10
  "type": "git",
@@ -29,18 +16,6 @@
29
16
  "type": "module",
30
17
  "main": "src/index.js",
31
18
  "types": "types/index.d.ts",
32
- "scripts": {
33
- "test": "ava --timeout=1s --fast-fail --tap | tap-min",
34
- "test:watch": "ava --watch",
35
- "dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
36
- "build": "npm run genversion && node script/build.js",
37
- "gzip-size": "gzip -c dist/dist.min.js | wc -c | xargs printf \" Gzip size: %s bytes\n\"",
38
- "format": "prettier -w src/* samples/* types/* script/* types/*",
39
- "check-types": "npx ts types/*",
40
- "genversion": "genversion --es6 --semi version.js",
41
- "prepare": "npm run build",
42
- "prepublishOnly": "npm test"
43
- },
44
19
  "keywords": [
45
20
  "canvas",
46
21
  "2d",
@@ -53,6 +28,36 @@
53
28
  "pico-8",
54
29
  "p5"
55
30
  ],
31
+ "devDependencies": {
32
+ "@litecanvas/jsdom-extras": "^2.0.0",
33
+ "@size-limit/preset-small-lib": "^11.2.0",
34
+ "@swc/core": "^1.12.9",
35
+ "@types/jsdom": "^21.1.7",
36
+ "ava": "^6.4.0",
37
+ "esbuild": "^0.25.5",
38
+ "genversion": "^3.2.0",
39
+ "gzip-size": "^7.0.0",
40
+ "jsdom": "^26.1.0",
41
+ "prettier": "^3.6.2",
42
+ "sinon": "^21.0.0",
43
+ "size-limit": "^11.2.0",
44
+ "tap-min": "^3.0.0"
45
+ },
46
+ "trustedDependencies": [
47
+ "@swc/core"
48
+ ],
49
+ "scripts": {
50
+ "prepare": "npm run build",
51
+ "prepublishOnly": "npm test",
52
+ "test": "ava --timeout=1s --fast-fail --tap | tap-min",
53
+ "test:watch": "ava --watch",
54
+ "dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
55
+ "build": "npm run genversion && node script/build.js && size-limit",
56
+ "gzip-size": "gzip -c dist/dist.min.js | wc -c | xargs printf \" Gzip size: %s bytes\n\"",
57
+ "format": "prettier -w src/* samples/* types/* script/* types/*",
58
+ "check-types": "npx ts types/*",
59
+ "genversion": "genversion --es6 --semi version.js"
60
+ },
56
61
  "files": [
57
62
  "dist",
58
63
  "src",
@@ -63,7 +68,16 @@
63
68
  "tests/**/*.js"
64
69
  ]
65
70
  },
66
- "trustedDependencies": [
67
- "@swc/core"
71
+ "size-limit": [
72
+ {
73
+ "path": "dist/dist.min.js",
74
+ "limit": "4kb",
75
+ "gzip": true
76
+ },
77
+ {
78
+ "path": "dist/dist.min.js",
79
+ "limit": "4kb",
80
+ "gzip": false
81
+ }
68
82
  ]
69
83
  }
package/src/index.js CHANGED
@@ -23,6 +23,8 @@ export default function litecanvas(settings = {}) {
23
23
  elem.addEventListener(evt, callback, false)
24
24
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
25
25
  },
26
+ /** @type {(c: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) => void} */
27
+ beginPath = (c) => c.beginPath(),
26
28
  isNumber = Number.isFinite,
27
29
  zzfx = setupZzFX(root),
28
30
  /** @type {LitecanvasOptions} */
@@ -63,7 +65,7 @@ export default function litecanvas(settings = {}) {
63
65
  _deltaTime = 1 / 60,
64
66
  /** @type {number} */
65
67
  _accumulated = 0,
66
- /** @type {number|null} */
68
+ /** @type {number?} */
67
69
  _rafid,
68
70
  /** @type {string} */
69
71
  _fontFamily = 'sans-serif',
@@ -406,7 +408,7 @@ export default function litecanvas(settings = {}) {
406
408
  '[litecanvas] rect() 6th param must be a number or array of numbers'
407
409
  )
408
410
 
409
- _ctx.beginPath()
411
+ beginPath(_ctx)
410
412
  _ctx[radii ? 'roundRect' : 'rect'](
411
413
  ~~x - _outline_fix,
412
414
  ~~y - _outline_fix,
@@ -447,7 +449,7 @@ export default function litecanvas(settings = {}) {
447
449
  '[litecanvas] rectfill() 6th param must be a number or array of at least 2 numbers'
448
450
  )
449
451
 
450
- _ctx.beginPath()
452
+ beginPath(_ctx)
451
453
  _ctx[radii ? 'roundRect' : 'rect'](~~x, ~~y, ~~width, ~~height, radii)
452
454
  instance.fill(color)
453
455
  },
@@ -472,7 +474,7 @@ export default function litecanvas(settings = {}) {
472
474
  '[litecanvas] circ() 4th param must be a positive number or zero'
473
475
  )
474
476
 
475
- _ctx.beginPath()
477
+ beginPath(_ctx)
476
478
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI)
477
479
  instance.stroke(color)
478
480
  },
@@ -497,7 +499,7 @@ export default function litecanvas(settings = {}) {
497
499
  '[litecanvas] circfill() 4th param must be a positive number or zero'
498
500
  )
499
501
 
500
- _ctx.beginPath()
502
+ beginPath(_ctx)
501
503
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI)
502
504
  instance.fill(color)
503
505
  },
@@ -527,7 +529,7 @@ export default function litecanvas(settings = {}) {
527
529
  '[litecanvas] oval() 5th param must be a positive number or zero'
528
530
  )
529
531
 
530
- _ctx.beginPath()
532
+ beginPath(_ctx)
531
533
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
532
534
  instance.stroke(color)
533
535
  },
@@ -557,7 +559,7 @@ export default function litecanvas(settings = {}) {
557
559
  '[litecanvas] ovalfill() 5th param must be a positive number or zero'
558
560
  )
559
561
 
560
- _ctx.beginPath()
562
+ beginPath(_ctx)
561
563
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
562
564
  instance.fill(color)
563
565
  },
@@ -587,7 +589,7 @@ export default function litecanvas(settings = {}) {
587
589
  '[litecanvas] line() 5th param must be a positive number or zero'
588
590
  )
589
591
 
590
- _ctx.beginPath()
592
+ beginPath(_ctx)
591
593
 
592
594
  let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0
593
595
  let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0
@@ -876,86 +878,50 @@ export default function litecanvas(settings = {}) {
876
878
  },
877
879
 
878
880
  /**
879
- * Returns a newly instantiated Path2D object, optionally with another
880
- * path as an argument (creates a copy), or optionally with a string
881
- * consisting of SVG path data.
882
- *
883
- * @param {Path2D|string} [arg]
884
- * @returns Path2D
885
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
886
- */
887
- path: (arg) => {
888
- DEV: assert(
889
- null == arg || 'string' === typeof arg || arg instanceof Path2D,
890
- '[litecanvas] path() 1st param must be a string or a Path2D instance'
891
- )
892
-
893
- return new Path2D(arg)
894
- },
895
-
896
- /**
897
- * Fills the current or given path with a given color.
881
+ * Fills the current path with a given color.
898
882
  *
899
883
  * @param {number} [color=0]
900
- * @param {Path2D} [path]
901
884
  */
902
- fill(color, path) {
885
+ fill(color) {
903
886
  DEV: assert(
904
887
  null == color || (isNumber(color) && color >= 0),
905
888
  '[litecanvas] fill() 1st param must be a positive number or zero'
906
889
  )
907
- DEV: assert(
908
- null == path || path instanceof Path2D,
909
- '[litecanvas] fill() 2nd param must be a Path2D instance'
910
- )
911
890
 
912
891
  _ctx.fillStyle = _colors[~~color % _colors.length]
913
- if (path) {
914
- _ctx.fill(path)
915
- } else {
916
- _ctx.fill()
917
- }
892
+ _ctx.fill()
918
893
  },
919
894
 
920
895
  /**
921
- * Outlines the current or given path with a given color.
896
+ * Outlines the current path with a given color.
922
897
  *
923
898
  * @param {number} [color=0]
924
- * @param {Path2D} [path]
925
899
  */
926
- stroke(color, path) {
900
+ stroke(color) {
927
901
  DEV: assert(
928
902
  null == color || (isNumber(color) && color >= 0),
929
903
  '[litecanvas] stroke() 1st param must be a positive number or zero'
930
904
  )
931
- DEV: assert(
932
- null == path || path instanceof Path2D,
933
- '[litecanvas] stroke() 2nd param must be a Path2D instance'
934
- )
935
905
 
936
906
  _ctx.strokeStyle = _colors[~~color % _colors.length]
937
- if (path) {
938
- _ctx.stroke(path)
939
- } else {
940
- _ctx.stroke()
941
- }
907
+ _ctx.stroke()
942
908
  },
943
909
 
944
910
  /**
945
- * Turn given path into a clipping region.
911
+ * Turns a path (in the callback) into the current clipping region.
946
912
  *
947
- * Note: always call `push()` before and `pop()` after.
948
- *
949
- * @param {Path2D} path
913
+ * @param {clipCallback} callback
950
914
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
951
915
  */
952
- clip(path) {
916
+ clip(callback) {
953
917
  DEV: assert(
954
- path instanceof Path2D,
955
- '[litecanvas] clip() 1st param must be a Path2D instance'
918
+ 'function' === typeof callback,
919
+ '[litecanvas] clip() 1st param must be a function'
956
920
  )
957
921
 
958
- _ctx.clip(path)
922
+ beginPath(_ctx)
923
+ callback(_ctx)
924
+ _ctx.clip()
959
925
  },
960
926
 
961
927
  /** SOUND API */
@@ -1020,6 +986,7 @@ export default function litecanvas(settings = {}) {
1020
986
  * @returns {HTMLCanvasElement}
1021
987
  */
1022
988
  canvas: () => _canvas,
989
+
1023
990
  /**
1024
991
  * Prepares a plugin to be loaded
1025
992
  *
@@ -1169,7 +1136,7 @@ export default function litecanvas(settings = {}) {
1169
1136
  // 1
1170
1137
  _initialized,
1171
1138
  // 2
1172
- _rafid,
1139
+ _deltaTime,
1173
1140
  // 3
1174
1141
  _scale,
1175
1142
  // 4
@@ -1204,9 +1171,7 @@ export default function litecanvas(settings = {}) {
1204
1171
  */
1205
1172
  quit() {
1206
1173
  // stop the game loop (update & draw)
1207
- cancelAnimationFrame(_rafid)
1208
-
1209
- _rafid = 0
1174
+ instance.pause()
1210
1175
 
1211
1176
  // emit "quit" event to manual clean ups
1212
1177
  instance.emit('quit')
@@ -1231,6 +1196,32 @@ export default function litecanvas(settings = {}) {
1231
1196
  // unset that flag
1232
1197
  _initialized = false
1233
1198
  },
1199
+
1200
+ /**
1201
+ * Pauses the engine loop (update & draw).
1202
+ */
1203
+ pause() {
1204
+ cancelAnimationFrame(_rafid)
1205
+ _rafid = 0
1206
+ },
1207
+
1208
+ /**
1209
+ * Resumes (if paused) the engine loop.
1210
+ */
1211
+ resume() {
1212
+ if (!_rafid && _initialized) {
1213
+ _rafid = raf(drawFrame)
1214
+ }
1215
+ },
1216
+
1217
+ /**
1218
+ * Returns `true` if the engine loop is paused.
1219
+ *
1220
+ * @returns {boolean}
1221
+ */
1222
+ paused() {
1223
+ return !_rafid
1224
+ },
1234
1225
  }
1235
1226
 
1236
1227
  // prettier-ignore
@@ -1506,51 +1497,49 @@ export default function litecanvas(settings = {}) {
1506
1497
  )
1507
1498
  }
1508
1499
 
1500
+ // start the engine
1509
1501
  _initialized = true
1510
-
1511
- // start the game loop
1512
1502
  instance.emit('init', instance)
1513
1503
 
1504
+ // set the default text align and baseline
1505
+ instance.textalign('start', 'top')
1506
+
1514
1507
  _lastFrameTime = performance.now()
1515
- _rafid = raf(drawFrame)
1508
+ instance.resume()
1516
1509
  }
1517
1510
 
1518
1511
  /**
1519
1512
  * @param {DOMHighResTimeStamp} now
1520
1513
  */
1521
1514
  function drawFrame(now) {
1515
+ if (!settings.animate) {
1516
+ return instance.emit('draw')
1517
+ }
1518
+
1522
1519
  let updated = 0
1520
+ let frameTime = (now - _lastFrameTime) / 1000
1523
1521
 
1524
- if (settings.animate) {
1525
- // prevents too long frames
1526
- _accumulated += math.min(0.1, (now - _lastFrameTime) / 1000)
1527
- _lastFrameTime = now
1522
+ _lastFrameTime = now
1528
1523
 
1524
+ if (frameTime < 0.1) {
1525
+ _accumulated += frameTime
1529
1526
  while (_accumulated >= _deltaTime) {
1530
1527
  updated++
1531
1528
  instance.emit('update', _deltaTime * _timeScale, updated)
1532
1529
  instance.def('T', instance.T + _deltaTime * _timeScale)
1533
1530
  _accumulated -= _deltaTime
1534
1531
  }
1535
-
1536
- // request the next frame
1537
- // check if the last ID exists, because
1538
- // quit() delete it (sets to zero)
1539
- if (_rafid) {
1540
- _rafid = raf(drawFrame)
1541
- }
1542
- } else {
1543
- // when the canvas is not animated
1544
- // we force one frame when redraws are triggered
1545
- updated = 1
1546
1532
  }
1547
1533
 
1548
1534
  if (updated) {
1549
- // always set default values for
1550
- // _ctx.textAlign and _ctx.textBaseline before draw
1551
- instance.textalign('start', 'top')
1552
1535
  instance.emit('draw')
1553
1536
  }
1537
+
1538
+ // request the next frame
1539
+ // only when the engine loop are not paused (_rafid >= 1)
1540
+ if (_rafid) {
1541
+ _rafid = raf(drawFrame)
1542
+ }
1554
1543
  }
1555
1544
 
1556
1545
  function setupCanvas() {
package/types/index.d.ts CHANGED
@@ -444,35 +444,24 @@ declare global {
444
444
  */
445
445
  function alpha(value: number): void
446
446
  /**
447
- * Returns a newly instantiated Path2D object, optionally with another
448
- * path as an argument (creates a copy), or optionally with a string
449
- * consisting of SVG path data.
450
- *
451
- * @param [arg]
452
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
453
- */
454
- function path(arg?: Path2D | string): Path2D
455
- /**
456
- * Fills the current or given path with a given color.
447
+ * Fills the current path with a given color.
457
448
  *
458
449
  * @param color
459
- * @param [path]
460
450
  */
461
- function fill(color: number, path?: Path2D): void
451
+ function fill(color: number): void
462
452
  /**
463
- * Outlines the current or given path with a given color.
453
+ * Outlines the current path with a given color.
464
454
  *
465
455
  * @param color
466
- * @param [path]
467
456
  */
468
- function stroke(color: number, path?: Path2D): void
457
+ function stroke(color: number): void
469
458
  /**
470
- * Turn given path into a clipping region.
459
+ * Turns a path (in the callback) into the current clipping region.
471
460
  *
472
- * @param path
461
+ * @param callback
473
462
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
474
463
  */
475
- function clip(path: Path2D): void
464
+ function clip(callback: clipCallback): void
476
465
 
477
466
  /** SOUND API */
478
467
  /**
@@ -578,7 +567,7 @@ declare global {
578
567
  *
579
568
  * - n = 0: the settings passed to that instance
580
569
  * - n = 1: returns true if the "init" event has already been emitted
581
- * - n = 2: the current ID returned by last requestAnimationFrame
570
+ * - n = 2: the current delta time (dt)
582
571
  * - n = 3: the current canvas element scale (not the context 2D scale)
583
572
  * - n = 4: the attached event callbacks
584
573
  * - n = 5: the current color palette
@@ -588,13 +577,25 @@ declare global {
588
577
  * - n = 9: the current RNG state
589
578
  * - n = 10: the current font size
590
579
  * - n = 11: the current font family
591
- * - n = *any other value*: returns undefined
580
+ * - n = *any other value*: probably returns undefined
592
581
  *
593
582
  * @param n
594
583
  */
595
584
  function stat(n: number): any
596
585
  /**
597
- * Stops the litecanvas instance and remove all event listeners.
586
+ * Shutdown the litecanvas instance and remove all event listeners.
598
587
  */
599
588
  function quit(): void
589
+ /**
590
+ * Pauses the engine loop (update & draw).
591
+ */
592
+ function pause(): void
593
+ /**
594
+ * Resumes (if paused) the engine loop.
595
+ */
596
+ function resume(): void
597
+ /**
598
+ * Returns `true` if the engine loop is paused.
599
+ */
600
+ function paused(): boolean
600
601
  }
package/types/types.d.ts CHANGED
@@ -435,35 +435,24 @@ type LitecanvasInstance = {
435
435
  */
436
436
  alpha(value: number): void
437
437
  /**
438
- * Returns a newly instantiated Path2D object, optionally with another
439
- * path as an argument (creates a copy), or optionally with a string
440
- * consisting of SVG path data.
441
- *
442
- * @param [arg]
443
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
444
- */
445
- path(arg?: Path2D | string): Path2D
446
- /**
447
- * Fills the current or given path with a given color.
438
+ * Fills the current path with a given color.
448
439
  *
449
440
  * @param color
450
- * @param [path]
451
441
  */
452
- fill(color: number, path?: Path2D): void
442
+ fill(color: number): void
453
443
  /**
454
- * Outlines the current or given path with a given color.
444
+ * Outlines the current path with a given color.
455
445
  *
456
446
  * @param color
457
- * @param [path]
458
447
  */
459
- stroke(color: number, path?: Path2D): void
448
+ stroke(color: number): void
460
449
  /**
461
- * Turn given path into a clipping region.
450
+ * Turns a path (in the callback) into the current clipping region.
462
451
  *
463
- * @param path
452
+ * @param callback
464
453
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
465
454
  */
466
- clip(path: Path2D): void
455
+ clip(callback: clipCallback): void
467
456
 
468
457
  /** SOUND API */
469
458
  /**
@@ -565,7 +554,7 @@ type LitecanvasInstance = {
565
554
  *
566
555
  * - n = 0: the settings passed to that instance
567
556
  * - n = 1: returns true if the "init" event has already been emitted
568
- * - n = 2: the current ID returned by last requestAnimationFrame
557
+ * - n = 2: the current delta time (dt)
569
558
  * - n = 3: the current canvas element scale (not the context 2D scale)
570
559
  * - n = 4: the attached event callbacks
571
560
  * - n = 5: the current color palette
@@ -575,7 +564,7 @@ type LitecanvasInstance = {
575
564
  * - n = 9: the current RNG state
576
565
  * - n = 10: the current font size
577
566
  * - n = 11: the current font family
578
- * - n = *any other value*: returns undefined
567
+ * - n = *any other value*: probably returns undefined
579
568
  *
580
569
  * @param n
581
570
  */
@@ -584,6 +573,18 @@ type LitecanvasInstance = {
584
573
  * Stops the litecanvas instance and remove all event listeners.
585
574
  */
586
575
  quit(): void
576
+ /**
577
+ * Pauses the engine loop (update & draw).
578
+ */
579
+ pause(): void
580
+ /**
581
+ * Resumes (if paused) the engine loop.
582
+ */
583
+ resume(): void
584
+ /**
585
+ * Returns `true` if the engine loop is paused.
586
+ */
587
+ paused(): boolean
587
588
  }
588
589
 
589
590
  type LitecanvasOptions = {
@@ -667,3 +668,5 @@ type LitecanvasGameLoop = {
667
668
  type drawCallback = (context: OffscreenCanvasRenderingContext2D) => void
668
669
 
669
670
  type pluginCallback = (instance: LitecanvasInstance, config?: any) => any
671
+
672
+ type clipCallback = (ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) => void