litecanvas 0.93.2 → 0.94.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.93.2";
35
+ var version = "0.94.1";
36
36
 
37
37
  // src/index.js
38
38
  function litecanvas(settings = {}) {
@@ -52,7 +52,7 @@
52
52
  animate: true
53
53
  };
54
54
  settings = Object.assign(defaults, settings);
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 = {};
55
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 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 = {};
56
56
  const instance = {
57
57
  /** @type {number} */
58
58
  W: 0,
@@ -958,7 +958,7 @@
958
958
  isNumber(value) && value >= 1,
959
959
  "[litecanvas] framerate() 1st param must be a positive number"
960
960
  );
961
- _deltaTime = 1 / ~~value;
961
+ _fpsInterval = 1e3 / ~~value;
962
962
  },
963
963
  /**
964
964
  * Returns information about that engine instance.
@@ -974,7 +974,7 @@
974
974
  // 1
975
975
  _initialized,
976
976
  // 2
977
- _deltaTime,
977
+ _fpsInterval / 1e3,
978
978
  // 3
979
979
  _scale,
980
980
  // 4
@@ -1028,8 +1028,8 @@
1028
1028
  */
1029
1029
  resume() {
1030
1030
  if (_initialized && !_rafid) {
1031
- _accumulated = _deltaTime;
1032
- _lastFrameTime = performance.now();
1031
+ _accumulated = _fpsInterval;
1032
+ _lastFrameTime = Date.now();
1033
1033
  _rafid = raf(drawFrame);
1034
1034
  }
1035
1035
  },
@@ -1085,7 +1085,7 @@
1085
1085
  // initial y
1086
1086
  yi: y,
1087
1087
  // timestamp
1088
- t: performance.now()
1088
+ t: Date.now()
1089
1089
  };
1090
1090
  _taps.set(id, tap);
1091
1091
  return tap;
@@ -1105,7 +1105,7 @@
1105
1105
  /**
1106
1106
  * @param {{t: number}} tap
1107
1107
  */
1108
- (tap) => tap && performance.now() - tap.t <= 300
1108
+ (tap) => tap && Date.now() - tap.t <= 300
1109
1109
  ), preventDefault = (
1110
1110
  /**
1111
1111
  * @param {Event} ev
@@ -1278,37 +1278,36 @@
1278
1278
  }
1279
1279
  );
1280
1280
  }
1281
- setInterval(() => {
1282
- if (_rafid) {
1283
- instance.pause();
1284
- instance.resume();
1285
- }
1286
- }, 5e3);
1287
1281
  _initialized = true;
1288
1282
  instance.emit("init", instance);
1289
1283
  instance.resume();
1290
1284
  }
1291
- function drawFrame(now) {
1285
+ function drawFrame() {
1292
1286
  if (!settings.animate) {
1293
- return instance.emit("draw");
1294
- } else if (_rafid) {
1295
- _rafid = raf(drawFrame);
1287
+ return instance.emit("draw", _ctx);
1296
1288
  }
1289
+ let now = Date.now();
1297
1290
  let updated = 0;
1298
- let frameTime = (now - _lastFrameTime) / 1e3;
1291
+ let frameTime = now - _lastFrameTime;
1299
1292
  _lastFrameTime = now;
1300
- if (frameTime < 0.1) {
1301
- _accumulated += frameTime;
1302
- while (_accumulated >= _deltaTime) {
1303
- updated++;
1304
- instance.emit("update", _deltaTime * _timeScale, updated);
1305
- instance.def("T", instance.T + _deltaTime * _timeScale);
1306
- _accumulated -= _deltaTime;
1307
- }
1293
+ _accumulated += frameTime < 100 ? frameTime : _fpsInterval;
1294
+ while (_accumulated >= _fpsInterval) {
1295
+ updated++;
1296
+ _accumulated -= _fpsInterval;
1297
+ let dt = _fpsInterval / 1e3 * _timeScale;
1298
+ instance.emit("update", dt, updated);
1299
+ instance.def("T", instance.T + dt);
1308
1300
  }
1309
1301
  if (updated) {
1310
- instance.emit("draw");
1302
+ instance.emit("draw", _ctx);
1303
+ if (updated > 1) {
1304
+ _accumulated = 0;
1305
+ DEV: console.warn(
1306
+ "[litecanvas] the last frame updated " + updated + " times. This can drop the FPS if it keeps happening."
1307
+ );
1308
+ }
1311
1309
  }
1310
+ _rafid = raf(drawFrame);
1312
1311
  }
1313
1312
  function setupCanvas() {
1314
1313
  if ("string" === typeof settings.canvas) {
@@ -1369,7 +1368,6 @@
1369
1368
  }
1370
1369
  instance.textalign("start", "top");
1371
1370
  instance.emit("resized", _scale);
1372
- instance.cls(0);
1373
1371
  if (!settings.animate) {
1374
1372
  raf(drawFrame);
1375
1373
  }
package/dist/dist.js CHANGED
@@ -44,7 +44,7 @@
44
44
  animate: true
45
45
  };
46
46
  settings = Object.assign(defaults, settings);
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 = {};
47
+ let _initialized = false, _plugins = [], _canvas, _scale = 1, _ctx, _outline_fix = 0.5, _timeScale = 1, _lastFrameTime, _fpsInterval = 1e3 / 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 = {};
48
48
  const instance = {
49
49
  /** @type {number} */
50
50
  W: 0,
@@ -663,7 +663,7 @@
663
663
  * @param {number} value
664
664
  */
665
665
  framerate(value) {
666
- _deltaTime = 1 / ~~value;
666
+ _fpsInterval = 1e3 / ~~value;
667
667
  },
668
668
  /**
669
669
  * Returns information about that engine instance.
@@ -678,7 +678,7 @@
678
678
  // 1
679
679
  _initialized,
680
680
  // 2
681
- _deltaTime,
681
+ _fpsInterval / 1e3,
682
682
  // 3
683
683
  _scale,
684
684
  // 4
@@ -732,8 +732,8 @@
732
732
  */
733
733
  resume() {
734
734
  if (_initialized && !_rafid) {
735
- _accumulated = _deltaTime;
736
- _lastFrameTime = performance.now();
735
+ _accumulated = _fpsInterval;
736
+ _lastFrameTime = Date.now();
737
737
  _rafid = raf(drawFrame);
738
738
  }
739
739
  },
@@ -786,7 +786,7 @@
786
786
  // initial y
787
787
  yi: y,
788
788
  // timestamp
789
- t: performance.now()
789
+ t: Date.now()
790
790
  };
791
791
  _taps.set(id, tap);
792
792
  return tap;
@@ -806,7 +806,7 @@
806
806
  /**
807
807
  * @param {{t: number}} tap
808
808
  */
809
- (tap) => tap && performance.now() - tap.t <= 300
809
+ (tap) => tap && Date.now() - tap.t <= 300
810
810
  ), preventDefault = (
811
811
  /**
812
812
  * @param {Event} ev
@@ -971,37 +971,33 @@
971
971
  }
972
972
  );
973
973
  }
974
- setInterval(() => {
975
- if (_rafid) {
976
- instance.pause();
977
- instance.resume();
978
- }
979
- }, 5e3);
980
974
  _initialized = true;
981
975
  instance.emit("init", instance);
982
976
  instance.resume();
983
977
  }
984
- function drawFrame(now) {
978
+ function drawFrame() {
985
979
  if (!settings.animate) {
986
- return instance.emit("draw");
987
- } else if (_rafid) {
988
- _rafid = raf(drawFrame);
980
+ return instance.emit("draw", _ctx);
989
981
  }
982
+ let now = Date.now();
990
983
  let updated = 0;
991
- let frameTime = (now - _lastFrameTime) / 1e3;
984
+ let frameTime = now - _lastFrameTime;
992
985
  _lastFrameTime = now;
993
- if (frameTime < 0.1) {
994
- _accumulated += frameTime;
995
- while (_accumulated >= _deltaTime) {
996
- updated++;
997
- instance.emit("update", _deltaTime * _timeScale, updated);
998
- instance.def("T", instance.T + _deltaTime * _timeScale);
999
- _accumulated -= _deltaTime;
1000
- }
986
+ _accumulated += frameTime < 100 ? frameTime : _fpsInterval;
987
+ while (_accumulated >= _fpsInterval) {
988
+ updated++;
989
+ _accumulated -= _fpsInterval;
990
+ let dt = _fpsInterval / 1e3 * _timeScale;
991
+ instance.emit("update", dt, updated);
992
+ instance.def("T", instance.T + dt);
1001
993
  }
1002
994
  if (updated) {
1003
- instance.emit("draw");
995
+ instance.emit("draw", _ctx);
996
+ if (updated > 1) {
997
+ _accumulated = 0;
998
+ }
1004
999
  }
1000
+ _rafid = raf(drawFrame);
1005
1001
  }
1006
1002
  function setupCanvas() {
1007
1003
  if ("string" === typeof settings.canvas) {
@@ -1042,7 +1038,6 @@
1042
1038
  }
1043
1039
  instance.textalign("start", "top");
1044
1040
  instance.emit("resized", _scale);
1045
- instance.cls(0);
1046
1041
  if (!settings.animate) {
1047
1042
  raf(drawFrame);
1048
1043
  }
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,w=0,v=0,x=1,y=0,b=0,k=0)=>{let E=Math,z=2*E.PI,I=c*=500*z/44100/44100,T=l*=(1-i+2*i*E.random(i=[]))*z/44100,C=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(C*C):E.max(E.min(E.tan(C),1),-1):1-(2*C/z%2+2)%2:1-4*E.abs(E.round(C/z)-C/z):B(C),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))),C+=(F=(l+=c+=d)*E.cos(g*A++))+F*h*B(L**5),S&&++S>p&&(l+=u,T+=u,S=0),!m||++M%m||(l=T,c=I,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,I=[.5,0,1750,,,.3,1,,,,600,.1],T={},C={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?C.clamp(o,i,l):o},norm:(e,t,a)=>C.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(C.rand(e,t+1)),rseed(e){E=~~e},cls(e){null==e?m.clearRect(0,0,m.canvas.width,m.canvas.height):C.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),C.stroke(l)},rectfill(e,t,a,i,l,n){s(m),m[n?"roundRect":"rect"](~~e,~~t,~~a,~~i,n),C.fill(l)},circ(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),C.stroke(i)},circfill(e,t,a,i){s(m),m.arc(~~e,~~t,~~a,0,l),C.fill(i)},oval(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),C.stroke(n)},ovalfill(e,t,a,i,n){s(m),m.ellipse(~~e,~~t,~~a,~~i,0,0,l),C.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),C.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&&C.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=C.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||I,(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)=>(T[e=e.toLowerCase()]=T[e]||new Set,T[e].add(t),()=>T&&T[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){C[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,T,z,I,g,a.zzfxV,E,k,b][e]};return C.emit("stat",i),i.value},quit(){for(let e of(C.pause(),C.emit("quit"),T={},o))e();if(t.global){for(let e in C)delete a[e];delete a.ENGINE}c=!1},pause(){cancelAnimationFrame(y),y=0},resume(){c&&!y&&(x=v,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(","))C[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]&&C.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);C.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)&&C.emit("tapped",i.xi,i.yi,0),C.emit("untap",l,r,0),t.delete(0),s=!1}}),r(a,"mousemove",t=>{o(t);let[a,i]=e(t);C.def("MX",a),C.def("MY",i),s&&(C.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);C.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);C.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)&&C.emit("tapped",i.xi,i.yi,e),C.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))C.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()),C.listen("after:update",()=>t.clear()),C.def("iskeydown",t=>i(e,t)),C.def("iskeypressed",e=>i(t,e))}setInterval(()=>{y&&(C.pause(),C.resume())},5e3),c=!0,C.emit("init",C),C.resume()}function L(e){if(!t.animate)return C.emit("draw");y&&(y=n(L));let a=0,i=(e-w)/1e3;if(w=e,i<.1)for(x+=i;x>=v;)a++,C.emit("update",v*g,a),C.def("T",C.T+v*g),x-=v;a&&C.emit("draw")}function S(){let e=t.width>0?t.width:innerWidth,a=t.width>0?t.height||t.width:innerHeight;if(C.def("W",e),C.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"),C.textalign("start","top"),C.emit("resized",p),C.cls(0),t.animate||n(L)}function M(e,t,a,i,l){if(T[e])for(let n of T[e])n(t,a,i,l)}function N(e,t){let a=e(C,t);for(let e in a)C.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("only one global litecanvas is allowed");Object.assign(a,C),a.ENGINE=C}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),C}})();
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,h=0,m=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,D=0,M=0,N=0,P=k<0?-1:1,F=z*P*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+P*q)/2/O,G=-(P+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,h=44100*h|0,a*=.3*e.zzfxV,P=n+y+o+r+v|0;L<P;i[L++]=N*a)++M%(100*w|0)||(N=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),N=(h?1-b+b*B(z*L/h):1)*(N<0?-1:1)*E.abs(N)**f*(L<n?L/n:L<n+y?1-(L-n)/y*(1-x):L<n+y+o?x:L<P-v?(P-L-v)/r*x:0),N=v?N/2+(v>L?0:(L<P-v?1:(P-L)/v)*i[L-v|0]/2/a):N,k&&(N=j=R*X+G*(X=Y)+R*(Y=N)-W*$-V*($=j))),I+=(F=(l+=c+=d)*E.cos(g*A++))+F*m*B(L**5),S&&++S>p&&(l+=u,C+=u,S=0),!h||++D%h||(l=C,c=T,S=S||1);(a=t.createBuffer(1,P,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,h,m=.5,g=1,w,v=1e3/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?h.clearRect(0,0,h.canvas.width,h.canvas.height):I.rectfill(0,0,h.canvas.width,h.canvas.height,e)},rect(e,t,a,i,l,n){s(h),h[n?"roundRect":"rect"](~~e-m,~~t-m,~~a+2*m,~~i+2*m,n),I.stroke(l)},rectfill(e,t,a,i,l,n){s(h),h[n?"roundRect":"rect"](~~e,~~t,~~a,~~i,n),I.fill(l)},circ(e,t,a,i){s(h),h.arc(~~e,~~t,~~a,0,l),I.stroke(i)},circfill(e,t,a,i){s(h),h.arc(~~e,~~t,~~a,0,l),I.fill(i)},oval(e,t,a,i,n){s(h),h.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.stroke(n)},ovalfill(e,t,a,i,n){s(h),h.ellipse(~~e,~~t,~~a,~~i,0,0,l),I.fill(n)},line(e,t,a,i,l){s(h);let n=.5*(0!==m&&~~e==~~a),o=.5*(0!==m&&~~t==~~i);h.moveTo(~~e+n,~~t+o),h.lineTo(~~a+n,~~i+o),I.stroke(l)},linewidth(e){h.lineWidth=~~e,m=.5*(0!=~~e%2)},linedash(e,t=0){h.setLineDash(e),h.lineDashOffset=t},text(e,t,a,i=3,l="normal"){h.font=`${l} ${k}px ${b}`,h.fillStyle=z[~~i%z.length],h.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){k=e},textalign(e,t){e&&(h.textAlign=e),t&&(h.textBaseline=t)},image(e,t,a){h.drawImage(a,~~e,~~t)},paint(e,t,a,i={}){let l=i.canvas||new OffscreenCanvas(1,1),n=i.scale||1,o=h;if(l.width=e*n,l.height=t*n,(h=l.getContext("2d")).scale(n,n),Array.isArray(a)){let e=0,t=0;for(let i of(h.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(h);return h=o,l.transferToImageBitmap()},ctx:e=>(e&&(h=e),h),push:()=>h.save(),pop:()=>h.restore(),translate:(e,t)=>h.translate(~~e,~~t),scale:(e,t)=>h.scale(e,t||e),rotate:e=>h.rotate(e),alpha(e){h.globalAlpha=I.clamp(e,0,1)},fill(e){h.fillStyle=z[~~e%z.length],h.fill()},stroke(e){h.strokeStyle=z[~~e%z.length],h.stroke()},clip(e){s(h),e(h),h.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?M(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&&(D("before:"+(e=e.toLowerCase()),t,a,i,l),D(e,t,a,i,l),D("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=1e3/~~e},stat(e){let i={index:e,value:[t,c,v/1e3,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=v,w=Date.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)M(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:Date.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&&Date.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),I.resume()}function L(){if(!t.animate)return I.emit("draw",h);let e=Date.now(),a=0,i=e-w;for(w=e,x+=i<100?i:v;x>=v;){a++,x-=v;let e=v/1e3*g;I.emit("update",e,a),I.def("T",I.T+e)}a&&(I.emit("draw",h),a>1&&(x=0)),y=n(L)}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&&(h.imageSmoothingEnabled=!1,u.style.imageRendering="pixelated"),I.textalign("start","top"),I.emit("resized",p),t.animate||n(L)}function D(e,t,a,i,l){if(C[e])for(let n of C[e])n(t,a,i,l)}function M(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 h=(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.93.2",
3
+ "version": "0.94.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>",
package/src/index.js CHANGED
@@ -61,7 +61,7 @@ export default function litecanvas(settings = {}) {
61
61
  /** @type {number} */
62
62
  _lastFrameTime,
63
63
  /** @type {number} duration of a frame at 60 FPS (default) */
64
- _deltaTime = 1 / 60,
64
+ _fpsInterval = 1000 / 60,
65
65
  /** @type {number} */
66
66
  _accumulated,
67
67
  /** @type {number?} */
@@ -1120,7 +1120,7 @@ export default function litecanvas(settings = {}) {
1120
1120
  '[litecanvas] framerate() 1st param must be a positive number'
1121
1121
  )
1122
1122
 
1123
- _deltaTime = 1 / ~~value
1123
+ _fpsInterval = 1000 / ~~value
1124
1124
  },
1125
1125
 
1126
1126
  /**
@@ -1138,7 +1138,7 @@ export default function litecanvas(settings = {}) {
1138
1138
  // 1
1139
1139
  _initialized,
1140
1140
  // 2
1141
- _deltaTime,
1141
+ _fpsInterval / 1000,
1142
1142
  // 3
1143
1143
  _scale,
1144
1144
  // 4
@@ -1210,8 +1210,8 @@ export default function litecanvas(settings = {}) {
1210
1210
  */
1211
1211
  resume() {
1212
1212
  if (_initialized && !_rafid) {
1213
- _accumulated = _deltaTime
1214
- _lastFrameTime = performance.now()
1213
+ _accumulated = _fpsInterval
1214
+ _lastFrameTime = Date.now()
1215
1215
  _rafid = raf(drawFrame)
1216
1216
  }
1217
1217
  },
@@ -1280,7 +1280,7 @@ export default function litecanvas(settings = {}) {
1280
1280
  // initial y
1281
1281
  yi: y,
1282
1282
  // timestamp
1283
- t: performance.now(),
1283
+ t: Date.now(),
1284
1284
  }
1285
1285
  _taps.set(id, tap)
1286
1286
  return tap
@@ -1300,7 +1300,7 @@ export default function litecanvas(settings = {}) {
1300
1300
  /**
1301
1301
  * @param {{t: number}} tap
1302
1302
  */
1303
- (tap) => tap && performance.now() - tap.t <= 300,
1303
+ (tap) => tap && Date.now() - tap.t <= 300,
1304
1304
  preventDefault =
1305
1305
  /**
1306
1306
  * @param {Event} ev
@@ -1506,52 +1506,48 @@ export default function litecanvas(settings = {}) {
1506
1506
  )
1507
1507
  }
1508
1508
 
1509
- // this seems to solve a wierd bug that drop the FPS
1510
- // when switching tabs in the browser
1511
- setInterval(() => {
1512
- if (_rafid) {
1513
- instance.pause()
1514
- instance.resume()
1515
- }
1516
- }, 5000)
1517
-
1518
1509
  // start the engine
1519
1510
  _initialized = true
1520
1511
  instance.emit('init', instance)
1521
1512
  instance.resume()
1522
1513
  }
1523
1514
 
1524
- /**
1525
- * @param {DOMHighResTimeStamp} now
1526
- */
1527
- function drawFrame(now) {
1515
+ function drawFrame() {
1528
1516
  if (!settings.animate) {
1529
- return instance.emit('draw')
1530
- }
1531
- // request the next frame
1532
- // only when the engine loop are not paused (_rafid >= 1)
1533
- else if (_rafid) {
1534
- _rafid = raf(drawFrame)
1517
+ return instance.emit('draw', _ctx)
1535
1518
  }
1536
1519
 
1520
+ let now = Date.now()
1537
1521
  let updated = 0
1538
- let frameTime = (now - _lastFrameTime) / 1000
1522
+ let frameTime = now - _lastFrameTime
1539
1523
 
1540
1524
  _lastFrameTime = now
1525
+ _accumulated += frameTime < 100 ? frameTime : _fpsInterval
1541
1526
 
1542
- if (frameTime < 0.1) {
1543
- _accumulated += frameTime
1544
- while (_accumulated >= _deltaTime) {
1545
- updated++
1546
- instance.emit('update', _deltaTime * _timeScale, updated)
1547
- instance.def('T', instance.T + _deltaTime * _timeScale)
1548
- _accumulated -= _deltaTime
1549
- }
1527
+ while (_accumulated >= _fpsInterval) {
1528
+ updated++
1529
+ _accumulated -= _fpsInterval
1530
+
1531
+ let dt = (_fpsInterval / 1000) * _timeScale
1532
+
1533
+ instance.emit('update', dt, updated)
1534
+ instance.def('T', instance.T + dt)
1550
1535
  }
1551
1536
 
1552
1537
  if (updated) {
1553
- instance.emit('draw')
1538
+ instance.emit('draw', _ctx)
1539
+ if (updated > 1) {
1540
+ _accumulated = 0
1541
+ DEV: console.warn(
1542
+ '[litecanvas] the last frame updated ' +
1543
+ updated +
1544
+ ' times. This can drop the FPS if it keeps happening.'
1545
+ )
1546
+ }
1554
1547
  }
1548
+
1549
+ // request the next frame
1550
+ _rafid = raf(drawFrame)
1555
1551
  }
1556
1552
 
1557
1553
  function setupCanvas() {
@@ -1638,9 +1634,6 @@ export default function litecanvas(settings = {}) {
1638
1634
  // note: not triggered before the "init" event
1639
1635
  instance.emit('resized', _scale)
1640
1636
 
1641
- // paint a temporary background
1642
- instance.cls(0)
1643
-
1644
1637
  // force redraw when the canvas is not animated
1645
1638
  if (!settings.animate) {
1646
1639
  raf(drawFrame)
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.93.2'
2
+ export const version = '0.94.1'
package/types/types.d.ts CHANGED
@@ -613,16 +613,17 @@ type LitecanvasOptions = {
613
613
  /**
614
614
  * Specify your game loop callbacks.
615
615
  * By default use that global functions (if they exist):
616
- * - `window.init(): void`
616
+ * - `window.init(instance: LitecanvasInstance): void`
617
617
  * - `window.update(dt: number): void`
618
- * - `window.draw(): void`
619
- * - `window.resized(): void`
620
- * - `window.tap(tapX: number, tapY: number, tapId: number): void`
621
- * - `window.untap(tapX: number, tapY: number, tapId: number): void`
622
- * - `window.tapped(tapX: number, tapY: number, tapId: number): void`
623
- * - `window.tapping(tapX: number, tapY: number, tapId: number): void`
618
+ * - `window.draw(ctx: CanvasRenderingContext2D): void`
619
+ * - `window.resized(scale: number): void`
620
+ * - `window.tap(tapX: number, tapY: number, touchId: number): void`
621
+ * - `window.untap(tapX: number, tapY: number, touchId: number): void`
622
+ * - `window.tapped(tapX: number, tapY: number, touchId: number): void`
623
+ * - `window.tapping(tapX: number, tapY: number, touchId: number): void`
624
624
  */
625
625
  loop?: LitecanvasGameLoop
626
+
626
627
  /**
627
628
  * default: `true`
628
629
  *
@@ -652,10 +653,10 @@ type LitecanvasGameLoop = {
652
653
  update?: (dt: number) => void
653
654
  draw?: () => void
654
655
  resized?: () => void
655
- tap?: (tapX: number, tapY: number, tapId: number) => void
656
- untap?: (tapX: number, tapY: number, tapId: number) => void
657
- tapped?: (tapX: number, tapY: number, tapId: number) => void
658
- tapping?: (tapX: number, tapY: number, tapId: number) => void
656
+ tap?: (tapX: number, tapY: number, touchId: number) => void
657
+ untap?: (tapX: number, tapY: number, touchId: number) => void
658
+ tapped?: (tapX: number, tapY: number, touchId: number) => void
659
+ tapping?: (tapX: number, tapY: number, touchId: number) => void
659
660
  }
660
661
 
661
662
  type drawCallback = (context: OffscreenCanvasRenderingContext2D) => void