litecanvas 0.400.0 → 0.402.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
@@ -115,7 +115,15 @@
115
115
  var assert = (condition, message = "Assertion failed") => {
116
116
  if (!condition) throw new Error("[Litecanvas] " + message);
117
117
  };
118
- var version = "0.400.0";
118
+ var version = "0.402.0";
119
+ var mulberry32 = (seed) => {
120
+ return () => {
121
+ let t = (seed += 1831565813);
122
+ t = Math.imul(t ^ (t >>> 15), t | 1);
123
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
124
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
125
+ };
126
+ };
119
127
  function litecanvas(settings = {}) {
120
128
  const root = window,
121
129
  math = Math,
@@ -166,6 +174,7 @@
166
174
  _fontSize = 20,
167
175
  _fontLineHeight = 1.2,
168
176
  _rngSeed = Date.now(),
177
+ _rngNext = mulberry32(_rngSeed >>> 0),
169
178
  _currentPalette = defaultPalette,
170
179
  _lastPalette = defaultPalette,
171
180
  _colorPaletteState = [],
@@ -177,6 +186,32 @@
177
186
  T: 0,
178
187
  MX: -1,
179
188
  MY: -1,
189
+ rand: (min = 0, max = 1) => {
190
+ DEV: assert(isNumber(min), "rand() 1st argument must be a number");
191
+ DEV: assert(isNumber(max), "rand() 2nd argument must be a number");
192
+ DEV: assert(
193
+ max >= min,
194
+ "rand() the 1st argument must be less than the 2nd argument",
195
+ );
196
+ return min + _rngNext() * (max - min);
197
+ },
198
+ randi: (min = 0, max = 1) => {
199
+ DEV: assert(isNumber(min), "randi() 1st argument must be a number");
200
+ DEV: assert(isNumber(max), "randi() 2nd argument must be a number");
201
+ DEV: assert(
202
+ max >= min,
203
+ "randi() the 1st argument must be less than the 2nd argument",
204
+ );
205
+ return ~~instance.rand(min, max + 1);
206
+ },
207
+ rseed(value) {
208
+ DEV: assert(
209
+ isNumber(value) && value >= 0,
210
+ "rseed() 1st argument must be a non-negative integer",
211
+ );
212
+ _rngSeed = value;
213
+ _rngNext = mulberry32(_rngSeed >>> 0);
214
+ },
180
215
  TAU,
181
216
  lerp: (start, end, t) => {
182
217
  DEV: assert(isNumber(start), "lerp() 1st argument must be a number");
@@ -265,35 +300,6 @@
265
300
  );
266
301
  return instance.map(value, start, stop, 0, 1);
267
302
  },
268
- rand: (min = 0, max = 1) => {
269
- DEV: assert(isNumber(min), "rand() 1st argument must be a number");
270
- DEV: assert(isNumber(max), "rand() 2nd argument must be a number");
271
- DEV: assert(
272
- max >= min,
273
- "rand() the 1st argument must be less than the 2nd argument",
274
- );
275
- const a = 1664525;
276
- const c = 1013904223;
277
- const m = 4294967296;
278
- _rngSeed = (a * _rngSeed + c) % m;
279
- return (_rngSeed / m) * (max - min) + min;
280
- },
281
- randi: (min = 0, max = 1) => {
282
- DEV: assert(isNumber(min), "randi() 1st argument must be a number");
283
- DEV: assert(isNumber(max), "randi() 2nd argument must be a number");
284
- DEV: assert(
285
- max >= min,
286
- "randi() the 1st argument must be less than the 2nd argument",
287
- );
288
- return ~~instance.rand(min, max + 1);
289
- },
290
- rseed(value) {
291
- DEV: assert(
292
- isNumber(value) && value >= 0,
293
- "rseed() 1st argument must be a non-negative integer",
294
- );
295
- _rngSeed = ~~value;
296
- },
297
303
  cls(color) {
298
304
  DEV: assert(
299
305
  null == color ||
@@ -990,77 +996,56 @@
990
996
  },
991
997
  _checkTapped = (tap) => tap && perf.now() - tap.t <= 300;
992
998
  let _pressingMouse = false;
993
- on(_canvas, "mousedown", (ev) => {
994
- if (!ev.button) {
995
- preventDefault(ev);
996
- const [x, y] = _getXY(ev);
997
- instance.emit("tap", x, y, 0);
998
- _registerTap(0, x, y);
999
+ on(_canvas, "pointerdown", (ev) => {
1000
+ const [x, y] = _getXY(ev);
1001
+ if ("mouse" === ev.pointerType) {
1002
+ if (ev.button) return;
999
1003
  _pressingMouse = true;
1000
1004
  }
1005
+ const id = ev.pointerId;
1006
+ _registerTap(id, x, y);
1007
+ instance.emit("tap", x, y, id);
1008
+ _canvas.setPointerCapture(id);
1001
1009
  });
1002
- on(_canvas, "mouseup", (ev) => {
1003
- if (!ev.button) {
1004
- preventDefault(ev);
1005
- const tap = _taps.get(0);
1006
- const [x, y] = _getXY(ev);
1007
- if (_checkTapped(tap)) {
1008
- instance.emit("tapped", tap.xi, tap.yi, 0);
1009
- }
1010
- instance.emit("untap", x, y, 0);
1011
- _taps.delete(0);
1012
- _pressingMouse = false;
1013
- }
1014
- });
1015
- on(root, "mousemove", (ev) => {
1016
- preventDefault(ev);
1010
+ on(_canvas, "pointermove", (ev) => {
1017
1011
  const [x, y] = _getXY(ev);
1018
- instance.def("MX", x);
1019
- instance.def("MY", y);
1020
- if (!_pressingMouse) return;
1021
- instance.emit("tapping", x, y, 0);
1022
- _updateTap(0, x, y);
1023
- });
1024
- on(_canvas, "touchstart", (ev) => {
1025
- preventDefault(ev);
1026
- const touches = ev.changedTouches;
1027
- for (const touch of touches) {
1028
- const [x, y] = _getXY(touch);
1029
- instance.emit("tap", x, y, touch.identifier + 1);
1030
- _registerTap(touch.identifier + 1, x, y);
1012
+ if ("mouse" === ev.pointerType) {
1013
+ instance.def("MX", x);
1014
+ instance.def("MY", y);
1015
+ if (!_pressingMouse) return;
1031
1016
  }
1032
- });
1033
- on(_canvas, "touchmove", (ev) => {
1034
- preventDefault(ev);
1035
- const touches = ev.changedTouches;
1036
- for (const touch of touches) {
1037
- const [x, y] = _getXY(touch);
1038
- instance.emit("tapping", x, y, touch.identifier + 1);
1039
- _updateTap(touch.identifier + 1, x, y);
1017
+ const id = ev.pointerId;
1018
+ if (_taps.get(id)) {
1019
+ instance.emit("tapping", x, y, id);
1020
+ _updateTap(id, x, y);
1040
1021
  }
1041
1022
  });
1042
- const _touchEndHandler = (ev) => {
1043
- preventDefault(ev);
1044
- const existing = [];
1045
- for (const touch of ev.targetTouches) {
1046
- existing.push(touch.identifier + 1);
1023
+ const _pointerUpHandler = (ev) => {
1024
+ const id = ev.pointerId;
1025
+ const tap = _taps.get(id);
1026
+ const [x, y] = _getXY(ev);
1027
+ if (!tap) return;
1028
+ if ("mouse" === ev.pointerType) {
1029
+ if (ev.button) return;
1030
+ _pressingMouse = false;
1047
1031
  }
1048
- for (const [id, tap] of _taps) {
1049
- if (existing.includes(id)) continue;
1050
- if (_checkTapped(tap)) {
1051
- instance.emit("tapped", tap.xi, tap.yi, id);
1052
- }
1053
- instance.emit("untap", tap.x, tap.y, id);
1054
- _taps.delete(id);
1032
+ if (_checkTapped(tap)) {
1033
+ instance.emit("tapped", tap.xi, tap.yi, id);
1055
1034
  }
1035
+ instance.emit("untap", x, y, id);
1036
+ _taps.delete(id);
1037
+ _canvas.releasePointerCapture(id);
1056
1038
  };
1057
- on(_canvas, "touchend", _touchEndHandler);
1058
- on(_canvas, "touchcancel", _touchEndHandler);
1039
+ on(_canvas, "pointerup", _pointerUpHandler);
1040
+ on(_canvas, "pointercancel", _pointerUpHandler);
1059
1041
  on(root, "blur", () => {
1060
1042
  _pressingMouse = false;
1061
1043
  for (const [id, tap] of _taps) {
1062
1044
  instance.emit("untap", tap.x, tap.y, id);
1063
1045
  _taps.delete(id);
1046
+ try {
1047
+ _canvas.releasePointerCapture(id);
1048
+ } catch (_) {}
1064
1049
  }
1065
1050
  });
1066
1051
  }
@@ -1180,7 +1165,7 @@
1180
1165
  instance.def("H", height);
1181
1166
  _canvas.width = width;
1182
1167
  _canvas.height = height;
1183
- _canvas.style = "image-rendering:pixelated";
1168
+ _canvas.style = "image-rendering:pixelated;touch-action:none";
1184
1169
  if (settings.autoscale) {
1185
1170
  let maxScale = +settings.autoscale;
1186
1171
  if (!_canvas.style.display) {
@@ -1242,3 +1227,16 @@
1242
1227
  }
1243
1228
  window.litecanvas = litecanvas;
1244
1229
  })();
1230
+ /**
1231
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
1232
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
1233
+ * The same seed always gives the same sequence.
1234
+ *
1235
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
1236
+ * @license CC0-1.0 (Public Domain)
1237
+ *
1238
+ * @param {number} seed an unsigned 32-bit integer
1239
+ * @param {number} t
1240
+ * @param {typeof Math.imul} i
1241
+ * @returns {() => number}
1242
+ */
package/dist/dist.js CHANGED
@@ -112,6 +112,14 @@
112
112
  };
113
113
  };
114
114
  var defaultPalette = ["#211e20", "#555568", "#a0a08b", "#e9efec"];
115
+ var mulberry32 = (seed) => {
116
+ return () => {
117
+ let t = (seed += 1831565813);
118
+ t = Math.imul(t ^ (t >>> 15), t | 1);
119
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
120
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
121
+ };
122
+ };
115
123
  function litecanvas(settings = {}) {
116
124
  const root = window,
117
125
  math = Math,
@@ -158,6 +166,7 @@
158
166
  _fontSize = 20,
159
167
  _fontLineHeight = 1.2,
160
168
  _rngSeed = Date.now(),
169
+ _rngNext = mulberry32(_rngSeed >>> 0),
161
170
  _currentPalette = defaultPalette,
162
171
  _lastPalette = defaultPalette,
163
172
  _colorPaletteState = [],
@@ -169,6 +178,16 @@
169
178
  T: 0,
170
179
  MX: -1,
171
180
  MY: -1,
181
+ rand: (min = 0, max = 1) => {
182
+ return min + _rngNext() * (max - min);
183
+ },
184
+ randi: (min = 0, max = 1) => {
185
+ return ~~instance.rand(min, max + 1);
186
+ },
187
+ rseed(value) {
188
+ _rngSeed = value;
189
+ _rngNext = mulberry32(_rngSeed >>> 0);
190
+ },
172
191
  TAU,
173
192
  lerp: (start, end, t) => {
174
193
  return start + t * (end - start);
@@ -208,19 +227,6 @@
208
227
  norm: (value, start, stop) => {
209
228
  return instance.map(value, start, stop, 0, 1);
210
229
  },
211
- rand: (min = 0, max = 1) => {
212
- const a = 1664525;
213
- const c = 1013904223;
214
- const m = 4294967296;
215
- _rngSeed = (a * _rngSeed + c) % m;
216
- return (_rngSeed / m) * (max - min) + min;
217
- },
218
- randi: (min = 0, max = 1) => {
219
- return ~~instance.rand(min, max + 1);
220
- },
221
- rseed(value) {
222
- _rngSeed = ~~value;
223
- },
224
230
  cls(color) {
225
231
  if (null == color) {
226
232
  _ctx.clearRect(0, 0, instance.W, instance.H);
@@ -564,77 +570,56 @@
564
570
  },
565
571
  _checkTapped = (tap) => tap && perf.now() - tap.t <= 300;
566
572
  let _pressingMouse = false;
567
- on(_canvas, "mousedown", (ev) => {
568
- if (!ev.button) {
569
- preventDefault(ev);
570
- const [x, y] = _getXY(ev);
571
- instance.emit("tap", x, y, 0);
572
- _registerTap(0, x, y);
573
+ on(_canvas, "pointerdown", (ev) => {
574
+ const [x, y] = _getXY(ev);
575
+ if ("mouse" === ev.pointerType) {
576
+ if (ev.button) return;
573
577
  _pressingMouse = true;
574
578
  }
579
+ const id = ev.pointerId;
580
+ _registerTap(id, x, y);
581
+ instance.emit("tap", x, y, id);
582
+ _canvas.setPointerCapture(id);
575
583
  });
576
- on(_canvas, "mouseup", (ev) => {
577
- if (!ev.button) {
578
- preventDefault(ev);
579
- const tap = _taps.get(0);
580
- const [x, y] = _getXY(ev);
581
- if (_checkTapped(tap)) {
582
- instance.emit("tapped", tap.xi, tap.yi, 0);
583
- }
584
- instance.emit("untap", x, y, 0);
585
- _taps.delete(0);
586
- _pressingMouse = false;
587
- }
588
- });
589
- on(root, "mousemove", (ev) => {
590
- preventDefault(ev);
584
+ on(_canvas, "pointermove", (ev) => {
591
585
  const [x, y] = _getXY(ev);
592
- instance.def("MX", x);
593
- instance.def("MY", y);
594
- if (!_pressingMouse) return;
595
- instance.emit("tapping", x, y, 0);
596
- _updateTap(0, x, y);
597
- });
598
- on(_canvas, "touchstart", (ev) => {
599
- preventDefault(ev);
600
- const touches = ev.changedTouches;
601
- for (const touch of touches) {
602
- const [x, y] = _getXY(touch);
603
- instance.emit("tap", x, y, touch.identifier + 1);
604
- _registerTap(touch.identifier + 1, x, y);
586
+ if ("mouse" === ev.pointerType) {
587
+ instance.def("MX", x);
588
+ instance.def("MY", y);
589
+ if (!_pressingMouse) return;
605
590
  }
606
- });
607
- on(_canvas, "touchmove", (ev) => {
608
- preventDefault(ev);
609
- const touches = ev.changedTouches;
610
- for (const touch of touches) {
611
- const [x, y] = _getXY(touch);
612
- instance.emit("tapping", x, y, touch.identifier + 1);
613
- _updateTap(touch.identifier + 1, x, y);
591
+ const id = ev.pointerId;
592
+ if (_taps.get(id)) {
593
+ instance.emit("tapping", x, y, id);
594
+ _updateTap(id, x, y);
614
595
  }
615
596
  });
616
- const _touchEndHandler = (ev) => {
617
- preventDefault(ev);
618
- const existing = [];
619
- for (const touch of ev.targetTouches) {
620
- existing.push(touch.identifier + 1);
597
+ const _pointerUpHandler = (ev) => {
598
+ const id = ev.pointerId;
599
+ const tap = _taps.get(id);
600
+ const [x, y] = _getXY(ev);
601
+ if (!tap) return;
602
+ if ("mouse" === ev.pointerType) {
603
+ if (ev.button) return;
604
+ _pressingMouse = false;
621
605
  }
622
- for (const [id, tap] of _taps) {
623
- if (existing.includes(id)) continue;
624
- if (_checkTapped(tap)) {
625
- instance.emit("tapped", tap.xi, tap.yi, id);
626
- }
627
- instance.emit("untap", tap.x, tap.y, id);
628
- _taps.delete(id);
606
+ if (_checkTapped(tap)) {
607
+ instance.emit("tapped", tap.xi, tap.yi, id);
629
608
  }
609
+ instance.emit("untap", x, y, id);
610
+ _taps.delete(id);
611
+ _canvas.releasePointerCapture(id);
630
612
  };
631
- on(_canvas, "touchend", _touchEndHandler);
632
- on(_canvas, "touchcancel", _touchEndHandler);
613
+ on(_canvas, "pointerup", _pointerUpHandler);
614
+ on(_canvas, "pointercancel", _pointerUpHandler);
633
615
  on(root, "blur", () => {
634
616
  _pressingMouse = false;
635
617
  for (const [id, tap] of _taps) {
636
618
  instance.emit("untap", tap.x, tap.y, id);
637
619
  _taps.delete(id);
620
+ try {
621
+ _canvas.releasePointerCapture(id);
622
+ } catch (_) {}
638
623
  }
639
624
  });
640
625
  }
@@ -719,7 +704,7 @@
719
704
  instance.def("H", height);
720
705
  _canvas.width = width;
721
706
  _canvas.height = height;
722
- _canvas.style = "image-rendering:pixelated";
707
+ _canvas.style = "image-rendering:pixelated;touch-action:none";
723
708
  if (settings.autoscale) {
724
709
  let maxScale = +settings.autoscale;
725
710
  if (!_canvas.style.display) {
@@ -775,3 +760,16 @@
775
760
  }
776
761
  window.litecanvas = litecanvas;
777
762
  })();
763
+ /**
764
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
765
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
766
+ * The same seed always gives the same sequence.
767
+ *
768
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
769
+ * @license CC0-1.0 (Public Domain)
770
+ *
771
+ * @param {number} seed an unsigned 32-bit integer
772
+ * @param {number} t
773
+ * @param {typeof Math.imul} i
774
+ * @returns {() => number}
775
+ */
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,u=0,p=0,h=0,m=0,g=0,w=1,v=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,v*=44100,n*=44100,i*=44100,g*=44100,f*=500*z/85766121e6,h*=z/44100,d*=z/44100,c*=44100,u=44100*u|0,e*=.3*zzfxV,A=l+v+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=(u?1-x+x*W(z*P/u):1)*(S<0?-1:1)*b.abs(S)**r*(P<l?P/l:P<l+v?1-(P-l)/v*(1-w):P<l+v+n?w:P<A-g?(A-P-g)/i*w: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*p*W(P**5),I&&++I>c&&(a+=d,E+=d,I=0),!u||++D%u||(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,u,p,h,m=1,g,w=.5,v=1,x,y=1e3/60,b,z=0,k=3,E="sans-serif",T=20,C=1.2,P=Date.now(),I=e,D=e,L=[],S=[.5,0,1750,,,.3,1,,,,600,.1],A={},M={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?M.clamp(o,l,n):o},norm:(e,t,a)=>M.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)=>~~M.rand(e,t+1),rseed(e){P=~~e},cls(e){null==e?g.clearRect(0,0,M.W,M.H):M.rectfill(0,0,M.W,M.H,e)},rect(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e-w,~~t-w,~~a+2*w,~~l+2*w,i),M.stroke(n)},rectfill(e,t,a,l,n,i){g.beginPath(),g[i?"roundRect":"rect"](~~e,~~t,~~a,~~l,i),M.fill(n)},oval(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),M.stroke(n)},ovalfill(e,t,a,l,n){g.beginPath(),g.ellipse(~~e,~~t,~~a,~~l,0,0,o),M.fill(n)},circ(e,t,a,l){M.oval(e,t,a,a,l)},circfill(e,t,a,l){M.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=w&&~~e==~~a?.5:0,o=w&&~~t==~~l?.5:0;g.moveTo(~~e+i,~~t+o),g.lineTo(~~a+i,~~l+o),M.stroke(n)},linewidth(e){g.lineWidth=~~e,w=~~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=q(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]&&M.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(),M.translate(e,t),M.rotate(a),M.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=M.clamp(e,0,1)},fill(e){g.fillStyle=q(e),g.fill()},stroke(e){g.strokeStyle=q(e),g.stroke()},clip(e){g.beginPath(),e(g),g.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=S,(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(M,l);for(let e in n)M.def(e,n[e])},resize(e,a=e,l){t.width=e,t.height=a,t.autoscale=null==l?t.autoscale:l,X()},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)=>(u&&(H("before:"+(e=e.toLowerCase()),t,a,n,i),c||l[e]===M[e]||"function"!=typeof l[e]||l[e](t,a,n,i),H(e,t,a,n,i),H("after:"+e,t,a,n,i)),t),pal(e,t=3){null==e?I=D:(D=I,I=e),L=[],k=t,M.emit("pal",I,k)},palc(e,t){null==e?L=[]:L[e]=t},def(e,a){M[e]=a,t.global&&(l[e]=a)},timescale(e){v=e},framerate(e){y=1e3/~~e},stat:e=>[t,u,y/1e3,m,A,I,S,v,l.zzfxV,P,T,E,L,C][e],ispaused:()=>p,pause(){p||(p=!0,z=~~cancelAnimationFrame(z),M.emit("paused"))},resume(){u&&p&&(N(),p=!1,M.emit("resumed"))},quit(){for(let e of(M.emit("quit"),M.pause(),u=!1,A={},s))e();if(t.global){for(let e in M)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,min,max,pow,sqrt,sign".split(","))M[e]=n[e];function N(){z||(b=0,x=i.now(),z=r(W))}function W(){z=r(W);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*v;M.emit("update",e,t),M.def("T",M.T+e)}t&&(M.emit("draw",g),t>1&&(b=0))}function X(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(M.def("W",e),M.def("H",a),h.width=e,h.height=a,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,M.textalign("start","top"),M.emit("resized",m)}function H(e,t,a,l,n){if(A[e])for(let i of A[e])i(t,a,l,n)}function q(e){return e>=0?I[~~(L[e]??e)%I.length]:e}if(t.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,M),l.ENGINE=M}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,X(),c)for(let e in c)c[e]&&M.listen(e,c[e]);return r(function(){if(t.autoscale&&f(l,"resize",X),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);M.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)&&M.emit("tapped",l.xi,l.yi,0),M.emit("untap",n,i,0),t.delete(0),r=!1}}),f(l,"mousemove",t=>{t.preventDefault();let[a,l]=e(t);M.def("MX",a),M.def("MY",l),r&&(M.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);M.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);M.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)&&M.emit("tapped",l.xi,l.yi,e),M.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))M.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()),M.listen("after:update",()=>t.clear()),M.def("iskeydown",t=>a(e,t)),M.def("iskeypressed",e=>a(t,e)),M.def("lastkey",()=>n)}u=!0,M.emit("init",M),p||N()}),M}})();
1
+ (()=>{var e=["#211e20","#555568","#a0a08b","#e9efec"],t=e=>()=>{let t=e+=0x6d2b79f5;return t=Math.imul(t^t>>>15,1|t),(((t^=t+Math.imul(t^t>>>7,61|t))^t>>>14)>>>0)/0x100000000};window.litecanvas=function(a={}){let n,l=window,i=Math,o=performance,r=2*i.PI,s=requestAnimationFrame,f=[],d=(e,t,a)=>{e.addEventListener(t,a,!1),f.push(()=>e.removeEventListener(t,a,!1))},c=(l.zzfxX=new AudioContext,l.zzfxV=1,(e=1,t=.05,a=220,n=0,l=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,w=1,x=0,y=0,v=0)=>{let b=Math,z=2*b.PI,k=s*=500*z/44100/44100,C=a*=(1-t+2*t*b.random(t=[]))*z/44100,E=0,P=0,I=0,T=1,L=0,M=0,S=0,A=v<0?-1:1,N=z*A*v*2/44100,W=b.cos(N),X=b.sin,H=X(N)/4,q=1+H,B=-2*W/q,V=(1-H)/q,D=(1+A*W)/2/q,O=-(A+W)/q,G=0,R=0,Y=0,$=0;for(n=44100*n+9,x*=44100,l*=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=n+x+l+i+g|0;I<A;t[I++]=S*e)++M%(100*m|0)||(S=o?1<o?2<o?3<o?X(E*E):b.max(b.min(b.tan(E),1),-1):1-(2*E/z%2+2)%2:1-4*b.abs(b.round(E/z)-E/z):X(E),S=(p?1-y+y*X(z*I/p):1)*(S<0?-1:1)*b.abs(S)**r*(I<n?I/n:I<n+x?1-(I-n)/x*(1-w):I<n+x+l?w:I<A-g?(A-I-g)/i*w:0),S=g?S/2+(g>I?0:(I<A-g?1:(A-I)/g)*t[I-g|0]/2/e):S,v&&(S=$=D*G+O*(G=R)+D*(R=S)-V*Y-B*(Y=$))),E+=(N=(a+=s+=f)*b.cos(h*P++))+N*u*X(I**5),T&&++T>c&&(a+=d,C+=d,T=0),!p||++L%p||(a=C,s=k,T=T||1);(e=zzfxX.createBuffer(1,A,44100)).getChannelData(0).set(t),(a=zzfxX.createBufferSource()).buffer=e,a.connect(zzfxX.destination),a.start()}),p=(a=Object.assign({width:null,height:null,autoscale:!0,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0},a)).loop,u,h,m,g=1,w,x=.5,y=1,v,b=1e3/60,z,k=0,C=3,E="sans-serif",P=20,I=1.2,T=Date.now(),L=t(T>>>0),M=e,S=e,A=[],N=[.5,0,1750,,,.3,1,,,,600,.1],W={},X={W:0,H:0,T:0,MX:-1,MY:-1,rand:(e=0,t=1)=>e+L()*(t-e),randi:(e=0,t=1)=>~~X.rand(e,t+1),rseed(e){L=t((T=e)>>>0)},TAU:r,lerp:(e,t,a)=>e+a*(t-e),deg2rad:e=>i.PI/180*e,rad2deg:e=>180/i.PI*e,mod:(e,t)=>(e%t+t)%t||0,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,dist:(e,t,a,n)=>i.hypot(a-e,n-t),wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,n,l,i){let o=(e-t)/(a-t)*(l-n)+n;return i?X.clamp(o,n,l):o},norm:(e,t,a)=>X.map(e,t,a,0,1),cls(e){null==e?w.clearRect(0,0,X.W,X.H):X.rectfill(0,0,X.W,X.H,e)},rect(e,t,a,n,l,i){w.beginPath(),w[i?"roundRect":"rect"](~~e-x,~~t-x,~~a+2*x,~~n+2*x,i),X.stroke(l)},rectfill(e,t,a,n,l,i){w.beginPath(),w[i?"roundRect":"rect"](~~e,~~t,~~a,~~n,i),X.fill(l)},oval(e,t,a,n,l){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~n,0,0,r),X.stroke(l)},ovalfill(e,t,a,n,l){w.beginPath(),w.ellipse(~~e,~~t,~~a,~~n,0,0,r),X.fill(l)},circ(e,t,a,n){X.oval(e,t,a,a,n)},circfill(e,t,a,n){X.ovalfill(e,t,a,a,n)},shape(e){w.beginPath();for(let t=0;t<e.length;t+=2)t?w.lineTo(~~e[t],~~e[t+1]):w.moveTo(~~e[t],~~e[t+1]);w.lineTo(~~e[0],~~e[1])},line(e,t,a,n,l){w.beginPath();let i=x&&~~e==~~a?.5:0,o=x&&~~t==~~n?.5:0;w.moveTo(~~e+i,~~t+o),w.lineTo(~~a+i,~~n+o),X.stroke(l)},linewidth(e){w.lineWidth=~~e,x=~~e%2?.5:0},linedash(e,t=0){w.setLineDash(e),w.lineDashOffset=t},text(e,t,a,n=C,l="normal"){w.font=`${l} ${P}px ${E}`,w.fillStyle=D(n);let i=(""+a).split("\n");for(let a=0;a<i.length;a++)w.fillText(i[a],~~e,~~t+P*I*a)},textgap(e){I=e},textfont(e){E=e},textsize(e){P=e},textalign(e,t){e&&(w.textAlign=e),t&&(w.textBaseline=t)},image(e,t,a){w.drawImage(a,~~e,~~t)},spr(e,t,a){let n=a.replace(/[^\w.\n]/g,"").split("\n").filter(e=>e);for(let a=0;a<n.length;a++)for(let l=0;l<n[a].length;l++)"."!==n[a][l]&&X.rectfill(e+l,t+a,1,1,parseInt(n[a][l],36)||0)},paint(e,t,a,n={}){let l=n.canvas||new OffscreenCanvas(1,1),i=n.scale||1,o=w;return l.width=e*i,l.height=t*i,(w=l.getContext("2d")).scale(i,i),a(w),w=o,l.transferToImageBitmap()},ctx:e=>(e&&(w=e),w),push(e=0,t=e,a=0,n=1,l=n){w.save(),X.translate(e,t),X.rotate(a),X.scale(n,l)},pop(){w.restore()},translate(e,t){w.translate(~~e,~~t)},scale(e,t=e){w.scale(e,t)},rotate(e){w.rotate(e)},alpha(e){w.globalAlpha=X.clamp(e,0,1)},fill(e){w.fillStyle=D(e),w.fill()},stroke(e){w.strokeStyle=D(e),w.stroke()},clip(e){w.beginPath(),e(w),w.clip()},sfx:(e,t,a)=>!!l.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e||=N,(t||a>=0)&&((e=e.slice())[0]=a*(e[0]||1),e[10]=~~e[10]+t),c.apply(0,e),e),volume(e){l.zzfxV=e},canvas:()=>m,use(e,t={}){var a=e,n=t;let l=a(X,n);for(let e in l)X.def(e,l[e])},resize(e,t=e,n){a.width=e,a.height=t,a.autoscale=null==n?a.autoscale:n,B()},listen:(e,t)=>{W[e=e.toLowerCase()]=W[e]||new Set,W[e].add(t)},unlisten:(e,t)=>{W[e=e.toLowerCase()]&&W[e].delete(t)},emit:(e,t,a,n,i)=>(u&&(V("before:"+(e=e.toLowerCase()),t,a,n,i),p||l[e]===X[e]||"function"!=typeof l[e]||l[e](t,a,n,i),V(e,t,a,n,i),V("after:"+e,t,a,n,i)),t),pal(e,t=3){null==e?M=S:(S=M,M=e),A=[],C=t,X.emit("pal",M,C)},palc(e,t){null==e?A=[]:A[e]=t},def(e,t){X[e]=t,a.global&&(l[e]=t)},timescale(e){y=e},framerate(e){b=1e3/~~e},stat:e=>[a,u,b/1e3,g,W,M,N,y,l.zzfxV,T,P,E,A,I][e],ispaused:()=>h,pause(){h||(h=!0,k=~~cancelAnimationFrame(k),X.emit("paused"))},resume(){u&&h&&(H(),h=!1,X.emit("resumed"))},quit(){for(let e of(X.emit("quit"),X.pause(),u=!1,W={},f))e();if(a.global){for(let e in X)delete l[e];delete l.ENGINE}}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,min,max,pow,sqrt,sign".split(","))X[e]=i[e];function H(){k||(z=0,v=o.now(),k=s(q))}function q(){k=s(q);let e=o.now(),t=0,a=e-v;for(v=e,z+=a<100?a:b;z>=b;){t++,z-=b;let e=b/1e3*y;X.emit("update",e,t),X.def("T",X.T+e)}t&&(X.emit("draw",w),t>1&&(z=0))}function B(){let e=a.width>0?a.width:innerWidth,t=a.width>0?a.height||a.width:innerHeight;if(X.def("W",e),X.def("H",t),m.width=e,m.height=t,m.style="image-rendering:pixelated;touch-action:none",a.autoscale){let n=+a.autoscale;m.style.display||(m.style.display="block",m.style.margin="auto"),g=i.min(innerWidth/e,innerHeight/t),g=n>1&&g>n?n:g,m.style.width=e*g+"px",m.style.height=t*g+"px"}w.imageSmoothingEnabled=!1,X.textalign("start","top"),X.emit("resized",g)}function V(e,t,a,n,l){if(W[e])for(let i of W[e])i(t,a,n,l)}function D(e){return e>=0?M[~~(A[e]??e)%M.length]:e}if(a.global){if(l.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(l,X),l.ENGINE=X}if(n=document,w=(m=(m="string"==typeof a.canvas?n.querySelector(a.canvas):a.canvas)||n.createElement("canvas")).getContext("2d"),d(m,"click",()=>focus()),m.parentNode||n.body.appendChild(m),m.oncontextmenu=()=>!1,B(),p)for(let e in p)p[e]&&X.listen(e,p[e]);return s(function(){if(a.autoscale&&d(l,"resize",B),a.tapEvents){let e=e=>[(e.pageX-m.offsetLeft)/g,(e.pageY-m.offsetTop)/g],t=new Map,a=(e,a,n)=>{let l={x:a,y:n,xi:a,yi:n,t:o.now()};return t.set(e,l),l},n=!1;d(m,"pointerdown",t=>{let[l,i]=e(t);if("mouse"===t.pointerType){if(t.button)return;n=!0}let o=t.pointerId;a(o,l,i),X.emit("tap",l,i,o),m.setPointerCapture(o)}),d(m,"pointermove",l=>{let[i,o]=e(l);if("mouse"===l.pointerType&&(X.def("MX",i),X.def("MY",o),!n))return;let r=l.pointerId;if(t.get(r)){let e;X.emit("tapping",i,o,r),(e=t.get(r)||a(r)).x=i,e.y=o}});let i=a=>{let l=a.pointerId,i=t.get(l),[r,s]=e(a);if(i){if("mouse"===a.pointerType){if(a.button)return;n=!1}i&&o.now()-i.t<=300&&X.emit("tapped",i.xi,i.yi,l),X.emit("untap",r,s,l),t.delete(l),m.releasePointerCapture(l)}};d(m,"pointerup",i),d(m,"pointercancel",i),d(l,"blur",()=>{for(let[e,a]of(n=!1,t)){X.emit("untap",a.x,a.y,e),t.delete(e);try{m.releasePointerCapture(e)}catch(e){}}})}if(a.keyboardEvents){let e=new Set,t=new Set,a=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0,n="";d(l,"keydown",a=>{let l=a.key.toLowerCase();e.has(l)||(e.add(l),t.add(l),n=" "===l?"space":l)}),d(l,"keyup",t=>{e.delete(t.key.toLowerCase())}),d(l,"blur",()=>e.clear()),X.listen("after:update",()=>t.clear()),X.def("iskeydown",t=>a(e,t)),X.def("iskeypressed",e=>a(t,e)),X.def("lastkey",()=>n)}u=!0,X.emit("init",X),h||H()}),X}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.400.0",
3
+ "version": "0.402.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>",
@@ -32,10 +32,10 @@
32
32
  "creative coding"
33
33
  ],
34
34
  "devDependencies": {
35
- "@happy-dom/global-registrator": "^21.0.1",
35
+ "@happy-dom/global-registrator": "^21.0.3",
36
36
  "@size-limit/preset-small-lib": "^14.2.0",
37
37
  "@swc/core": "^1.16.18",
38
- "ava": "^8.0.1",
38
+ "ava": "^8.1.0",
39
39
  "esbuild": "^0.28.2",
40
40
  "genversion": "^3.2.0",
41
41
  "gzip-size": "^7.0.0",
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ import { setupZzFX } from './zzfx.js'
3
3
  import { defaultPalette } from './palette.js'
4
4
  import { assert } from './dev.js'
5
5
  import { version } from './version.js'
6
+ import { mulberry32 } from './mulberry32.js'
6
7
 
7
8
  /**
8
9
  * The litecanvas constructor
@@ -84,13 +85,15 @@ export default function litecanvas(settings = {}) {
84
85
  _fontLineHeight = 1.2,
85
86
  /** @type {number} */
86
87
  _rngSeed = Date.now(),
88
+ /** @type {() => number} */
89
+ _rngNext = mulberry32(_rngSeed >>> 0),
87
90
  /** @type {string[]} */
88
91
  _currentPalette = defaultPalette,
89
92
  /** @type {string[]} */
90
93
  _lastPalette = defaultPalette,
91
94
  /** @type {number[]} */
92
95
  _colorPaletteState = [],
93
- /** @type {number[]} */
96
+ /** @type {(number|undefined)[]} */
94
97
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
95
98
  /**
96
99
  * @type {Object<string,Set<Function>>} game event listeners
@@ -114,6 +117,54 @@ export default function litecanvas(settings = {}) {
114
117
  /** @type {number} */
115
118
  MY: -1,
116
119
 
120
+ /** RNG API */
121
+ /**
122
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
123
+ * using the Mulberry32 algorithm.
124
+ *
125
+ * @param {number} [min=0.0]
126
+ * @param {number} [max=1.0]
127
+ * @returns {number} the random number
128
+ */
129
+ rand: (min = 0.0, max = 1.0) => {
130
+ DEV: assert(isNumber(min), 'rand() 1st argument must be a number')
131
+ DEV: assert(isNumber(max), 'rand() 2nd argument must be a number')
132
+ DEV: assert(max >= min, 'rand() the 1st argument must be less than the 2nd argument')
133
+
134
+ return min + _rngNext() * (max - min)
135
+ },
136
+
137
+ /**
138
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
139
+ *
140
+ * @param {number} [min=0]
141
+ * @param {number} [max=1]
142
+ * @returns {number} the random number
143
+ */
144
+ randi: (min = 0, max = 1) => {
145
+ DEV: assert(isNumber(min), 'randi() 1st argument must be a number')
146
+ DEV: assert(isNumber(max), 'randi() 2nd argument must be a number')
147
+ DEV: assert(max >= min, 'randi() the 1st argument must be less than the 2nd argument')
148
+
149
+ return ~~instance.rand(min, max + 1)
150
+ },
151
+
152
+ /**
153
+ * Initializes the random number generator with an explicit seed value.
154
+ *
155
+ * Note: The seed should be a integer number greater than or equal to zero.
156
+ *
157
+ * @param {number} value
158
+ */
159
+ rseed(value) {
160
+ DEV: assert(
161
+ isNumber(value) && value >= 0,
162
+ 'rseed() 1st argument must be a non-negative integer'
163
+ )
164
+ _rngSeed = value
165
+ _rngNext = mulberry32(_rngSeed >>> 0)
166
+ },
167
+
117
168
  /** MATH API */
118
169
  /**
119
170
  * Twice the value of the mathematical constant PI (π).
@@ -306,60 +357,6 @@ export default function litecanvas(settings = {}) {
306
357
  return instance.map(value, start, stop, 0, 1)
307
358
  },
308
359
 
309
- /** RNG API */
310
- /**
311
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
312
- * using the Linear Congruential Generator (LCG) algorithm.
313
- *
314
- * @param {number} [min=0.0]
315
- * @param {number} [max=1.0]
316
- * @returns {number} the random number
317
- */
318
- rand: (min = 0.0, max = 1.0) => {
319
- DEV: assert(isNumber(min), 'rand() 1st argument must be a number')
320
- DEV: assert(isNumber(max), 'rand() 2nd argument must be a number')
321
- DEV: assert(max >= min, 'rand() the 1st argument must be less than the 2nd argument')
322
-
323
- const a = 1664525
324
- const c = 1013904223
325
- const m = 4294967296
326
-
327
- _rngSeed = (a * _rngSeed + c) % m
328
-
329
- return (_rngSeed / m) * (max - min) + min
330
- },
331
-
332
- /**
333
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
334
- *
335
- * @param {number} [min=0]
336
- * @param {number} [max=1]
337
- * @returns {number} the random number
338
- */
339
- randi: (min = 0, max = 1) => {
340
- DEV: assert(isNumber(min), 'randi() 1st argument must be a number')
341
- DEV: assert(isNumber(max), 'randi() 2nd argument must be a number')
342
- DEV: assert(max >= min, 'randi() the 1st argument must be less than the 2nd argument')
343
-
344
- return ~~instance.rand(min, max + 1)
345
- },
346
-
347
- /**
348
- * Initializes the random number generator with an explicit seed value.
349
- *
350
- * Note: The seed should be a integer number greater than or equal to zero.
351
- *
352
- * @param {number} value
353
- */
354
- rseed(value) {
355
- DEV: assert(
356
- isNumber(value) && value >= 0,
357
- 'rseed() 1st argument must be a non-negative integer'
358
- )
359
-
360
- _rngSeed = ~~value
361
- },
362
-
363
360
  /** BASIC GRAPHICS API */
364
361
  /**
365
362
  * Clear the game screen with an optional color.
@@ -1502,128 +1499,70 @@ export default function litecanvas(settings = {}) {
1502
1499
 
1503
1500
  let _pressingMouse = false
1504
1501
 
1505
- on(
1506
- _canvas,
1507
- 'mousedown',
1508
- /**
1509
- * @param {MouseEvent} ev
1510
- */
1511
- (ev) => {
1512
- if (!ev.button) {
1513
- preventDefault(ev)
1514
- const [x, y] = _getXY(ev)
1515
- instance.emit('tap', x, y, 0)
1516
- _registerTap(0, x, y)
1517
- _pressingMouse = true
1518
- }
1519
- }
1520
- )
1502
+ on(_canvas, 'pointerdown', (ev) => {
1503
+ const [x, y] = _getXY(ev)
1521
1504
 
1522
- on(
1523
- _canvas,
1524
- 'mouseup',
1525
- /**
1526
- * @param {MouseEvent} ev
1527
- */
1528
- (ev) => {
1529
- if (!ev.button) {
1530
- preventDefault(ev)
1531
- const tap = _taps.get(0)
1532
- const [x, y] = _getXY(ev)
1533
- if (_checkTapped(tap)) {
1534
- instance.emit('tapped', tap.xi, tap.yi, 0)
1535
- }
1536
- instance.emit('untap', x, y, 0)
1537
- _taps.delete(0)
1538
- _pressingMouse = false
1539
- }
1505
+ if ('mouse' === ev.pointerType) {
1506
+ if (ev.button) return
1507
+ _pressingMouse = true
1540
1508
  }
1541
- )
1542
1509
 
1543
- on(
1544
- root,
1545
- 'mousemove',
1546
- /**
1547
- * @param {MouseEvent} ev
1548
- */
1549
- (ev) => {
1550
- preventDefault(ev)
1510
+ const id = ev.pointerId
1511
+ _registerTap(id, x, y)
1512
+ instance.emit('tap', x, y, id)
1513
+
1514
+ _canvas.setPointerCapture(id)
1515
+ })
1516
+
1517
+ on(_canvas, 'pointermove', (ev) => {
1518
+ const [x, y] = _getXY(ev)
1551
1519
 
1552
- const [x, y] = _getXY(ev)
1520
+ if ('mouse' === ev.pointerType) {
1553
1521
  instance.def('MX', x)
1554
1522
  instance.def('MY', y)
1555
-
1556
1523
  if (!_pressingMouse) return
1557
-
1558
- instance.emit('tapping', x, y, 0)
1559
- _updateTap(0, x, y)
1560
1524
  }
1561
- )
1562
1525
 
1563
- on(
1564
- _canvas,
1565
- 'touchstart',
1566
- /**
1567
- * @param {TouchEvent} ev
1568
- */
1569
- (ev) => {
1570
- preventDefault(ev)
1571
- /** @type {TouchList} touches */
1572
- const touches = ev.changedTouches
1573
- for (const touch of touches) {
1574
- const [x, y] = _getXY(touch)
1575
- instance.emit('tap', x, y, touch.identifier + 1)
1576
- _registerTap(touch.identifier + 1, x, y)
1577
- }
1526
+ const id = ev.pointerId
1527
+ if (_taps.get(id)) {
1528
+ instance.emit('tapping', x, y, id)
1529
+ _updateTap(id, x, y)
1578
1530
  }
1579
- )
1531
+ })
1580
1532
 
1581
- on(
1582
- _canvas,
1583
- 'touchmove',
1584
- /**
1585
- * @param {TouchEvent} ev
1586
- */
1587
- (ev) => {
1588
- preventDefault(ev)
1589
- const touches = ev.changedTouches
1590
- for (const touch of touches) {
1591
- const [x, y] = _getXY(touch)
1592
- instance.emit('tapping', x, y, touch.identifier + 1)
1593
- _updateTap(touch.identifier + 1, x, y)
1594
- }
1595
- }
1596
- )
1533
+ const _pointerUpHandler = (ev) => {
1534
+ const id = ev.pointerId
1535
+ const tap = _taps.get(id)
1536
+ const [x, y] = _getXY(ev)
1597
1537
 
1598
- /**
1599
- * @param {TouchEvent} ev
1600
- */
1601
- const _touchEndHandler = (ev) => {
1602
- preventDefault(ev)
1603
- const existing = []
1538
+ if (!tap) return
1604
1539
 
1605
- for (const touch of ev.targetTouches) {
1606
- existing.push(touch.identifier + 1)
1540
+ if ('mouse' === ev.pointerType) {
1541
+ if (ev.button) return
1542
+ _pressingMouse = false
1607
1543
  }
1608
1544
 
1609
- for (const [id, tap] of _taps) {
1610
- if (existing.includes(id)) continue
1611
- if (_checkTapped(tap)) {
1612
- instance.emit('tapped', tap.xi, tap.yi, id)
1613
- }
1614
- instance.emit('untap', tap.x, tap.y, id)
1615
- _taps.delete(id)
1545
+ if (_checkTapped(tap)) {
1546
+ instance.emit('tapped', tap.xi, tap.yi, id)
1616
1547
  }
1548
+
1549
+ instance.emit('untap', x, y, id)
1550
+
1551
+ _taps.delete(id)
1552
+ _canvas.releasePointerCapture(id)
1617
1553
  }
1618
1554
 
1619
- on(_canvas, 'touchend', _touchEndHandler)
1620
- on(_canvas, 'touchcancel', _touchEndHandler)
1555
+ on(_canvas, 'pointerup', _pointerUpHandler)
1556
+ on(_canvas, 'pointercancel', _pointerUpHandler)
1621
1557
 
1622
1558
  on(root, 'blur', () => {
1623
1559
  _pressingMouse = false
1624
1560
  for (const [id, tap] of _taps) {
1625
1561
  instance.emit('untap', tap.x, tap.y, id)
1626
1562
  _taps.delete(id)
1563
+ try {
1564
+ _canvas.releasePointerCapture(id)
1565
+ } catch (_) {}
1627
1566
  }
1628
1567
  })
1629
1568
  }
@@ -1806,8 +1745,7 @@ export default function litecanvas(settings = {}) {
1806
1745
  _canvas.width = width
1807
1746
  _canvas.height = height
1808
1747
 
1809
- /** @ts-ignore */
1810
- _canvas.style = 'image-rendering:pixelated'
1748
+ _canvas.style = 'image-rendering:pixelated;touch-action:none'
1811
1749
 
1812
1750
  if (settings.autoscale) {
1813
1751
  let maxScale = +settings.autoscale
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mulberry32 is a 32-bit pseudo-random number generator (PRNG).
3
+ * You provide a 32-bit seed, and it produces a deterministic sequence of values.
4
+ * The same seed always gives the same sequence.
5
+ *
6
+ * @copyright https://gist.github.com/tommyettinger/46a874533244883189143505d203312c
7
+ * @license CC0-1.0 (Public Domain)
8
+ *
9
+ * @param {number} seed an unsigned 32-bit integer
10
+ * @param {number} t
11
+ * @param {typeof Math.imul} i
12
+ * @returns {() => number}
13
+ */
14
+ export const mulberry32 = (seed) => {
15
+ return () => {
16
+ let t = (seed += 0x6d2b79f5)
17
+ t = Math.imul(t ^ (t >>> 15), t | 1)
18
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
19
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296
20
+ }
21
+ }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.400.0'
2
+ export const version = '0.402.0'
package/types/global.d.ts CHANGED
@@ -186,7 +186,8 @@ declare global {
186
186
 
187
187
  /** RNG API */
188
188
  /**
189
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
189
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
190
+ * using the Mulberry32 algorithm.
190
191
  *
191
192
  * @param [min=0.0]
192
193
  * @param [max=1.0]
@@ -194,7 +195,7 @@ declare global {
194
195
  */
195
196
  function rand(min?: number, max?: number): number
196
197
  /**
197
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
198
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
198
199
  *
199
200
  * @param [min=0]
200
201
  * @param [max=1]
@@ -204,9 +205,7 @@ declare global {
204
205
  /**
205
206
  * Initializes the random number generator with an explicit seed value.
206
207
  *
207
- * Note: The seed should be a integer number greater than or equal to zero.
208
- *
209
- * @param value
208
+ * @param value the PRNG seed. Must be an non-negative number.
210
209
  */
211
210
  function rseed(value: number): void
212
211
 
package/types/types.d.ts CHANGED
@@ -180,7 +180,8 @@ type LitecanvasInstance = {
180
180
 
181
181
  /** RNG API */
182
182
  /**
183
- * Generates a pseudorandom float between min (inclusive) and max (exclusive)
183
+ * Generates a pseudo random float between min (inclusive) and max (exclusive)
184
+ * using the Mulberry32 algorithm.
184
185
  *
185
186
  * @param [min=0.0]
186
187
  * @param [max=1.0]
@@ -188,7 +189,7 @@ type LitecanvasInstance = {
188
189
  */
189
190
  rand(min?: number, max?: number): number
190
191
  /**
191
- * Generates a pseudorandom integer between min (inclusive) and max (inclusive)
192
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive)
192
193
  *
193
194
  * @param [min=0]
194
195
  * @param [max=1]
@@ -196,11 +197,11 @@ type LitecanvasInstance = {
196
197
  */
197
198
  randi(min?: number, max?: number): number
198
199
  /**
199
- * Initializes the random number generator with an explicit seed value.
200
+ * Generates a pseudo random integer between min (inclusive) and max (inclusive).
200
201
  *
201
- * Note: The seed should be a integer number greater than or equal to zero.
202
- *
203
- * @param value
202
+ * @param [min=0]
203
+ * @param [max=1]
204
+ * @returns the random number
204
205
  */
205
206
  rseed(value: number): void
206
207