litecanvas 0.92.3 → 0.93.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dist.dev.js CHANGED
@@ -32,7 +32,7 @@
32
32
  };
33
33
 
34
34
  // src/version.js
35
- var version = "0.92.3";
35
+ var version = "0.93.1";
36
36
 
37
37
  // src/index.js
38
38
  function litecanvas(settings = {}) {
@@ -44,7 +44,6 @@
44
44
  height: null,
45
45
  autoscale: true,
46
46
  pixelart: false,
47
- antialias: false,
48
47
  canvas: null,
49
48
  global: true,
50
49
  loop: null,
@@ -53,7 +52,7 @@
53
52
  animate: true
54
53
  };
55
54
  settings = Object.assign(defaults, settings);
56
- let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
55
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
57
56
  const instance = {
58
57
  /** @type {number} */
59
58
  W: 0,
@@ -811,7 +810,7 @@
811
810
  );
812
811
  DEV: assert(isNumber(pitchSlide), "[litecanvas] sfx() 2nd param must be a number");
813
812
  DEV: assert(isNumber(volumeFactor), "[litecanvas] sfx() 3rd param must be a number");
814
- if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
813
+ if (!root.zzfxV || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
815
814
  return false;
816
815
  }
817
816
  zzfxParams = zzfxParams || _defaultSound;
@@ -830,7 +829,10 @@
830
829
  * @param {number} value
831
830
  */
832
831
  volume(value) {
833
- DEV: assert(isNumber(value), "[litecanvas] volume() 1st param must be a number");
832
+ DEV: assert(
833
+ isNumber(value) && value >= 0,
834
+ "[litecanvas] volume() 1st param must be a positive number or zero"
835
+ );
834
836
  root.zzfxV = value;
835
837
  },
836
838
  /** PLUGINS API */
@@ -1025,7 +1027,9 @@
1025
1027
  * Resumes (if paused) the engine loop.
1026
1028
  */
1027
1029
  resume() {
1028
- if (!_rafid && _initialized) {
1030
+ if (_initialized && !_rafid) {
1031
+ _accumulated = 0;
1032
+ _lastFrameTime = performance.now();
1029
1033
  _rafid = raf(drawFrame);
1030
1034
  }
1031
1035
  },
@@ -1274,14 +1278,20 @@
1274
1278
  }
1275
1279
  );
1276
1280
  }
1281
+ on(root, "focus", () => {
1282
+ DEV: console.warn('[litecanvas] engine loop restarted on "focus" event');
1283
+ instance.pause();
1284
+ instance.resume();
1285
+ });
1277
1286
  _initialized = true;
1278
1287
  instance.emit("init", instance);
1279
- _lastFrameTime = performance.now();
1280
1288
  instance.resume();
1281
1289
  }
1282
1290
  function drawFrame(now) {
1283
1291
  if (!settings.animate) {
1284
1292
  return instance.emit("draw");
1293
+ } else if (_rafid) {
1294
+ _rafid = raf(drawFrame);
1285
1295
  }
1286
1296
  let updated = 0;
1287
1297
  let frameTime = (now - _lastFrameTime) / 1e3;
@@ -1298,9 +1308,6 @@
1298
1308
  if (updated) {
1299
1309
  instance.emit("draw");
1300
1310
  }
1301
- if (_rafid) {
1302
- _rafid = raf(drawFrame);
1303
- }
1304
1311
  }
1305
1312
  function setupCanvas() {
1306
1313
  if ("string" === typeof settings.canvas) {
@@ -1318,7 +1325,7 @@
1318
1325
  '[litecanvas] litecanvas() option "canvas" should be a canvas element or string (CSS selector)'
1319
1326
  );
1320
1327
  _ctx = _canvas.getContext("2d");
1321
- on(_canvas, "click", () => root.focus());
1328
+ on(_canvas, "click", () => focus());
1322
1329
  _canvas.style = "";
1323
1330
  resizeCanvas();
1324
1331
  if (!_canvas.parentNode) {
@@ -1339,7 +1346,7 @@
1339
1346
  null == settings.height || settings.width > 0 && settings.height > 0,
1340
1347
  '[litecanvas] litecanvas() option "width" is required when the option "height" is defined'
1341
1348
  );
1342
- const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
1349
+ const width = settings.width > 0 ? settings.width : innerWidth, height = settings.width > 0 ? settings.height || settings.width : innerHeight;
1343
1350
  instance.def("W", width);
1344
1351
  instance.def("H", height);
1345
1352
  _canvas.width = width;
@@ -1350,13 +1357,12 @@
1350
1357
  _canvas.style.display = "block";
1351
1358
  _canvas.style.margin = "auto";
1352
1359
  }
1353
- _scale = math.min(root.innerWidth / width, root.innerHeight / height);
1360
+ _scale = math.min(innerWidth / width, innerHeight / height);
1354
1361
  _scale = maxScale > 1 && _scale > maxScale ? maxScale : _scale;
1355
- _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
1356
1362
  _canvas.style.width = width * _scale + "px";
1357
1363
  _canvas.style.height = height * _scale + "px";
1358
1364
  }
1359
- if (!settings.antialias || settings.pixelart) {
1365
+ if (settings.pixelart) {
1360
1366
  _ctx.imageSmoothingEnabled = false;
1361
1367
  _canvas.style.imageRendering = "pixelated";
1362
1368
  }
package/dist/dist.js CHANGED
@@ -36,7 +36,6 @@
36
36
  height: null,
37
37
  autoscale: true,
38
38
  pixelart: false,
39
- antialias: false,
40
39
  canvas: null,
41
40
  global: true,
42
41
  loop: null,
@@ -45,7 +44,7 @@
45
44
  animate: true
46
45
  };
47
46
  settings = Object.assign(defaults, settings);
48
- let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated = 0, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
47
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _deltaTime = 1 / 60, _accumulated, _rafid, _fontFamily = "sans-serif", _fontSize = 20, _rngSeed = Date.now(), _colors = defaultPalette, _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1], _coreEvents = "init,update,draw,tap,untap,tapping,tapped,resized", _mathFunctions = "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp", _eventListeners = {};
49
48
  const instance = {
50
49
  /** @type {number} */
51
50
  W: 0,
@@ -558,7 +557,7 @@
558
557
  * @see https://github.com/KilledByAPixel/ZzFX
559
558
  */
560
559
  sfx(zzfxParams, pitchSlide = 0, volumeFactor = 1) {
561
- if (root.zzfxV <= 0 || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
560
+ if (!root.zzfxV || navigator.userActivation && !navigator.userActivation.hasBeenActive) {
562
561
  return false;
563
562
  }
564
563
  zzfxParams = zzfxParams || _defaultSound;
@@ -732,7 +731,9 @@
732
731
  * Resumes (if paused) the engine loop.
733
732
  */
734
733
  resume() {
735
- if (!_rafid && _initialized) {
734
+ if (_initialized && !_rafid) {
735
+ _accumulated = 0;
736
+ _lastFrameTime = performance.now();
736
737
  _rafid = raf(drawFrame);
737
738
  }
738
739
  },
@@ -970,14 +971,19 @@
970
971
  }
971
972
  );
972
973
  }
974
+ on(root, "focus", () => {
975
+ instance.pause();
976
+ instance.resume();
977
+ });
973
978
  _initialized = true;
974
979
  instance.emit("init", instance);
975
- _lastFrameTime = performance.now();
976
980
  instance.resume();
977
981
  }
978
982
  function drawFrame(now) {
979
983
  if (!settings.animate) {
980
984
  return instance.emit("draw");
985
+ } else if (_rafid) {
986
+ _rafid = raf(drawFrame);
981
987
  }
982
988
  let updated = 0;
983
989
  let frameTime = (now - _lastFrameTime) / 1e3;
@@ -994,9 +1000,6 @@
994
1000
  if (updated) {
995
1001
  instance.emit("draw");
996
1002
  }
997
- if (_rafid) {
998
- _rafid = raf(drawFrame);
999
- }
1000
1003
  }
1001
1004
  function setupCanvas() {
1002
1005
  if ("string" === typeof settings.canvas) {
@@ -1006,7 +1009,7 @@
1006
1009
  }
1007
1010
  _canvas = _canvas || document.createElement("canvas");
1008
1011
  _ctx = _canvas.getContext("2d");
1009
- on(_canvas, "click", () => root.focus());
1012
+ on(_canvas, "click", () => focus());
1010
1013
  _canvas.style = "";
1011
1014
  resizeCanvas();
1012
1015
  if (!_canvas.parentNode) {
@@ -1015,7 +1018,7 @@
1015
1018
  _canvas.oncontextmenu = () => false;
1016
1019
  }
1017
1020
  function resizeCanvas() {
1018
- const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
1021
+ const width = settings.width > 0 ? settings.width : innerWidth, height = settings.width > 0 ? settings.height || settings.width : innerHeight;
1019
1022
  instance.def("W", width);
1020
1023
  instance.def("H", height);
1021
1024
  _canvas.width = width;
@@ -1026,13 +1029,12 @@
1026
1029
  _canvas.style.display = "block";
1027
1030
  _canvas.style.margin = "auto";
1028
1031
  }
1029
- _scale = math.min(root.innerWidth / width, root.innerHeight / height);
1032
+ _scale = math.min(innerWidth / width, innerHeight / height);
1030
1033
  _scale = maxScale > 1 && _scale > maxScale ? maxScale : _scale;
1031
- _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
1032
1034
  _canvas.style.width = width * _scale + "px";
1033
1035
  _canvas.style.height = height * _scale + "px";
1034
1036
  }
1035
- if (!settings.antialias || settings.pixelart) {
1037
+ if (settings.pixelart) {
1036
1038
  _ctx.imageSmoothingEnabled = false;
1037
1039
  _canvas.style.imageRendering = "pixelated";
1038
1040
  }
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,i=Math,l=2*i.PI,n=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.beginPath(),f=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,i=.05,l=220,n=0,o=0,r=.1,s=0,f=1,c=0,d=0,u=0,p=0,m=0,h=0,g=0,x=0,v=0,w=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=l*=(1-i+2*i*E.random(i=[]))*z/44100,I=0,A=0,L=0,S=1,M=0,N=0,P=0,D=k<0?-1:1,F=z*D*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+D*q)/2/O,G=-(D+q)/O,X=0,Y=0,$=0,j=0;for(n=44100*n+9,y*=44100,o*=44100,r*=44100,v*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,m=44100*m|0,a*=.3*e.zzfxV,D=n+y+o+r+v|0;L<D;i[L++]=P*a)++N%(100*x|0)||(P=s?1<s?2<s?3<s?B(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):B(I),P=(m?1-b+b*B(z*L/m):1)*(P<0?-1:1)*E.abs(P)**f*(L<n?L/n:L<n+y?1-(L-n)/y*(1-w):L<n+y+o?w:L<D-v?(D-L-v)/r*w:0),P=v?P/2+(v>L?0:(L<D-v?1:(D-L)/v)*i[L-v|0]/2/a):P,k&&(P=j=R*X+G*(X=Y)+R*(Y=P)-W*$-V*($=j))),I+=(F=(l+=c+=d)*E.cos(g*A++))+F*h*B(L**5),S&&++S>p&&(l+=u,C+=u,S=0),!m||++M%m||(l=C,c=T,S=S||1);(a=t.createBuffer(1,D,44100)).getChannelData(0).set(i),(l=t.createBufferSource()).buffer=a,l.connect(t.destination),l.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=[],u,p=1,m,h=.5,g=1,x,v=1/60,w=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:l,HALF_PI:l/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>i.PI/180*e,rad2deg:e=>180/i.PI*e,round:(e,t=0)=>{if(!t)return i.round(e);let a=10**t;return i.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,i,l,n){let o=(e-t)/(a-t)*(l-i)+i;return n?I.clamp(o,i,l):o},norm:(e,t,a)=>I.map(e,t,a,0,1),wave:(e,t,a,i=Math.sin)=>e+(i(a)+1)/2*(t-e),rand:(e=0,t=1)=>(E=(1664525*E+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>i.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,i,l,n){s(m),m[n?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~i+2*h,n),I.stroke(l)},rectfill(e,t,a,i,l,n){s(m),m[n?"roundRect":"rect"](~~e,~~t,~~a,~~i,n),I.fill(l)},circ(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),I.stroke(i)},circfill(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),I.fill(i)},oval(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.stroke(n)},ovalfill(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.fill(n)},line(e,t,a,i,l){s(m);let n=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~i);m.moveTo(~~e+n,~~t+o),m.lineTo(~~a+n,~~i+o),I.stroke(l)},linewidth(e){m.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,i=3,l="normal"){m.font=`${l} ${k}px ${b}`,m.fillStyle=z[~~i%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,i={}){let l=i.canvas||new OffscreenCanvas(1,1),n=i.scale||1,o=m;if(l.width=e*n,l.height=t*n,(m=l.getContext("2d")).scale(n,n),Array.isArray(a)){let e=0,t=0;for(let i of(m.imageSmoothingEnabled=!1,a)){for(let a of i)" "!==a&&"."!==a&&I.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(m);return m=o,l.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,i=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||T,(0!==t||1!==i)&&((e=e.slice())[0]=i*(e[0]||1),e[10]=~~e[10]+t),f.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>u,use(e,t={}){c?N(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,i,l){c&&(M("before:"+(e=e.toLowerCase()),t,a,i,l),M(e,t,a,i,l),M("after:"+e,t,a,i,l))},pal(t=e){z=t},def(e,i){I[e]=i,t.global&&(a[e]=i)},timescale(e){g=e},framerate(e){v=1/~~e},stat(e){let i={index:e,value:[t,c,v,p,C,z,T,g,a.zzfxV,E,k,b][e]};return I.emit("stat",i),i.value},quit(){for(let e of(I.pause(),I.emit("quit"),C={},o))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]=i[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)N(e,t);if(t.autoscale&&r(a,"resize",S),t.tapEvents){let e=e=>[(e.pageX-u.offsetLeft)/p,(e.pageY-u.offsetTop)/p],t=new Map,i=(e,a,i)=>{let l={x:a,y:i,xi:a,yi:i,t:performance.now()};return t.set(e,l),l},l=(e,a,l)=>{let n=t.get(e)||i(e);n.x=a,n.y=l},n=e=>e&&performance.now()-e.t<=300,o=e=>e.preventDefault(),s=!1;r(u,"mousedown",t=>{if(0===t.button){o(t);let[a,l]=e(t);I.emit("tap",a,l,0),i(0,a,l),s=!0}}),r(u,"mouseup",a=>{if(0===a.button){o(a);let i=t.get(0),[l,r]=e(a);n(i)&&I.emit("tapped",i.xi,i.yi,0),I.emit("untap",l,r,0),t.delete(0),s=!1}}),r(a,"mousemove",t=>{o(t);let[a,i]=e(t);I.def("MX",a),I.def("MY",i),s&&(I.emit("tapping",a,i,0),l(0,a,i))}),r(u,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a);I.emit("tap",t,l,a.identifier+1),i(a.identifier+1,t,l)}}),r(u,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,i]=e(a);I.emit("tapping",t,i,a.identifier+1),l(a.identifier+1,t,i)}});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,i]of t)a.includes(e)||(n(i)&&I.emit("tapped",i.xi,i.yi,e),I.emit("untap",i.x,i.y,e),t.delete(e))};r(u,"touchend",f),r(u,"touchcancel",f),r(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,i=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let i=a.key.toLowerCase();e.has(i)||(e.add(i),t.add(i))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),I.listen("after:update",()=>t.clear()),I.def("iskeydown",t=>i(e,t)),I.def("iskeypressed",e=>i(t,e))}c=!0,I.emit("init",I),x=performance.now(),I.resume()}function L(e){if(!t.animate)return I.emit("draw");let a=0,i=(e-x)/1e3;if(x=e,i<.1)for(w+=i;w>=v;)a++,I.emit("update",v*g,a),I.def("T",I.T+v*g),w-=v;a&&I.emit("draw"),y&&(y=n(L))}function S(){let e=t.width||a.innerWidth,l=t.height||t.width||a.innerHeight;if(I.def("W",e),I.def("H",l),u.width=e,u.height=l,t.autoscale){let n=+t.autoscale;u.style.display||(u.style.display="block",u.style.margin="auto"),p=i.min(a.innerWidth/e,a.innerHeight/l),p=n>1&&p>n?n:p,p=(t.pixelart?~~p:p)||1,u.style.width=e*p+"px",u.style.height=l*p+"px"}(!t.antialias||t.pixelart)&&(m.imageSmoothingEnabled=!1,u.style.imageRendering="pixelated"),I.textalign("start","top"),I.emit("resized",p),I.cls(0),t.animate||n(L)}function M(e,t,a,i,l){if(C[e])for(let n of C[e])n(t,a,i,l)}function N(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=(u=(u="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(u,"click",()=>a.focus()),u.style="",S(),u.parentNode||document.body.appendChild(u),u.oncontextmenu=()=>!1,"loading"===document.readyState?r(a,"DOMContentLoaded",()=>n(A)):n(A),I}})();
1
+ (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,i=Math,l=2*i.PI,n=requestAnimationFrame,o=[],r=(e,t,a)=>{e.addEventListener(t,a,!1),o.push(()=>e.removeEventListener(t,a,!1))},s=e=>e.beginPath(),f=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,i=.05,l=220,n=0,o=0,r=.1,s=0,f=1,c=0,d=0,u=0,p=0,m=0,h=0,g=0,w=0,v=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=l*=(1-i+2*i*E.random(i=[]))*z/44100,I=0,A=0,L=0,S=1,M=0,N=0,P=0,D=k<0?-1:1,F=z*D*k*2/44100,q=E.cos(F),B=E.sin,H=B(F)/4,O=1+H,V=-2*q/O,W=(1-H)/O,R=(1+D*q)/2/O,G=-(D+q)/O,X=0,Y=0,$=0,j=0;for(n=44100*n+9,y*=44100,o*=44100,r*=44100,v*=44100,d*=500*z/85766121e6,g*=z/44100,u*=z/44100,p*=44100,m=44100*m|0,a*=.3*e.zzfxV,D=n+y+o+r+v|0;L<D;i[L++]=P*a)++N%(100*w|0)||(P=s?1<s?2<s?3<s?B(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):B(I),P=(m?1-b+b*B(z*L/m):1)*(P<0?-1:1)*E.abs(P)**f*(L<n?L/n:L<n+y?1-(L-n)/y*(1-x):L<n+y+o?x:L<D-v?(D-L-v)/r*x:0),P=v?P/2+(v>L?0:(L<D-v?1:(D-L)/v)*i[L-v|0]/2/a):P,k&&(P=j=R*X+G*(X=Y)+R*(Y=P)-W*$-V*($=j))),I+=(F=(l+=c+=d)*E.cos(g*A++))+F*h*B(L**5),S&&++S>p&&(l+=u,C+=u,S=0),!m||++M%m||(l=C,c=T,S=S||1);(a=t.createBuffer(1,D,44100)).getChannelData(0).set(i),(l=t.createBufferSource()).buffer=a,l.connect(t.destination),l.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let c=!1,d=[],u,p=1,m,h=.5,g=1,w,v=1/60,x,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:l,HALF_PI:l/4,lerp:(e,t,a)=>a*(t-e)+e,deg2rad:e=>i.PI/180*e,rad2deg:e=>180/i.PI*e,round:(e,t=0)=>{if(!t)return i.round(e);let a=10**t;return i.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*i.floor((e-t)/(a-t)),map(e,t,a,i,l,n){let o=(e-t)/(a-t)*(l-i)+i;return n?I.clamp(o,i,l):o},norm:(e,t,a)=>I.map(e,t,a,0,1),wave:(e,t,a,i=Math.sin)=>e+(i(a)+1)/2*(t-e),rand:(e=0,t=1)=>(E=(1664525*E+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>i.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,i,l,n){s(m),m[n?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~i+2*h,n),I.stroke(l)},rectfill(e,t,a,i,l,n){s(m),m[n?"roundRect":"rect"](~~e,~~t,~~a,~~i,n),I.fill(l)},circ(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),I.stroke(i)},circfill(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),I.fill(i)},oval(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.stroke(n)},ovalfill(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.fill(n)},line(e,t,a,i,l){s(m);let n=.5*(0!==h&&~~e==~~a),o=.5*(0!==h&&~~t==~~i);m.moveTo(~~e+n,~~t+o),m.lineTo(~~a+n,~~i+o),I.stroke(l)},linewidth(e){m.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){m.setLineDash(e),m.lineDashOffset=t},text(e,t,a,i=3,l="normal"){m.font=`${l} ${k}px ${b}`,m.fillStyle=z[~~i%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,i={}){let l=i.canvas||new OffscreenCanvas(1,1),n=i.scale||1,o=m;if(l.width=e*n,l.height=t*n,(m=l.getContext("2d")).scale(n,n),Array.isArray(a)){let e=0,t=0;for(let i of(m.imageSmoothingEnabled=!1,a)){for(let a of i)" "!==a&&"."!==a&&I.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(m);return m=o,l.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,i=1)=>!!a.zzfxV&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||T,(0!==t||1!==i)&&((e=e.slice())[0]=i*(e[0]||1),e[10]=~~e[10]+t),f.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>u,use(e,t={}){c?N(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,i,l){c&&(M("before:"+(e=e.toLowerCase()),t,a,i,l),M(e,t,a,i,l),M("after:"+e,t,a,i,l))},pal(t=e){z=t},def(e,i){I[e]=i,t.global&&(a[e]=i)},timescale(e){g=e},framerate(e){v=1/~~e},stat(e){let i={index:e,value:[t,c,v,p,C,z,T,g,a.zzfxV,E,k,b][e]};return I.emit("stat",i),i.value},quit(){for(let e of(I.pause(),I.emit("quit"),C={},o))e();if(t.global){for(let e in I)delete a[e];delete a.ENGINE}c=!1},pause(){cancelAnimationFrame(y),y=0},resume(){c&&!y&&(x=0,w=performance.now(),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]=i[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)N(e,t);if(t.autoscale&&r(a,"resize",S),t.tapEvents){let e=e=>[(e.pageX-u.offsetLeft)/p,(e.pageY-u.offsetTop)/p],t=new Map,i=(e,a,i)=>{let l={x:a,y:i,xi:a,yi:i,t:performance.now()};return t.set(e,l),l},l=(e,a,l)=>{let n=t.get(e)||i(e);n.x=a,n.y=l},n=e=>e&&performance.now()-e.t<=300,o=e=>e.preventDefault(),s=!1;r(u,"mousedown",t=>{if(0===t.button){o(t);let[a,l]=e(t);I.emit("tap",a,l,0),i(0,a,l),s=!0}}),r(u,"mouseup",a=>{if(0===a.button){o(a);let i=t.get(0),[l,r]=e(a);n(i)&&I.emit("tapped",i.xi,i.yi,0),I.emit("untap",l,r,0),t.delete(0),s=!1}}),r(a,"mousemove",t=>{o(t);let[a,i]=e(t);I.def("MX",a),I.def("MY",i),s&&(I.emit("tapping",a,i,0),l(0,a,i))}),r(u,"touchstart",t=>{for(let a of(o(t),t.changedTouches)){let[t,l]=e(a);I.emit("tap",t,l,a.identifier+1),i(a.identifier+1,t,l)}}),r(u,"touchmove",t=>{for(let a of(o(t),t.changedTouches)){let[t,i]=e(a);I.emit("tapping",t,i,a.identifier+1),l(a.identifier+1,t,i)}});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,i]of t)a.includes(e)||(n(i)&&I.emit("tapped",i.xi,i.yi,e),I.emit("untap",i.x,i.y,e),t.delete(e))};r(u,"touchend",f),r(u,"touchcancel",f),r(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,i=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;r(a,"keydown",a=>{let i=a.key.toLowerCase();e.has(i)||(e.add(i),t.add(i))}),r(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),r(a,"blur",()=>e.clear()),I.listen("after:update",()=>t.clear()),I.def("iskeydown",t=>i(e,t)),I.def("iskeypressed",e=>i(t,e))}r(a,"focus",()=>{I.pause(),I.resume()}),c=!0,I.emit("init",I),I.resume()}function L(e){if(!t.animate)return I.emit("draw");y&&(y=n(L));let a=0,i=(e-w)/1e3;if(w=e,i<.1)for(x+=i;x>=v;)a++,I.emit("update",v*g,a),I.def("T",I.T+v*g),x-=v;a&&I.emit("draw")}function S(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(I.def("W",e),I.def("H",a),u.width=e,u.height=a,t.autoscale){let l=+t.autoscale;u.style.display||(u.style.display="block",u.style.margin="auto"),p=i.min(innerWidth/e,innerHeight/a),p=l>1&&p>l?l:p,u.style.width=e*p+"px",u.style.height=a*p+"px"}t.pixelart&&(m.imageSmoothingEnabled=!1,u.style.imageRendering="pixelated"),I.textalign("start","top"),I.emit("resized",p),I.cls(0),t.animate||n(L)}function M(e,t,a,i,l){if(C[e])for(let n of C[e])n(t,a,i,l)}function N(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=(u=(u="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),r(u,"click",()=>focus()),u.style="",S(),u.parentNode||document.body.appendChild(u),u.oncontextmenu=()=>!1,"loading"===document.readyState?r(a,"DOMContentLoaded",()=>n(A)):n(A),I}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.92.3",
3
+ "version": "0.93.1",
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>",
@@ -19,23 +19,24 @@
19
19
  "types": "./types/index.d.ts",
20
20
  "unpkg": "./dist/dist.dev.js",
21
21
  "keywords": [
22
- "game engine",
23
- "game development",
24
- "2d",
22
+ "tiny",
23
+ "micro",
24
+ "javascript",
25
+ "html5",
25
26
  "canvas",
27
+ "2d",
28
+ "game",
26
29
  "gamedev",
27
30
  "js13k",
28
- "creative-coding",
29
- "pico-8",
30
- "p5"
31
+ "creative coding"
31
32
  ],
32
33
  "devDependencies": {
33
- "@litecanvas/jsdom-extras": "^2.0.0",
34
+ "@litecanvas/jsdom-extras": "^2.0.1",
34
35
  "@size-limit/preset-small-lib": "^11.2.0",
35
- "@swc/core": "^1.12.11",
36
+ "@swc/core": "^1.13.1",
36
37
  "@types/jsdom": "^21.1.7",
37
38
  "ava": "^6.4.1",
38
- "esbuild": "^0.25.6",
39
+ "esbuild": "^0.25.8",
39
40
  "genversion": "^3.2.0",
40
41
  "gzip-size": "^7.0.0",
41
42
  "jsdom": "^26.1.0",
@@ -52,7 +53,7 @@
52
53
  "prepublishOnly": "npm test",
53
54
  "test": "ava --timeout=1s --fast-fail --tap | tap-min",
54
55
  "test:watch": "ava --watch",
55
- "dev": "esbuild src/web.js --bundle --watch --outfile=dist/dist.dev.js --servedir=.",
56
+ "dev": "esbuild src/web.js --bundle --watch --outfile=samples/dist.js --servedir=samples",
56
57
  "build": "npm run genversion && node script/build.js && size-limit",
57
58
  "format": "prettier -w src/* samples/* types/* script/* types/*",
58
59
  "check-types": "npx ts types/*",
package/src/index.js CHANGED
@@ -33,7 +33,6 @@ export default function litecanvas(settings = {}) {
33
33
  height: null,
34
34
  autoscale: true,
35
35
  pixelart: false,
36
- antialias: false,
37
36
  canvas: null,
38
37
  global: true,
39
38
  loop: null,
@@ -64,7 +63,7 @@ export default function litecanvas(settings = {}) {
64
63
  /** @type {number} duration of a frame at 60 FPS (default) */
65
64
  _deltaTime = 1 / 60,
66
65
  /** @type {number} */
67
- _accumulated = 0,
66
+ _accumulated,
68
67
  /** @type {number?} */
69
68
  _rafid,
70
69
  /** @type {string} */
@@ -945,7 +944,7 @@ export default function litecanvas(settings = {}) {
945
944
  DEV: assert(isNumber(volumeFactor), '[litecanvas] sfx() 3rd param must be a number')
946
945
 
947
946
  if (
948
- root.zzfxV <= 0 ||
947
+ !root.zzfxV ||
949
948
  (navigator.userActivation && !navigator.userActivation.hasBeenActive)
950
949
  ) {
951
950
  return false
@@ -972,7 +971,10 @@ export default function litecanvas(settings = {}) {
972
971
  * @param {number} value
973
972
  */
974
973
  volume(value) {
975
- DEV: assert(isNumber(value), '[litecanvas] volume() 1st param must be a number')
974
+ DEV: assert(
975
+ isNumber(value) && value >= 0,
976
+ '[litecanvas] volume() 1st param must be a positive number or zero'
977
+ )
976
978
 
977
979
  root.zzfxV = value
978
980
  },
@@ -1207,7 +1209,9 @@ export default function litecanvas(settings = {}) {
1207
1209
  * Resumes (if paused) the engine loop.
1208
1210
  */
1209
1211
  resume() {
1210
- if (!_rafid && _initialized) {
1212
+ if (_initialized && !_rafid) {
1213
+ _accumulated = 0
1214
+ _lastFrameTime = performance.now()
1211
1215
  _rafid = raf(drawFrame)
1212
1216
  }
1213
1217
  },
@@ -1502,11 +1506,17 @@ export default function litecanvas(settings = {}) {
1502
1506
  )
1503
1507
  }
1504
1508
 
1509
+ // this seems to solve a strange bug that drop the FPS
1510
+ // when switching tabs in the browser
1511
+ on(root, 'focus', () => {
1512
+ DEV: console.warn('[litecanvas] engine loop restarted on "focus" event')
1513
+ instance.pause()
1514
+ instance.resume()
1515
+ })
1516
+
1505
1517
  // start the engine
1506
1518
  _initialized = true
1507
1519
  instance.emit('init', instance)
1508
-
1509
- _lastFrameTime = performance.now()
1510
1520
  instance.resume()
1511
1521
  }
1512
1522
 
@@ -1517,6 +1527,11 @@ export default function litecanvas(settings = {}) {
1517
1527
  if (!settings.animate) {
1518
1528
  return instance.emit('draw')
1519
1529
  }
1530
+ // request the next frame
1531
+ // only when the engine loop are not paused (_rafid >= 1)
1532
+ else if (_rafid) {
1533
+ _rafid = raf(drawFrame)
1534
+ }
1520
1535
 
1521
1536
  let updated = 0
1522
1537
  let frameTime = (now - _lastFrameTime) / 1000
@@ -1536,12 +1551,6 @@ export default function litecanvas(settings = {}) {
1536
1551
  if (updated) {
1537
1552
  instance.emit('draw')
1538
1553
  }
1539
-
1540
- // request the next frame
1541
- // only when the engine loop are not paused (_rafid >= 1)
1542
- if (_rafid) {
1543
- _rafid = raf(drawFrame)
1544
- }
1545
1554
  }
1546
1555
 
1547
1556
  function setupCanvas() {
@@ -1564,7 +1573,7 @@ export default function litecanvas(settings = {}) {
1564
1573
 
1565
1574
  _ctx = _canvas.getContext('2d')
1566
1575
 
1567
- on(_canvas, 'click', () => root.focus())
1576
+ on(_canvas, 'click', () => focus())
1568
1577
 
1569
1578
  /** @ts-ignore */
1570
1579
  _canvas.style = ''
@@ -1592,8 +1601,8 @@ export default function litecanvas(settings = {}) {
1592
1601
  '[litecanvas] litecanvas() option "width" is required when the option "height" is defined'
1593
1602
  )
1594
1603
 
1595
- const width = settings.width || root.innerWidth,
1596
- height = settings.height || settings.width || root.innerHeight
1604
+ const width = settings.width > 0 ? settings.width : innerWidth,
1605
+ height = settings.width > 0 ? settings.height || settings.width : innerHeight
1597
1606
 
1598
1607
  instance.def('W', width)
1599
1608
  instance.def('H', height)
@@ -1608,29 +1617,30 @@ export default function litecanvas(settings = {}) {
1608
1617
  _canvas.style.margin = 'auto'
1609
1618
  }
1610
1619
 
1611
- _scale = math.min(root.innerWidth / width, root.innerHeight / height)
1620
+ _scale = math.min(innerWidth / width, innerHeight / height)
1612
1621
  _scale = maxScale > 1 && _scale > maxScale ? maxScale : _scale
1613
- _scale = (settings.pixelart ? ~~_scale : _scale) || 1
1614
1622
 
1615
1623
  _canvas.style.width = width * _scale + 'px'
1616
1624
  _canvas.style.height = height * _scale + 'px'
1617
1625
  }
1618
1626
 
1619
- // restore canvas image rendering properties
1620
- if (!settings.antialias || settings.pixelart) {
1627
+ // set canvas image rendering properties
1628
+ if (settings.pixelart) {
1621
1629
  _ctx.imageSmoothingEnabled = false
1622
1630
  _canvas.style.imageRendering = 'pixelated'
1623
1631
  }
1624
1632
 
1625
- // reset the default text align and baseline
1633
+ // set the default text align and baseline
1626
1634
  instance.textalign('start', 'top')
1627
1635
 
1628
1636
  // trigger "resized" event
1637
+ // note: not triggered before the "init" event
1629
1638
  instance.emit('resized', _scale)
1630
1639
 
1640
+ // paint a temporary background
1631
1641
  instance.cls(0)
1632
1642
 
1633
- // force redraw
1643
+ // force redraw when the canvas is not animated
1634
1644
  if (!settings.animate) {
1635
1645
  raf(drawFrame)
1636
1646
  }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.92.3'
2
+ export const version = '0.93.1'
package/types/types.d.ts CHANGED
@@ -594,19 +594,18 @@ type LitecanvasOptions = {
594
594
  canvas?: HTMLCanvasElement | string
595
595
  /**
596
596
  * If `true` (default) scales the canvas to fill the screen, but preserving the aspect ratio.
597
- * Instead of `true`, you can pass a number to limit the maximum scale factor of the canvas.
597
+ * Instead of `true`, you can pass a number to determine the maximum scale.
598
598
  *
599
599
  * Note: Only works if the option "width" was specified.
600
600
  */
601
601
  autoscale?: boolean | number
602
602
  /**
603
603
  * If `true`, the pixel art images won't look blurry.
604
+ * Also, disables canvas built-in antialias.
605
+ *
606
+ * Default: `false`
604
607
  */
605
608
  pixelart?: boolean
606
- /**
607
- * If `false` (default), disable the canvas antialias.
608
- */
609
- antialias?: boolean
610
609
  /**
611
610
  * If `true` (default), all methods and properties of the engine will be exposed to the global scope (window).
612
611
  */