litecanvas 0.208.0 → 0.300.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -342,8 +342,7 @@ Like `MX` and `MY`, Litecanvas also declares these other variables:
342
342
  - `H`: the height of the game canvas
343
343
  - `T`: the amount of seconds since the game started
344
344
  - `PI`: approximately 3.14 radians (or 180 degrees)
345
- - `TWO_PI`: approximately 6.28 radians (or 360 degrees)
346
- - `HALF_PI`: approximately 1.57 radians (or 90 degrees)
345
+ - `TAU`: approximately 6.28 radians (or 360 degrees)
347
346
 
348
347
  ### And much more!
349
348
 
package/dist/dist.dev.js CHANGED
@@ -115,12 +115,12 @@
115
115
  var assert = (condition, message = "Assertion failed") => {
116
116
  if (!condition) throw new Error("[Litecanvas] " + message);
117
117
  };
118
- var version = "0.208.0";
118
+ var version = "0.300.0";
119
119
  function litecanvas(settings = {}) {
120
120
  const root = window,
121
121
  math = Math,
122
122
  perf = performance,
123
- TWO_PI = math.PI * 2,
123
+ TAU = math.PI * 2,
124
124
  raf = requestAnimationFrame,
125
125
  isNumber = Number.isFinite,
126
126
  _browserEventListeners = [],
@@ -176,8 +176,7 @@
176
176
  T: 0,
177
177
  MX: -1,
178
178
  MY: -1,
179
- TWO_PI,
180
- HALF_PI: TWO_PI / 4,
179
+ TAU,
181
180
  lerp: (start, end, t) => {
182
181
  DEV: assert(isNumber(start), "lerp() 1st parameter must be a number");
183
182
  DEV: assert(isNumber(end), "lerp() 2nd parameter must be a number");
@@ -377,7 +376,7 @@
377
376
  "oval() 5th parameter must be a non-negative number",
378
377
  );
379
378
  beginPath(_ctx);
380
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
379
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU);
381
380
  instance.stroke(color);
382
381
  },
383
382
  ovalfill(x, y, radiusX, radiusY, color) {
@@ -396,7 +395,7 @@
396
395
  "ovalfill() 5th parameter must be a non-negative number",
397
396
  );
398
397
  beginPath(_ctx);
399
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
398
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU);
400
399
  instance.fill(color);
401
400
  },
402
401
  circ(x, y, radius, color) {
@@ -562,18 +561,19 @@
562
561
  "string" === typeof pixels,
563
562
  "spr() 3rd parameter must be a string",
564
563
  );
565
- const rows = pixels.trim().split("\n");
566
- for (let row = 0; row < rows.length; row++) {
567
- const chars = rows[row].trim();
568
- for (let col = 0; col < chars.length; col++) {
569
- const char = chars[col];
570
- if (char !== "." && char !== " ") {
564
+ const rows = pixels
565
+ .replace(/[^\w.\n]/g, "")
566
+ .split("\n")
567
+ .filter((s) => s);
568
+ for (let i = 0; i < rows.length; i++) {
569
+ for (let j = 0; j < rows[i].length; j++) {
570
+ if (rows[i][j] !== ".") {
571
571
  instance.rectfill(
572
- x + col,
573
- y + row,
572
+ x + j,
573
+ y + i,
574
574
  1,
575
575
  1,
576
- parseInt(char, 36) || 0,
576
+ parseInt(rows[i][j], 36) || 0,
577
577
  );
578
578
  }
579
579
  }
@@ -877,6 +877,9 @@
877
877
  );
878
878
  return internals[index];
879
879
  },
880
+ ispaused() {
881
+ return _paused;
882
+ },
880
883
  pause() {
881
884
  if (!_paused) {
882
885
  _paused = true;
@@ -895,9 +898,6 @@
895
898
  instance.emit("resumed");
896
899
  }
897
900
  },
898
- ispaused() {
899
- return _paused;
900
- },
901
901
  quit() {
902
902
  instance.emit("quit");
903
903
  instance.pause();
@@ -930,6 +930,7 @@
930
930
  }
931
931
  }
932
932
  function init() {
933
+ resizeCanvas();
933
934
  if (settings.autoscale) {
934
935
  on(root, "resize", resizeCanvas);
935
936
  }
@@ -1109,12 +1110,11 @@
1109
1110
  );
1110
1111
  _ctx = _canvas.getContext("2d");
1111
1112
  on(_canvas, "click", () => focus());
1112
- resizeCanvas();
1113
1113
  if (!_canvas.parentNode) {
1114
1114
  d.body.appendChild(_canvas);
1115
1115
  }
1116
- _canvas.style.imageRendering = "pixelated";
1117
1116
  _canvas.oncontextmenu = () => false;
1117
+ resizeCanvas();
1118
1118
  }
1119
1119
  function resizeCanvas() {
1120
1120
  DEV: assert(
@@ -1131,6 +1131,11 @@
1131
1131
  null == settings.height || (settings.width > 0 && settings.height > 0),
1132
1132
  'litecanvas() option "width" is required when the option "height" is defined',
1133
1133
  );
1134
+ DEV: assert(
1135
+ "boolean" === typeof settings.autoscale ||
1136
+ (isNumber(settings.autoscale) && settings.autoscale > 1),
1137
+ 'litecanvas() option "autoscale" must be boolean or a number > 1',
1138
+ );
1134
1139
  const width = settings.width > 0 ? settings.width : innerWidth,
1135
1140
  height =
1136
1141
  settings.width > 0 ? settings.height || settings.width : innerHeight;
@@ -1138,6 +1143,7 @@
1138
1143
  instance.def("H", height);
1139
1144
  _canvas.width = width;
1140
1145
  _canvas.height = height;
1146
+ _canvas.style = "image-rendering:pixelated";
1141
1147
  if (settings.autoscale) {
1142
1148
  let maxScale = +settings.autoscale;
1143
1149
  if (!_canvas.style.display) {
package/dist/dist.js CHANGED
@@ -116,7 +116,7 @@
116
116
  const root = window,
117
117
  math = Math,
118
118
  perf = performance,
119
- TWO_PI = math.PI * 2,
119
+ TAU = math.PI * 2,
120
120
  raf = requestAnimationFrame,
121
121
  isNumber = Number.isFinite,
122
122
  _browserEventListeners = [],
@@ -168,8 +168,7 @@
168
168
  T: 0,
169
169
  MX: -1,
170
170
  MY: -1,
171
- TWO_PI,
172
- HALF_PI: TWO_PI / 4,
171
+ TAU,
173
172
  lerp: (start, end, t) => {
174
173
  return start + t * (end - start);
175
174
  },
@@ -246,12 +245,12 @@
246
245
  },
247
246
  oval(x, y, radiusX, radiusY, color) {
248
247
  beginPath(_ctx);
249
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
248
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU);
250
249
  instance.stroke(color);
251
250
  },
252
251
  ovalfill(x, y, radiusX, radiusY, color) {
253
252
  beginPath(_ctx);
254
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
253
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU);
255
254
  instance.fill(color);
256
255
  },
257
256
  circ(x, y, radius, color) {
@@ -316,18 +315,19 @@
316
315
  _ctx.drawImage(source, ~~x, ~~y);
317
316
  },
318
317
  spr(x, y, pixels) {
319
- const rows = pixels.trim().split("\n");
320
- for (let row = 0; row < rows.length; row++) {
321
- const chars = rows[row].trim();
322
- for (let col = 0; col < chars.length; col++) {
323
- const char = chars[col];
324
- if (char !== "." && char !== " ") {
318
+ const rows = pixels
319
+ .replace(/[^\w.\n]/g, "")
320
+ .split("\n")
321
+ .filter((s) => s);
322
+ for (let i = 0; i < rows.length; i++) {
323
+ for (let j = 0; j < rows[i].length; j++) {
324
+ if (rows[i][j] !== ".") {
325
325
  instance.rectfill(
326
- x + col,
327
- y + row,
326
+ x + j,
327
+ y + i,
328
328
  1,
329
329
  1,
330
- parseInt(char, 36) || 0,
330
+ parseInt(rows[i][j], 36) || 0,
331
331
  );
332
332
  }
333
333
  }
@@ -485,6 +485,9 @@
485
485
  ];
486
486
  return internals[index];
487
487
  },
488
+ ispaused() {
489
+ return _paused;
490
+ },
488
491
  pause() {
489
492
  if (!_paused) {
490
493
  _paused = true;
@@ -499,9 +502,6 @@
499
502
  instance.emit("resumed");
500
503
  }
501
504
  },
502
- ispaused() {
503
- return _paused;
504
- },
505
505
  quit() {
506
506
  instance.emit("quit");
507
507
  instance.pause();
@@ -531,6 +531,7 @@
531
531
  }
532
532
  }
533
533
  function init() {
534
+ resizeCanvas();
534
535
  if (settings.autoscale) {
535
536
  on(root, "resize", resizeCanvas);
536
537
  }
@@ -694,12 +695,11 @@
694
695
  _canvas = _canvas || d.createElement("canvas");
695
696
  _ctx = _canvas.getContext("2d");
696
697
  on(_canvas, "click", () => focus());
697
- resizeCanvas();
698
698
  if (!_canvas.parentNode) {
699
699
  d.body.appendChild(_canvas);
700
700
  }
701
- _canvas.style.imageRendering = "pixelated";
702
701
  _canvas.oncontextmenu = () => false;
702
+ resizeCanvas();
703
703
  }
704
704
  function resizeCanvas() {
705
705
  const width = settings.width > 0 ? settings.width : innerWidth,
@@ -709,6 +709,7 @@
709
709
  instance.def("H", height);
710
710
  _canvas.width = width;
711
711
  _canvas.height = height;
712
+ _canvas.style = "image-rendering:pixelated";
712
713
  if (settings.autoscale) {
713
714
  let maxScale = +settings.autoscale;
714
715
  if (!_canvas.style.display) {
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,l=0,n=0,i=.1,o=0,r=1,s=0,f=0,d=0,c=0,p=0,u=0,h=0,m=0,g=0,v=1,x=0,w=0,y=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,E=a*=(1-t+2*t*b.random(t=[]))*z/44100,T=0,P=0,C=0,I=1,L=0,D=0,A=0,S=y<0?-1:1,H=z*S*y*2/44100,M=b.cos(H),N=b.sin,W=N(H)/4,X=1+W,q=-2*M/X,B=(1-W)/X,V=(1+S*M)/2/X,O=-(S+M)/X,R=0,F=0,G=0,Y=0;for(l=44100*l+9,x*=44100,n*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,p=44100*p|0,e*=.3*zzfxV,S=l+x+n+i+g|0;C<S;t[C++]=A*e)++D%(100*m|0)||(A=o?1<o?2<o?3<o?N(T*T):b.max(b.min(b.tan(T),1),-1):1-(2*T/z%2+2)%2:1-4*b.abs(b.round(T/z)-T/z):N(T),A=(p?1-w+w*N(z*C/p):1)*(A<0?-1:1)*b.abs(A)**r*(C<l?C/l:C<l+x?1-(C-l)/x*(1-v):C<l+x+n?v:C<S-g?(S-C-g)/i*v:0),A=g?A/2+(g>C?0:(C<S-g?1:(S-C)/g)*t[C-g|0]/2/e):A,y&&(A=Y=V*R+O*(R=F)+V*(F=A)-B*G-q*(G=Y))),T+=(H=(a+=s+=f)*b.cos(h*P++))+H*u*N(C**5),I&&++I>c&&(a+=d,E+=d,I=0),!p||++L%p||(a=E,s=k,I=I||1);(e=zzfxX.createBuffer(1,S,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),c=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p=!1,u,h,m=1,g,v=.5,x=1,w,y=1e3/60,b,z=0,k=3,E="sans-serif",T=20,P=1.2,C=Date.now(),I=e,L=[],D=[.5,0,1750,,,.3,1,,,,600,.1],A={},S={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:o,HALF_PI:o/4,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,mod:(e,t)=>(e%t+t)%t||0,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,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,l,n,i){let o=(e-t)/(a-t)*(n-l)+l;return i?S.clamp(o,l,n):o},norm:(e,t,a)=>S.map(e,t,a,0,1),rand:(e=0,t=1)=>(C=(1664525*C+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~S.rand(e,t+1),rseed(e){C=~~e},cls(e){null==e?g.clearRect(0,0,S.W,S.H):S.rectfill(0,0,S.W,S.H,e)},rect(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),S.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),S.fill(n)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),S.fill(n)},circ(e,t,a,l){S.oval(e,t,a,a,l)},circfill(e,t,a,l){S.ovalfill(e,t,a,a,l)},shape(e){g.beginPath();for(let t=0;t<e.length;t+=2)t?g.lineTo(~~e[t],~~e[t+1]):g.moveTo(~~e[t],~~e[t+1]);g.lineTo(~~e[0],~~e[1])},line(e,t,a,l,n){g.beginPath();let i=v&&~~e==~~a?.5:0,o=v&&~~t==~~l?.5:0;g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),S.stroke(n)},linewidth(e){g.lineWidth=~~e,v=~~e%2?.5:0},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${T}px ${E}`,g.fillStyle=X(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+T*P*a)},textgap(e){P=e},textfont(e){E=e},textsize(e){T=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){let l=a.trim().split("\n");for(let a=0;a<l.length;a++){let n=l[a].trim();for(let l=0;l<n.length;l++){let i=n[l];"."!==i&&" "!==i&&S.rectfill(e+l,t+a,1,1,parseInt(i,36)||0)}}},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(e=0,t=e,a=0,l=1,n=l){g.save(),S.translate(e,t),S.rotate(a),S.scale(l,n)},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t=e){g.scale(e,t)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=S.clamp(e,0,1)},fill(e){g.fillStyle=X(e),g.fill()},stroke(e){g.strokeStyle=X(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=D,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>h,use(e,t={}){var a=e,l=t;let n=a(S,l);for(let e in n)S.def(e,n[e])},listen:(e,t)=>{A[e=e.toLowerCase()]=A[e]||new Set,A[e].add(t)},unlisten:(e,t)=>{A[e=e.toLowerCase()]&&A[e].delete(t)},emit:(e,t,a,n,i)=>(p&&(W("before:"+(e=e.toLowerCase()),t,a,n,i),c||l[e]===S[e]||"function"!=typeof l[e]||l[e](t,a,n,i),W(e,t,a,n,i),W("after:"+e,t,a,n,i)),t),pal(t,a=3){I=t||e,L=[],k=a,S.emit("pal",I,k)},palc(e,t){null==e?L=[]:L[e]=t},def(e,a){S[e]=a,t.global&&(l[e]=a)},timescale(e){x=e},framerate(e){y=1e3/~~e},stat:e=>[t,p,y/1e3,m,A,I,D,x,l.zzfxV,C,T,E,L,P][e],pause(){u||(u=!0,z=~~cancelAnimationFrame(z),S.emit("paused"))},resume(){p&&u&&(H(),u=!1,S.emit("resumed"))},ispaused:()=>u,quit(){for(let e of(S.emit("quit"),S.pause(),p=!1,A={},s))e();if(t.global){for(let e in S)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(","))S[e]=n[e];function H(){z||(b=0,w=i.now(),z=r(M))}function M(){z=r(M);let e=i.now(),t=0,a=e-w;for(w=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*x;S.emit("update",e,t),S.def("T",S.T+e)}t&&(S.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(S.def("W",e),S.def("H",a),h.width=e,h.height=a,t.autoscale){let l=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=n.min(innerWidth/e,innerHeight/a),m=l>1&&m>l?l:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,S.textalign("start","top"),S.emit("resized",m)}function W(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function X(e){return I[~~(L[e]??e)%I.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,S),l.ENGINE=S}if(a=document,g=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),f(h,"click",()=>focus()),N(),h.parentNode||a.body.appendChild(h),h.style.imageRendering="pixelated",h.oncontextmenu=()=>!1,c)for(let e in c)c[e]&&S.listen(e,c[e]);return r(function(){if(t.autoscale&&f(l,"resize",N),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(h,"mousedown",t=>{if(!t.button){t.preventDefault();let[l,n]=e(t);S.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(h,"mouseup",a=>{if(!a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&S.emit("tapped",l.xi,l.yi,0),S.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);S.def("MX",a),S.def("MY",l),r&&(S.emit("tapping",a,l,0),n(0,a,l))}),f(h,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);S.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(h,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);S.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{e.preventDefault();let a=[];for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(o(l)&&S.emit("tapped",l.xi,l.yi,e),S.emit("untap",l.x,l.y,e),t.delete(e))};f(h,"touchend",s),f(h,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))S.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),S.listen("after:update",()=>t.clear()),S.def("iskeydown",t=>a(e,t)),S.def("iskeypressed",e=>a(t,e)),S.def("lastkey",()=>n)}p=!0,S.emit("init",S),u||H()}),S}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"];window.litecanvas=function(t={}){let a,l=window,n=Math,i=performance,o=2*n.PI,r=requestAnimationFrame,s=[],f=(e,t,a)=>{e.addEventListener(t,a,!1),s.push(()=>e.removeEventListener(t,a,!1))},d=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,l=0,n=0,i=.1,o=0,r=1,s=0,f=0,d=0,c=0,p=0,u=0,h=0,m=0,g=0,v=1,w=0,x=0,y=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,E=a*=(1-t+2*t*b.random(t=[]))*z/44100,T=0,C=0,P=0,I=1,D=0,L=0,S=0,A=y<0?-1:1,M=z*A*y*2/44100,N=b.cos(M),W=b.sin,X=W(M)/4,H=1+X,q=-2*N/H,B=(1-X)/H,V=(1+A*N)/2/H,O=-(A+N)/H,G=0,R=0,Y=0,$=0;for(l=44100*l+9,w*=44100,n*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,p=44100*p|0,e*=.3*zzfxV,A=l+w+n+i+g|0;P<A;t[P++]=S*e)++L%(100*m|0)||(S=o?1<o?2<o?3<o?W(T*T):b.max(b.min(b.tan(T),1),-1):1-(2*T/z%2+2)%2:1-4*b.abs(b.round(T/z)-T/z):W(T),S=(p?1-x+x*W(z*P/p):1)*(S<0?-1:1)*b.abs(S)**r*(P<l?P/l:P<l+w?1-(P-l)/w*(1-v):P<l+w+n?v:P<A-g?(A-P-g)/i*v:0),S=g?S/2+(g>P?0:(P<A-g?1:(A-P)/g)*t[P-g|0]/2/e):S,y&&(S=$=V*G+O*(G=R)+V*(R=S)-B*Y-q*(Y=$))),T+=(M=(a+=s+=f)*b.cos(h*C++))+M*u*W(P**5),I&&++I>c&&(a+=d,E+=d,I=0),!p||++D%p||(a=E,s=k,I=I||1);(e=zzfxX.createBuffer(1,A,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),c=(t=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},t)).loop,p=!1,u,h,m=1,g,v=.5,w=1,x,y=1e3/60,b,z=0,k=3,E="sans-serif",T=20,C=1.2,P=Date.now(),I=e,D=[],L=[.5,0,1750,,,.3,1,,,,600,.1],S={},A={W:0,H:0,T:0,MX:-1,MY:-1,TAU:o,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>n.PI/180*e,rad2deg:e=>180/n.PI*e,mod:(e,t)=>(e%t+t)%t||0,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,dist:(e,t,a,l)=>n.hypot(a-e,l-t),wrap:(e,t,a)=>e-(a-t)*n.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),rand:(e=0,t=1)=>(P=(1664525*P+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>~~A.rand(e,t+1),rseed(e){P=~~e},cls(e){null==e?g.clearRect(0,0,A.W,A.H):A.rectfill(0,0,A.W,A.H,e)},rect(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-v,~~t-v,~~a+2*v,~~l+2*v,i),A.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),A.fill(n)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),A.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),A.fill(n)},circ(e,t,a,l){A.oval(e,t,a,a,l)},circfill(e,t,a,l){A.ovalfill(e,t,a,a,l)},shape(e){g.beginPath();for(let t=0;t<e.length;t+=2)t?g.lineTo(~~e[t],~~e[t+1]):g.moveTo(~~e[t],~~e[t+1]);g.lineTo(~~e[0],~~e[1])},line(e,t,a,l,n){g.beginPath();let i=v&&~~e==~~a?.5:0,o=v&&~~t==~~l?.5:0;g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),A.stroke(n)},linewidth(e){g.lineWidth=~~e,v=~~e%2?.5:0},linedash(e,t=0){g.setLineDash(e),g.lineDashOffset=t},text(e,t,a,l=k,n="normal"){g.font=`${n} ${T}px ${E}`,g.fillStyle=H(l);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)g.fillText(i[a],~~e,~~t+T*C*a)},textgap(e){C=e},textfont(e){E=e},textsize(e){T=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){let l=a.replace(/[^\w.\n]/g,"").split("\n").filter(e=>e);for(let a=0;a<l.length;a++)for(let n=0;n<l[a].length;n++)"."!==l[a][n]&&A.rectfill(e+n,t+a,1,1,parseInt(l[a][n],36)||0)},paint(e,t,a,l={}){let n=l.canvas||new OffscreenCanvas(1,1),i=l.scale||1,o=g;return n.width=e*i,n.height=t*i,(g=n.getContext("2d")).scale(i,i),a(g),g=o,n.transferToImageBitmap()},ctx:e=>(e&&(g=e),g),push(e=0,t=e,a=0,l=1,n=l){g.save(),A.translate(e,t),A.rotate(a),A.scale(l,n)},pop(){g.restore()},translate(e,t){g.translate(~~e,~~t)},scale(e,t=e){g.scale(e,t)},rotate(e){g.rotate(e)},alpha(e){g.globalAlpha=A.clamp(e,0,1)},fill(e){g.fillStyle=H(e),g.fill()},stroke(e){g.strokeStyle=H(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=L,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),d.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>h,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=e.toLowerCase()]=S[e]||new Set,S[e].add(t)},unlisten:(e,t)=>{S[e=e.toLowerCase()]&&S[e].delete(t)},emit:(e,t,a,n,i)=>(p&&(X("before:"+(e=e.toLowerCase()),t,a,n,i),c||l[e]===A[e]||"function"!=typeof l[e]||l[e](t,a,n,i),X(e,t,a,n,i),X("after:"+e,t,a,n,i)),t),pal(t,a=3){I=t||e,D=[],k=a,A.emit("pal",I,k)},palc(e,t){null==e?D=[]:D[e]=t},def(e,a){A[e]=a,t.global&&(l[e]=a)},timescale(e){w=e},framerate(e){y=1e3/~~e},stat:e=>[t,p,y/1e3,m,S,I,L,w,l.zzfxV,P,T,E,D,C][e],ispaused:()=>u,pause(){u||(u=!0,z=~~cancelAnimationFrame(z),A.emit("paused"))},resume(){p&&u&&(M(),u=!1,A.emit("resumed"))},quit(){for(let e of(A.emit("quit"),A.pause(),p=!1,S={},s))e();if(t.global){for(let e in A)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(","))A[e]=n[e];function M(){z||(b=0,x=i.now(),z=r(N))}function N(){z=r(N);let e=i.now(),t=0,a=e-x;for(x=e,b+=a<100?a:y;b>=y;){t++,b-=y;let e=y/1e3*w;A.emit("update",e,t),A.def("T",A.T+e)}t&&(A.emit("draw",g),t>1&&(b=0))}function W(){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),h.width=e,h.height=a,h.style="image-rendering:pixelated",t.autoscale){let l=+t.autoscale;h.style.display||(h.style.display="block",h.style.margin="auto"),m=n.min(innerWidth/e,innerHeight/a),m=l>1&&m>l?l:m,h.style.width=e*m+"px",h.style.height=a*m+"px"}g.imageSmoothingEnabled=!1,A.textalign("start","top"),A.emit("resized",m)}function X(e,t,a,l,n){if(S[e])for(let i of S[e])i(t,a,l,n)}function H(e){return I[~~(D[e]??e)%I.length]}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,A),l.ENGINE=A}if(a=document,g=(h=(h="string"==typeof t.canvas?a.querySelector(t.canvas):t.canvas)||a.createElement("canvas")).getContext("2d"),f(h,"click",()=>focus()),h.parentNode||a.body.appendChild(h),h.oncontextmenu=()=>!1,W(),c)for(let e in c)c[e]&&A.listen(e,c[e]);return r(function(){if(W(),t.autoscale&&f(l,"resize",W),t.tapEvents){let e=e=>[(e.pageX-h.offsetLeft)/m,(e.pageY-h.offsetTop)/m],t=new Map,a=(e,a,l)=>{let n={x:a,y:l,xi:a,yi:l,t:i.now()};return t.set(e,n),n},n=(e,l,n)=>{let i=t.get(e)||a(e);i.x=l,i.y=n},o=e=>e&&i.now()-e.t<=300,r=!1;f(h,"mousedown",t=>{if(!t.button){t.preventDefault();let[l,n]=e(t);A.emit("tap",l,n,0),a(0,l,n),r=!0}}),f(h,"mouseup",a=>{if(!a.button){a.preventDefault();let l=t.get(0),[n,i]=e(a);o(l)&&A.emit("tapped",l.xi,l.yi,0),A.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);A.def("MX",a),A.def("MY",l),r&&(A.emit("tapping",a,l,0),n(0,a,l))}),f(h,"touchstart",t=>{for(let l of(t.preventDefault(),t.changedTouches)){let[t,n]=e(l);A.emit("tap",t,n,l.identifier+1),a(l.identifier+1,t,n)}}),f(h,"touchmove",t=>{for(let a of(t.preventDefault(),t.changedTouches)){let[t,l]=e(a);A.emit("tapping",t,l,a.identifier+1),n(a.identifier+1,t,l)}});let s=e=>{e.preventDefault();let a=[];for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,l]of t)a.includes(e)||(o(l)&&A.emit("tapped",l.xi,l.yi,e),A.emit("untap",l.x,l.y,e),t.delete(e))};f(h,"touchend",s),f(h,"touchcancel",s),f(l,"blur",()=>{for(let[e,a]of(r=!1,t))A.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";f(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),f(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),f(l,"blur",()=>e.clear()),A.listen("after:update",()=>t.clear()),A.def("iskeydown",t=>a(e,t)),A.def("iskeypressed",e=>a(t,e)),A.def("lastkey",()=>n)}p=!0,A.emit("init",A),u||M()}),A}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.208.0",
3
+ "version": "0.300.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and p5.js/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
@@ -34,13 +34,13 @@
34
34
  "devDependencies": {
35
35
  "@happy-dom/global-registrator": "^20.9.0",
36
36
  "@size-limit/preset-small-lib": "^12.1.0",
37
- "@swc/core": "^1.15.33",
38
- "ava": "^7.0.0",
37
+ "@swc/core": "^1.15.40",
38
+ "ava": "^8.0.1",
39
39
  "esbuild": "^0.28.0",
40
40
  "genversion": "^3.2.0",
41
41
  "gzip-size": "^7.0.0",
42
42
  "prettier": "^3.8.3",
43
- "sinon": "^21.1.2",
43
+ "sinon": "^22.0.0",
44
44
  "size-limit": "^12.1.0",
45
45
  "tap-min": "^3.0.0"
46
46
  },
package/src/index.js CHANGED
@@ -14,7 +14,7 @@ export default function litecanvas(settings = {}) {
14
14
  const root = window,
15
15
  math = Math,
16
16
  perf = performance,
17
- TWO_PI = math.PI * 2,
17
+ TAU = math.PI * 2,
18
18
  raf = requestAnimationFrame,
19
19
  isNumber = Number.isFinite,
20
20
  /** @type {Function[]} */
@@ -117,20 +117,9 @@ export default function litecanvas(settings = {}) {
117
117
  * Twice the value of the mathematical constant PI (π).
118
118
  * Approximately 6.28318
119
119
  *
120
- * Note: TWO_PI radians equals 360°, PI radians equals 180°,
121
- * HALF_PI radians equals 90°, and HALF_PI/2 radians equals 45°.
122
- *
123
120
  * @type {number}
124
121
  */
125
- TWO_PI,
126
-
127
- /**
128
- * Half the value of the mathematical constant PI (π).
129
- * Approximately 1.57079
130
- *
131
- * @type {number}
132
- */
133
- HALF_PI: TWO_PI / 4,
122
+ TAU,
134
123
 
135
124
  /**
136
125
  * Calculates a linear (interpolation) value over t%.
@@ -181,9 +170,6 @@ export default function litecanvas(settings = {}) {
181
170
  * @param {number} a dividend
182
171
  * @param {number} b divisor
183
172
  * @returns {number} the remainder
184
- * @example
185
- * mod(-1, 5) // => 4
186
- * -1 % 5 // => -1
187
173
  */
188
174
  mod(a, b) {
189
175
  DEV: assert(isNumber(a), 'mod() 1st parameter must be a number')
@@ -499,7 +485,7 @@ export default function litecanvas(settings = {}) {
499
485
 
500
486
  beginPath(_ctx)
501
487
 
502
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
488
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU)
503
489
  instance.stroke(color)
504
490
  },
505
491
 
@@ -530,7 +516,7 @@ export default function litecanvas(settings = {}) {
530
516
 
531
517
  beginPath(_ctx)
532
518
 
533
- _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
519
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TAU)
534
520
  instance.fill(color)
535
521
  },
536
522
 
@@ -776,7 +762,11 @@ export default function litecanvas(settings = {}) {
776
762
  },
777
763
 
778
764
  /**
779
- * Draw a sprite pixel by pixel represented by a string. Each pixel must be a base 36 number (0-9 or a-z) or a dot.
765
+ * Draw a sprite, using a string of rows and columns representing a bitmask.
766
+ * - Each colored pixel must be a base 36 number (0-9 or a-z).
767
+ * - Use "." (dot) for transparent pixels.
768
+ * - Any other characters (like symbols) are ignored.
769
+ * - empty lines are ignored
780
770
  *
781
771
  * @param {number} x
782
772
  * @param {number} y
@@ -787,14 +777,15 @@ export default function litecanvas(settings = {}) {
787
777
  DEV: assert(isNumber(y), 'spr() 2nd parameter must be a number')
788
778
  DEV: assert('string' === typeof pixels, 'spr() 3rd parameter must be a string')
789
779
 
790
- const rows = pixels.trim().split('\n')
780
+ const rows = pixels
781
+ .replace(/[^\w.\n]/g, '')
782
+ .split('\n')
783
+ .filter((s) => s)
791
784
 
792
- for (let row = 0; row < rows.length; row++) {
793
- const chars = rows[row].trim()
794
- for (let col = 0; col < chars.length; col++) {
795
- const char = chars[col]
796
- if (char !== '.' && char !== ' ') {
797
- instance.rectfill(x + col, y + row, 1, 1, parseInt(char, 36) || 0)
785
+ for (let i = 0; i < rows.length; i++) {
786
+ for (let j = 0; j < rows[i].length; j++) {
787
+ if (rows[i][j] !== '.') {
788
+ instance.rectfill(x + j, y + i, 1, 1, parseInt(rows[i][j], 36) || 0)
798
789
  }
799
790
  }
800
791
  }
@@ -1331,6 +1322,15 @@ export default function litecanvas(settings = {}) {
1331
1322
  return internals[index]
1332
1323
  },
1333
1324
 
1325
+ /**
1326
+ * Returns `true` if the engine loop is paused.
1327
+ *
1328
+ * @returns {boolean}
1329
+ */
1330
+ ispaused() {
1331
+ return _paused
1332
+ },
1333
+
1334
1334
  /**
1335
1335
  * Pauses the engine loop (update & draw).
1336
1336
  */
@@ -1358,15 +1358,6 @@ export default function litecanvas(settings = {}) {
1358
1358
  }
1359
1359
  },
1360
1360
 
1361
- /**
1362
- * Returns `true` if the engine loop is paused.
1363
- *
1364
- * @returns {boolean}
1365
- */
1366
- ispaused() {
1367
- return _paused
1368
- },
1369
-
1370
1361
  /**
1371
1362
  * Shutdown the litecanvas instance and remove all event listeners.
1372
1363
  */
@@ -1416,6 +1407,8 @@ export default function litecanvas(settings = {}) {
1416
1407
  }
1417
1408
 
1418
1409
  function init() {
1410
+ resizeCanvas()
1411
+
1419
1412
  // listen window resize event when "autoscale" is enabled
1420
1413
  if (settings.autoscale) {
1421
1414
  on(root, 'resize', resizeCanvas)
@@ -1743,16 +1736,14 @@ export default function litecanvas(settings = {}) {
1743
1736
 
1744
1737
  on(_canvas, 'click', () => focus())
1745
1738
 
1746
- resizeCanvas()
1747
-
1748
1739
  if (!_canvas.parentNode) {
1749
1740
  d.body.appendChild(_canvas)
1750
1741
  }
1751
1742
 
1752
- _canvas.style.imageRendering = 'pixelated'
1753
-
1754
1743
  // disable default browser's right click in canvas
1755
1744
  _canvas.oncontextmenu = () => false
1745
+
1746
+ resizeCanvas()
1756
1747
  }
1757
1748
 
1758
1749
  function resizeCanvas() {
@@ -1769,6 +1760,11 @@ export default function litecanvas(settings = {}) {
1769
1760
 
1770
1761
  'litecanvas() option "width" is required when the option "height" is defined'
1771
1762
  )
1763
+ DEV: assert(
1764
+ 'boolean' === typeof settings.autoscale ||
1765
+ (isNumber(settings.autoscale) && settings.autoscale > 1),
1766
+ 'litecanvas() option "autoscale" must be boolean or a number > 1'
1767
+ )
1772
1768
 
1773
1769
  const width = settings.width > 0 ? settings.width : innerWidth,
1774
1770
  height = settings.width > 0 ? settings.height || settings.width : innerHeight
@@ -1779,6 +1775,9 @@ export default function litecanvas(settings = {}) {
1779
1775
  _canvas.width = width
1780
1776
  _canvas.height = height
1781
1777
 
1778
+ /** @ts-ignore */
1779
+ _canvas.style = 'image-rendering:pixelated'
1780
+
1782
1781
  if (settings.autoscale) {
1783
1782
  let maxScale = +settings.autoscale
1784
1783
  if (!_canvas.style.display) {
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.208.0'
2
+ export const version = '0.300.0'
package/types/global.d.ts CHANGED
@@ -25,16 +25,8 @@ declare global {
25
25
  /**
26
26
  * Twice the value of the mathematical constant PI (π).
27
27
  * Approximately 6.28318
28
- *
29
- * Note: TWO_PI radians equals 360°, PI radians equals 180°,
30
- * HALF_PI radians equals 90°, and HALF_PI/2 radians equals 45°.
31
- */
32
- var TWO_PI: number
33
- /**
34
- * Half the value of the mathematical constant PI (π).
35
- * Approximately 1.57079
36
28
  */
37
- var HALF_PI: number
29
+ var TAU: number
38
30
  /**
39
31
  * Calculates a linear (interpolation) value over t%.
40
32
  *
@@ -68,8 +60,8 @@ declare global {
68
60
  * @param b divisor
69
61
  * @returns the remainder
70
62
  * @example
71
- * mod(-1, 5) // => 4
72
- * -1 % 5 // => -1
63
+ * mod(-1, 5) // => 4
64
+ * -1 % 5 // => -1
73
65
  */
74
66
  function mod(a: number, b: number): number
75
67
  /**
@@ -401,15 +393,32 @@ declare global {
401
393
  */
402
394
  function image(x: number, y: number, source: CanvasImageSource): void
403
395
  /**
404
- * Draw a sprite pixel by pixel represented by a string. Each pixel must be a base 36 number or a dot:
405
- *
406
- * - A base 36 number (`0-9` or `a-z`) represent a pixel color (supporting color palettes with max 36 colors).
407
- * - A dot (`.`) represent a transparent pixel.
408
- * - Spaces are ignored and can be used to improve the visualization.
396
+ * Draw a sprite, using a string of rows and columns representing a bitmask.
397
+ * - Each colored pixel must be a base 36 number (0-9 or a-z).
398
+ * - Use "." (dot) for transparent pixels.
399
+ * - Any other characters (like symbols) are ignored.
400
+ * - empty lines are ignored
409
401
  *
410
- * @param x the position X of the first pixel
411
- * @param y the position Y of the first pixel
402
+ * @param x
403
+ * @param y
412
404
  * @param pixels
405
+ * @see https://litecanvas.js.org/tools/pixel-art-editor.html
406
+ * @example
407
+ * function draw() {
408
+ * // a little white key 8x8 sprite
409
+ * const littleKeySprite = `
410
+ * ........
411
+ * .3......
412
+ * 323.....
413
+ * 3.333333
414
+ * 3.322323
415
+ * 232..2.2
416
+ * .2......
417
+ * ........
418
+ * `
419
+ * // draw the sprite pixels at position x=10, y=10
420
+ * spr(10, 10, littleKeySprite)
421
+ * }
413
422
  */
414
423
  function spr(x: number, y: number, pixels: string): void
415
424
  /**
package/types/types.d.ts CHANGED
@@ -19,16 +19,8 @@ type LitecanvasInstance = {
19
19
  /**
20
20
  * Twice the value of the mathematical constant PI (π).
21
21
  * Approximately 6.28318
22
- *
23
- * Note: TWO_PI radians equals 360°, PI radians equals 180°,
24
- * HALF_PI radians equals 90°, and HALF_PI/2 radians equals 45°.
25
- */
26
- TWO_PI: number
27
- /**
28
- * Half the value of the mathematical constant PI (π).
29
- * Approximately 1.57079
30
22
  */
31
- HALF_PI: number
23
+ TAU: number
32
24
  /**
33
25
  * Calculates a linear (interpolation) value over t%.
34
26
  *
@@ -62,8 +54,8 @@ type LitecanvasInstance = {
62
54
  * @param b divisor
63
55
  * @returns the remainder
64
56
  * @example
65
- * mod(-1, 5) // => 4
66
- * -1 % 5 // => -1
57
+ * mod(-1, 5) // => 4
58
+ * -1 % 5 // => -1
67
59
  */
68
60
  mod(a: number, b: number): number
69
61
  /**
@@ -389,15 +381,32 @@ type LitecanvasInstance = {
389
381
  */
390
382
  image(x: number, y: number, source: CanvasImageSource): void
391
383
  /**
392
- * Draw a sprite pixel by pixel represented by a string. Each pixel must be a base 36 number or a dot:
393
- *
394
- * - A base 36 number (`0-9` or `a-z`) represent a pixel color (supporting color palettes with max 36 colors).
395
- * - A dot (`.`) represent a transparent pixel.
396
- * - Spaces are ignored and can be used to improve the visualization.
384
+ * Draw a sprite, using a string of rows and columns representing a bitmask.
385
+ * - Each colored pixel must be a base 36 number (0-9 or a-z).
386
+ * - Use "." (dot) for transparent pixels.
387
+ * - Any other characters (like symbols) are ignored.
388
+ * - empty lines are ignored
397
389
  *
398
- * @param x the position X of the first pixel
399
- * @param y the position Y of the first pixel
390
+ * @param x
391
+ * @param y
400
392
  * @param pixels
393
+ * @see https://litecanvas.js.org/tools/pixel-art-editor.html
394
+ * @example
395
+ * function draw() {
396
+ * // a little white key 8x8 sprite
397
+ * const littleKeySprite = `
398
+ * ........
399
+ * .3......
400
+ * 323.....
401
+ * 3.333333
402
+ * 3.322323
403
+ * 232..2.2
404
+ * .2......
405
+ * ........
406
+ * `
407
+ * // draw the sprite pixels at position x=10, y=10
408
+ * spr(10, 10, littleKeySprite)
409
+ * }
401
410
  */
402
411
  spr(x: number, y: number, pixels: string): void
403
412
  /**