litecanvas 0.85.0 → 0.86.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
@@ -50,16 +50,7 @@
50
50
  animate: true
51
51
  };
52
52
  settings = Object.assign(defaults, settings);
53
- 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], _events = {
54
- init: null,
55
- update: null,
56
- draw: null,
57
- resized: null,
58
- tap: null,
59
- untap: null,
60
- tapping: null,
61
- tapped: null
62
- };
53
+ 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 = {};
63
54
  const instance = {
64
55
  /** @type {number} */
65
56
  W: 0,
@@ -212,6 +203,7 @@
212
203
  DEV: assert(isNumber(value), "norm: 1st param must be a number");
213
204
  DEV: assert(isNumber(start), "norm: 2nd param must be a number");
214
205
  DEV: assert(isNumber(stop), "norm: 3rd param must be a number");
206
+ DEV: assert(start !== stop, "norm: the 2nd param must be different than the 3rd param");
215
207
  return instance.map(value, start, stop, 0, 1);
216
208
  },
217
209
  /**
@@ -412,6 +404,62 @@
412
404
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
413
405
  instance.fill(color);
414
406
  },
407
+ /**
408
+ * Draw a ellipse outline
409
+ *
410
+ * @param {number} x
411
+ * @param {number} y
412
+ * @param {number} radiusX
413
+ * @param {number} radiusY
414
+ * @param {number} [color=0] the color index
415
+ */
416
+ oval(x, y, radiusX, radiusY, color) {
417
+ DEV: assert(isNumber(x), "oval: 1st param must be a number");
418
+ DEV: assert(isNumber(y), "oval: 2nd param must be a number");
419
+ DEV: assert(
420
+ isNumber(radiusX) && radiusX >= 0,
421
+ "oval: 3rd param must be a positive number or zero"
422
+ );
423
+ DEV: assert(
424
+ isNumber(radiusY) && radiusY >= 0,
425
+ "oval: 4th param must be a positive number or zero"
426
+ );
427
+ DEV: assert(
428
+ null == color || isNumber(color) && color >= 0,
429
+ "oval: 5th param must be a positive number or zero"
430
+ );
431
+ _ctx.beginPath();
432
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
433
+ instance.stroke(color);
434
+ },
435
+ /**
436
+ * Draw a color-filled ellipse
437
+ *
438
+ * @param {number} x
439
+ * @param {number} y
440
+ * @param {number} radiusX
441
+ * @param {number} radiusY
442
+ * @param {number} [color=0] the color index
443
+ */
444
+ ovalfill(x, y, radiusX, radiusY, color) {
445
+ DEV: assert(isNumber(x), "ovalfill: 1st param must be a number");
446
+ DEV: assert(isNumber(y), "ovalfill: 2nd param must be a number");
447
+ DEV: assert(
448
+ isNumber(radiusX) && radiusX >= 0,
449
+ "ovalfill: 3rd param must be a positive number or zero"
450
+ );
451
+ DEV: assert(
452
+ isNumber(radiusY) && radiusY >= 0,
453
+ "ovalfill: 4th param must be a positive number or zero"
454
+ );
455
+ DEV: assert(
456
+ null == color || isNumber(color) && color >= 0,
457
+ "ovalfill: 5th param must be a positive number or zero"
458
+ );
459
+ _ctx.beginPath();
460
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
461
+ instance.fill(color);
462
+ },
415
463
  /**
416
464
  * Draw a line
417
465
  *
@@ -812,9 +860,9 @@
812
860
  DEV: assert("string" === typeof eventName, "listen: 1st param must be a string");
813
861
  DEV: assert("function" === typeof callback, "listen: 2nd param must be a function");
814
862
  eventName = eventName.toLowerCase();
815
- _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
816
- _events[eventName].add(callback);
817
- return () => _events[eventName].delete(callback);
863
+ _eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
864
+ _eventListeners[eventName].add(callback);
865
+ return () => _eventListeners && _eventListeners[eventName].delete(callback);
818
866
  },
819
867
  /**
820
868
  * Call all listeners attached to a game event
@@ -906,7 +954,7 @@
906
954
  // 3
907
955
  _scale,
908
956
  // 4
909
- _events,
957
+ _eventListeners,
910
958
  // 5
911
959
  _colors,
912
960
  // 6
@@ -934,10 +982,10 @@
934
982
  cancelAnimationFrame(_rafid);
935
983
  _rafid = 0;
936
984
  instance.emit("quit");
985
+ _eventListeners = {};
937
986
  for (const removeListener of _browserEventListeners) {
938
987
  removeListener();
939
988
  }
940
- _events = {};
941
989
  if (settings.global) {
942
990
  for (const key in instance) {
943
991
  delete root[key];
@@ -947,12 +995,12 @@
947
995
  _initialized = false;
948
996
  }
949
997
  };
950
- for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
998
+ for (const k of _mathFunctions.split(",")) {
951
999
  instance[k] = math[k];
952
1000
  }
953
1001
  function init() {
954
1002
  const source = settings.loop ? settings.loop : root;
955
- for (const event in _events) {
1003
+ for (const event of _coreEvents.split(",")) {
956
1004
  if (source[event]) instance.listen(event, source[event]);
957
1005
  }
958
1006
  for (const [callback, config] of _plugins) {
@@ -1233,17 +1281,19 @@
1233
1281
  `Litecanvas' option "width" is required when the option "height" is defined`
1234
1282
  );
1235
1283
  const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
1236
- instance.def("W", _canvas.width = width);
1237
- instance.def("H", _canvas.height = height);
1284
+ instance.def("W", width);
1285
+ instance.def("H", height);
1286
+ _canvas.width = width;
1287
+ _canvas.height = height;
1238
1288
  if (settings.autoscale) {
1239
1289
  if (!_canvas.style.display) {
1240
1290
  _canvas.style.display = "block";
1241
1291
  _canvas.style.margin = "auto";
1242
1292
  }
1243
- _scale = math.min(root.innerWidth / instance.W, root.innerHeight / instance.H);
1293
+ _scale = math.min(root.innerWidth / width, root.innerHeight / height);
1244
1294
  _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
1245
- _canvas.style.width = instance.W * _scale + "px";
1246
- _canvas.style.height = instance.H * _scale + "px";
1295
+ _canvas.style.width = width * _scale + "px";
1296
+ _canvas.style.height = height * _scale + "px";
1247
1297
  }
1248
1298
  if (!settings.antialias || settings.pixelart) {
1249
1299
  _ctx.imageSmoothingEnabled = false;
@@ -1256,8 +1306,8 @@
1256
1306
  }
1257
1307
  }
1258
1308
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1259
- if (!_events[eventName]) return;
1260
- for (const callback of _events[eventName]) {
1309
+ if (!_eventListeners[eventName]) return;
1310
+ for (const callback of _eventListeners[eventName]) {
1261
1311
  callback(arg1, arg2, arg3, arg4);
1262
1312
  }
1263
1313
  }
package/dist/dist.js CHANGED
@@ -45,16 +45,7 @@
45
45
  animate: true
46
46
  };
47
47
  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], _events = {
49
- init: null,
50
- update: null,
51
- draw: null,
52
- resized: null,
53
- tap: null,
54
- untap: null,
55
- tapping: null,
56
- tapped: null
57
- };
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 = {};
58
49
  const instance = {
59
50
  /** @type {number} */
60
51
  W: 0,
@@ -306,6 +297,34 @@
306
297
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI);
307
298
  instance.fill(color);
308
299
  },
300
+ /**
301
+ * Draw a ellipse outline
302
+ *
303
+ * @param {number} x
304
+ * @param {number} y
305
+ * @param {number} radiusX
306
+ * @param {number} radiusY
307
+ * @param {number} [color=0] the color index
308
+ */
309
+ oval(x, y, radiusX, radiusY, color) {
310
+ _ctx.beginPath();
311
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
312
+ instance.stroke(color);
313
+ },
314
+ /**
315
+ * Draw a color-filled ellipse
316
+ *
317
+ * @param {number} x
318
+ * @param {number} y
319
+ * @param {number} radiusX
320
+ * @param {number} radiusY
321
+ * @param {number} [color=0] the color index
322
+ */
323
+ ovalfill(x, y, radiusX, radiusY, color) {
324
+ _ctx.beginPath();
325
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI);
326
+ instance.fill(color);
327
+ },
309
328
  /**
310
329
  * Draw a line
311
330
  *
@@ -613,9 +632,9 @@
613
632
  */
614
633
  listen(eventName, callback) {
615
634
  eventName = eventName.toLowerCase();
616
- _events[eventName] = _events[eventName] || /* @__PURE__ */ new Set();
617
- _events[eventName].add(callback);
618
- return () => _events[eventName].delete(callback);
635
+ _eventListeners[eventName] = _eventListeners[eventName] || /* @__PURE__ */ new Set();
636
+ _eventListeners[eventName].add(callback);
637
+ return () => _eventListeners && _eventListeners[eventName].delete(callback);
619
638
  },
620
639
  /**
621
640
  * Call all listeners attached to a game event
@@ -689,7 +708,7 @@
689
708
  // 3
690
709
  _scale,
691
710
  // 4
692
- _events,
711
+ _eventListeners,
693
712
  // 5
694
713
  _colors,
695
714
  // 6
@@ -717,10 +736,10 @@
717
736
  cancelAnimationFrame(_rafid);
718
737
  _rafid = 0;
719
738
  instance.emit("quit");
739
+ _eventListeners = {};
720
740
  for (const removeListener of _browserEventListeners) {
721
741
  removeListener();
722
742
  }
723
- _events = {};
724
743
  if (settings.global) {
725
744
  for (const key in instance) {
726
745
  delete root[key];
@@ -730,12 +749,12 @@
730
749
  _initialized = false;
731
750
  }
732
751
  };
733
- for (const k of "PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(",")) {
752
+ for (const k of _mathFunctions.split(",")) {
734
753
  instance[k] = math[k];
735
754
  }
736
755
  function init() {
737
756
  const source = settings.loop ? settings.loop : root;
738
- for (const event in _events) {
757
+ for (const event of _coreEvents.split(",")) {
739
758
  if (source[event]) instance.listen(event, source[event]);
740
759
  }
741
760
  for (const [callback, config] of _plugins) {
@@ -991,17 +1010,19 @@
991
1010
  }
992
1011
  function resizeCanvas() {
993
1012
  const width = settings.width || root.innerWidth, height = settings.height || settings.width || root.innerHeight;
994
- instance.def("W", _canvas.width = width);
995
- instance.def("H", _canvas.height = height);
1013
+ instance.def("W", width);
1014
+ instance.def("H", height);
1015
+ _canvas.width = width;
1016
+ _canvas.height = height;
996
1017
  if (settings.autoscale) {
997
1018
  if (!_canvas.style.display) {
998
1019
  _canvas.style.display = "block";
999
1020
  _canvas.style.margin = "auto";
1000
1021
  }
1001
- _scale = math.min(root.innerWidth / instance.W, root.innerHeight / instance.H);
1022
+ _scale = math.min(root.innerWidth / width, root.innerHeight / height);
1002
1023
  _scale = (settings.pixelart ? ~~_scale : _scale) || 1;
1003
- _canvas.style.width = instance.W * _scale + "px";
1004
- _canvas.style.height = instance.H * _scale + "px";
1024
+ _canvas.style.width = width * _scale + "px";
1025
+ _canvas.style.height = height * _scale + "px";
1005
1026
  }
1006
1027
  if (!settings.antialias || settings.pixelart) {
1007
1028
  _ctx.imageSmoothingEnabled = false;
@@ -1014,8 +1035,8 @@
1014
1035
  }
1015
1036
  }
1016
1037
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1017
- if (!_events[eventName]) return;
1018
- for (const callback of _events[eventName]) {
1038
+ if (!_eventListeners[eventName]) return;
1039
+ for (const callback of _eventListeners[eventName]) {
1019
1040
  callback(arg1, arg2, arg3, arg4);
1020
1041
  }
1021
1042
  }
package/dist/dist.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{var e=["#111","#6a7799","#aec2c2","#FFF1E8","#e83b3b","#fabc20","#155fd9","#3cbcfc","#327345","#63c64d","#6c2c1f","#ac7c00"];globalThis.litecanvas=function(t={}){let a=window,n=Math,l=2*n.PI,i=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,n=.05,l=220,i=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,w=0,v=0,x=1,b=0,y=0,k=0)=>{let E=Math,z=2*E.PI,T=c*=500*z/44100/44100,C=l*=(1-n+2*n*E.random(n=[]))*z/44100,I=0,P=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,H=E.cos(D),N=E.sin,W=N(D)/4,F=1+W,q=-2*H/F,B=(1-W)/F,O=(1+M*H)/2/F,V=-(M+H)/F,R=0,G=0,$=0,j=0;for(i=44100*i+9,b*=44100,r*=44100,o*=44100,v*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=i+b+r+o+v|0;A<M;n[A++]=Y*a)++X%(100*w|0)||(Y=s?1<s?2<s?3<s?N(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):N(I),Y=(h?1-y+y*N(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<i?A/i:A<i+b?1-(A-i)/b*(1-x):A<i+b+r?x:A<M-v?(M-A-v)/o*x:0),Y=v?Y/2+(v>A?0:(A<M-v?1:(M-A)/v)*n[A-v|0]/2/a):Y,k&&(Y=j=O*R+V*(R=G)+O*(G=Y)-B*$-q*($=j))),I+=(D=(l+=c+=d)*E.cos(m*P++))+D*g*N(A**5),L&&++L>u&&(l+=p,C+=p,L=0),!h||++S%h||(l=C,c=T,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(n),(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 f=!1,c=[],d,p=1,u,h=.5,g=1,m,w=1/60,v=0,x,b="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],T={init:null,update:null,draw:null,resized:null,tap:null,untap:null,tapping:null,tapped:null},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=>n.PI/180*e,rad2deg:e=>180/n.PI*e,round:(e,t=0)=>{if(!t)return n.round(e);let a=10**t;return n.round(e*a)/a},clamp:(e,t,a)=>e<t?t:e>a?a:e,wrap:(e,t,a)=>e-(a-t)*n.floor((e-t)/(a-t)),map(e,t,a,n,l,i){let r=(e-t)/(a-t)*(l-n)+n;return i?C.clamp(r,n,l):r},norm:(e,t,a)=>C.map(e,t,a,0,1),wave:(e,t,a,n=Math.sin)=>e+(n(a)+1)/2*(t-e),rand:(e=0,t=1)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>n.floor(C.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):C.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,n,l,i){u.beginPath(),u[i?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~n+2*h,i),C.stroke(l)},rectfill(e,t,a,n,l,i){u.beginPath(),u[i?"roundRect":"rect"](~~e,~~t,~~a,~~n,i),C.fill(l)},circ(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),C.stroke(n)},circfill(e,t,a,n){u.beginPath(),u.arc(~~e,~~t,~~a,0,l),C.fill(n)},line(e,t,a,n,l){u.beginPath();let i=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~n);u.moveTo(~~e+i,~~t+r),u.lineTo(~~a+i,~~n+r),C.stroke(l)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,n=3,l="normal"){u.font=`${l} ${y}px ${b}`,u.fillStyle=E[~~n%E.length],u.fillText(a,~~e,~~t)},textfont(e){b=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,n={}){let l=n.canvas||new OffscreenCanvas(1,1),i=n.scale||1,r=u;if(l.width=e*i,l.height=t*i,(u=l.getContext("2d")).scale(i,i),Array.isArray(a)){let e=0,t=0;for(let n of(u.imageSmoothingEnabled=!1,a)){for(let a of n)" "!==a&&"."!==a&&C.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,l.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=C.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,n=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==n)&&((e=e.slice())[0]=n*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(T[e=e.toLowerCase()]=T[e]||new Set,T[e].add(t),()=>T[e].delete(t)),emit(e,t,a,n,l){f&&(L("before:"+(e=e.toLowerCase()),t,a,n,l),L(e,t,a,n,l),L("after:"+e,t,a,n,l))},pal(t=e){E=t},def(e,n){C[e]=n,t.global&&(a[e]=n)},timescale(e){g=e},framerate(e){w=1/~~e},stat(e){let n={index:e,value:[t,f,x,p,T,E,z,g,a.zzfxV||1,k,y,b][e]};return C.emit("stat",n),n.value},quit(){for(let e of(cancelAnimationFrame(x),x=0,C.emit("quit"),r))e();if(T={},t.global){for(let e in C)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))C[e]=n[e];function I(){let e=t.loop?t.loop:a;for(let t in T)e[t]&&C.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&o(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,n=(e,a,n)=>{let l={x:a,y:n,startX:a,startY:n,ts:performance.now()};return t.set(e,l),l},l=(e,a,l)=>{let i=t.get(e)||n(e);i.x=a,i.y=l},i=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(d,"mousedown",t=>{if(0===t.button){r(t);let[a,l]=e(t.pageX,t.pageY);C.emit("tap",a,l,0),n(0,a,l),s=!0}}),o(d,"mouseup",a=>{if(0===a.button){r(a);let n=t.get(0),[l,o]=e(a.pageX,a.pageY);i(n)&&C.emit("tapped",n.startX,n.startY,0),C.emit("untap",l,o,0),t.delete(0),s=!1}}),o(d,"mousemove",t=>{r(t);let[a,n]=e(t.pageX,t.pageY);C.def("MX",a),C.def("MY",n),s&&(C.emit("tapping",a,n,0),l(0,a,n))}),o(d,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,l]=e(a.pageX,a.pageY);C.emit("tap",t,l,a.identifier+1),n(a.identifier+1,t,l)}}),o(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);C.emit("tapping",t,n,a.identifier+1),l(a.identifier+1,t,n)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,n]of t)a.includes(e)||(i(n)&&C.emit("tapped",n.startX,n.startY,e),C.emit("untap",n.x,n.y,e),t.delete(e))};o(d,"touchend",f),o(d,"touchcancel",f),o(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,n=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let n=a.key.toLowerCase();e.has(n)||(e.add(n),t.add(n))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),C.listen("after:update",()=>t.clear()),C.def("iskeydown",t=>n(e,t)),C.def("iskeypressed",e=>n(t,e))}f=!0,C.emit("init",C),m=performance.now(),x=i(P)}function P(e){let a=0;if(t.animate){for(v+=n.min(.1,(e-m)/1e3),m=e;v>=w;)a++,C.emit("update",w*g,a),C.def("T",C.T+w*g),v-=w;x&&(x=i(P))}else a=1;a&&(C.textalign("start","top"),C.emit("draw"))}function A(){let e=t.width||a.innerWidth,l=t.height||t.width||a.innerHeight;C.def("W",d.width=e),C.def("H",d.height=l),t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=n.min(a.innerWidth/C.W,a.innerHeight/C.H),p=(t.pixelart?~~p:p)||1,d.style.width=C.W*p+"px",d.style.height=C.H*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),C.emit("resized",p),C.cls(0),t.animate||i(P)}function L(e,t,a,n,l){if(T[e])for(let i of T[e])i(t,a,n,l)}function S(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("two global litecanvas detected");Object.assign(a,C),a.ENGINE=C}return u=(d=(d="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),o(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>i(I)):i(I),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,n=2*i.PI,l=requestAnimationFrame,r=[],o=(e,t,a)=>{e.addEventListener(t,a,!1),r.push(()=>e.removeEventListener(t,a,!1))},s=(e=>{let t=new AudioContext;return e.zzfxV=1,(a=1,i=.05,n=220,l=0,r=0,o=.1,s=0,f=1,c=0,d=0,p=0,u=0,h=0,g=0,m=0,v=0,w=0,b=1,x=0,y=0,k=0)=>{let E=Math,z=2*E.PI,P=c*=500*z/44100/44100,T=n*=(1-i+2*i*E.random(i=[]))*z/44100,C=0,I=0,A=0,L=1,S=0,X=0,Y=0,M=k<0?-1:1,D=z*M*k*2/44100,N=E.cos(D),F=E.sin,q=F(D)/4,B=1+q,H=-2*N/B,O=(1-q)/B,V=(1+M*N)/2/B,W=-(M+N)/B,R=0,G=0,$=0,j=0;for(l=44100*l+9,x*=44100,r*=44100,o*=44100,w*=44100,d*=500*z/85766121e6,m*=z/44100,p*=z/44100,u*=44100,h=44100*h|0,a*=.3*e.zzfxV,M=l+x+r+o+w|0;A<M;i[A++]=Y*a)++X%(100*v|0)||(Y=s?1<s?2<s?3<s?F(C*C):E.max(E.min(E.tan(C),1),-1):1-(2*C/z%2+2)%2:1-4*E.abs(E.round(C/z)-C/z):F(C),Y=(h?1-y+y*F(z*A/h):1)*(Y<0?-1:1)*E.abs(Y)**f*(A<l?A/l:A<l+x?1-(A-l)/x*(1-b):A<l+x+r?b:A<M-w?(M-A-w)/o*b:0),Y=w?Y/2+(w>A?0:(A<M-w?1:(M-A)/w)*i[A-w|0]/2/a):Y,k&&(Y=j=V*R+W*(R=G)+V*(G=Y)-O*$-H*($=j))),C+=(D=(n+=c+=d)*E.cos(m*I++))+D*g*F(A**5),L&&++L>u&&(n+=p,T+=p,L=0),!h||++S%h||(n=T,c=P,L=L||1);(a=t.createBuffer(1,M,44100)).getChannelData(0).set(i),(n=t.createBufferSource()).buffer=a,n.connect(t.destination),n.start()}})(a);t=Object.assign({width:null,height:null,autoscale:!0,pixelart:!1,antialias:!1,canvas:null,global:!0,loop:null,tapEvents:!0,keyboardEvents:!0,animate:!0},t);let f=!1,c=[],d,p=1,u,h=.5,g=1,m,v=1/60,w=0,b,x="sans-serif",y=20,k=Date.now(),E=e,z=[.5,0,1750,,,.3,1,,,,600,.1],P={},T={W:0,H:0,T:0,MX:-1,MY:-1,TWO_PI:n,HALF_PI:n/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,n,l){let r=(e-t)/(a-t)*(n-i)+i;return l?T.clamp(r,i,n):r},norm:(e,t,a)=>T.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)=>(k=(1664525*k+0x3c6ef35f)%0x100000000)/0x100000000*(t-e)+e,randi:(e=0,t=1)=>i.floor(T.rand(e,t+1)),rseed(e){k=~~e},cls(e){null==e?u.clearRect(0,0,u.canvas.width,u.canvas.height):T.rectfill(0,0,u.canvas.width,u.canvas.height,e)},rect(e,t,a,i,n,l){u.beginPath(),u[l?"roundRect":"rect"](~~e-h,~~t-h,~~a+2*h,~~i+2*h,l),T.stroke(n)},rectfill(e,t,a,i,n,l){u.beginPath(),u[l?"roundRect":"rect"](~~e,~~t,~~a,~~i,l),T.fill(n)},circ(e,t,a,i){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.stroke(i)},circfill(e,t,a,i){u.beginPath(),u.arc(~~e,~~t,~~a,0,n),T.fill(i)},oval(e,t,a,i,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~i,0,0,n),T.stroke(l)},ovalfill(e,t,a,i,l){u.beginPath(),u.ellipse(~~e,~~t,~~a,~~i,0,0,n),T.fill(l)},line(e,t,a,i,n){u.beginPath();let l=.5*(0!==h&&~~e==~~a),r=.5*(0!==h&&~~t==~~i);u.moveTo(~~e+l,~~t+r),u.lineTo(~~a+l,~~i+r),T.stroke(n)},linewidth(e){u.lineWidth=~~e,h=.5*(0!=~~e%2)},linedash(e,t=0){u.setLineDash(e),u.lineDashOffset=t},text(e,t,a,i=3,n="normal"){u.font=`${n} ${y}px ${x}`,u.fillStyle=E[~~i%E.length],u.fillText(a,~~e,~~t)},textfont(e){x=e},textsize(e){y=e},textalign(e,t){e&&(u.textAlign=e),t&&(u.textBaseline=t)},image(e,t,a){u.drawImage(a,~~e,~~t)},paint(e,t,a,i={}){let n=i.canvas||new OffscreenCanvas(1,1),l=i.scale||1,r=u;if(n.width=e*l,n.height=t*l,(u=n.getContext("2d")).scale(l,l),Array.isArray(a)){let e=0,t=0;for(let i of(u.imageSmoothingEnabled=!1,a)){for(let a of i)" "!==a&&"."!==a&&T.rectfill(e,t,1,1,parseInt(a,16)),e++;t++,e=0}}else a(u);return u=r,n.transferToImageBitmap()},ctx:e=>(e&&(u=e),u),push:()=>u.save(),pop:()=>u.restore(),translate:(e,t)=>u.translate(~~e,~~t),scale:(e,t)=>u.scale(e,t||e),rotate:e=>u.rotate(e),alpha(e){u.globalAlpha=T.clamp(e,0,1)},path:e=>new Path2D(e),fill(e,t){u.fillStyle=E[~~e%E.length],t?u.fill(t):u.fill()},stroke(e,t){u.strokeStyle=E[~~e%E.length],t?u.stroke(t):u.stroke()},clip(e){u.clip(e)},sfx:(e,t=0,i=1)=>!(a.zzfxV<=0)&&(!navigator.userActivation||!!navigator.userActivation.hasBeenActive)&&(e=e||z,(0!==t||1!==i)&&((e=e.slice())[0]=i*(e[0]||1),e[10]=~~e[10]+t),s.apply(0,e),e),volume(e){a.zzfxV=e},canvas:()=>d,use(e,t={}){f?S(e,t):c.push([e,t])},listen:(e,t)=>(P[e=e.toLowerCase()]=P[e]||new Set,P[e].add(t),()=>P&&P[e].delete(t)),emit(e,t,a,i,n){f&&(L("before:"+(e=e.toLowerCase()),t,a,i,n),L(e,t,a,i,n),L("after:"+e,t,a,i,n))},pal(t=e){E=t},def(e,i){T[e]=i,t.global&&(a[e]=i)},timescale(e){g=e},framerate(e){v=1/~~e},stat(e){let i={index:e,value:[t,f,b,p,P,E,z,g,a.zzfxV||1,k,y,x][e]};return T.emit("stat",i),i.value},quit(){for(let e of(cancelAnimationFrame(b),b=0,T.emit("quit"),P={},r))e();if(t.global){for(let e in T)delete a[e];delete a.ENGINE}f=!1}};for(let e of"PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp".split(","))T[e]=i[e];function C(){let e=t.loop?t.loop:a;for(let t of"init,update,draw,tap,untap,tapping,tapped,resized".split(","))e[t]&&T.listen(t,e[t]);for(let[e,t]of c)S(e,t);if(t.autoscale&&o(a,"resize",A),t.tapEvents){let e=(e,t)=>[(e-d.offsetLeft)/p,(t-d.offsetTop)/p],t=new Map,i=(e,a,i)=>{let n={x:a,y:i,startX:a,startY:i,ts:performance.now()};return t.set(e,n),n},n=(e,a,n)=>{let l=t.get(e)||i(e);l.x=a,l.y=n},l=e=>e&&performance.now()-e.ts<=300,r=e=>e.preventDefault(),s=!1;o(d,"mousedown",t=>{if(0===t.button){r(t);let[a,n]=e(t.pageX,t.pageY);T.emit("tap",a,n,0),i(0,a,n),s=!0}}),o(d,"mouseup",a=>{if(0===a.button){r(a);let i=t.get(0),[n,o]=e(a.pageX,a.pageY);l(i)&&T.emit("tapped",i.startX,i.startY,0),T.emit("untap",n,o,0),t.delete(0),s=!1}}),o(d,"mousemove",t=>{r(t);let[a,i]=e(t.pageX,t.pageY);T.def("MX",a),T.def("MY",i),s&&(T.emit("tapping",a,i,0),n(0,a,i))}),o(d,"touchstart",t=>{for(let a of(r(t),t.changedTouches)){let[t,n]=e(a.pageX,a.pageY);T.emit("tap",t,n,a.identifier+1),i(a.identifier+1,t,n)}}),o(d,"touchmove",t=>{for(let a of(r(t),t.changedTouches)){let[t,i]=e(a.pageX,a.pageY);T.emit("tapping",t,i,a.identifier+1),n(a.identifier+1,t,i)}});let f=e=>{r(e);let a=[];if(e.targetTouches.length>0)for(let t of e.targetTouches)a.push(t.identifier+1);for(let[e,i]of t)a.includes(e)||(l(i)&&T.emit("tapped",i.startX,i.startY,e),T.emit("untap",i.x,i.y,e),t.delete(e))};o(d,"touchend",f),o(d,"touchcancel",f),o(a,"blur",()=>{for(let[e,a]of(s=!1,t))T.emit("untap",a.x,a.y,e),t.delete(e)})}if(t.keyboardEvents){let e=new Set,t=new Set,i=(e,t="")=>(t=t.toLowerCase())?e.has("space"===t?" ":t):e.size>0;o(a,"keydown",a=>{let i=a.key.toLowerCase();e.has(i)||(e.add(i),t.add(i))}),o(a,"keyup",t=>{e.delete(t.key.toLowerCase())}),o(a,"blur",()=>e.clear()),T.listen("after:update",()=>t.clear()),T.def("iskeydown",t=>i(e,t)),T.def("iskeypressed",e=>i(t,e))}f=!0,T.emit("init",T),m=performance.now(),b=l(I)}function I(e){let a=0;if(t.animate){for(w+=i.min(.1,(e-m)/1e3),m=e;w>=v;)a++,T.emit("update",v*g,a),T.def("T",T.T+v*g),w-=v;b&&(b=l(I))}else a=1;a&&(T.textalign("start","top"),T.emit("draw"))}function A(){let e=t.width||a.innerWidth,n=t.height||t.width||a.innerHeight;T.def("W",e),T.def("H",n),d.width=e,d.height=n,t.autoscale&&(d.style.display||(d.style.display="block",d.style.margin="auto"),p=i.min(a.innerWidth/e,a.innerHeight/n),p=(t.pixelart?~~p:p)||1,d.style.width=e*p+"px",d.style.height=n*p+"px"),(!t.antialias||t.pixelart)&&(u.imageSmoothingEnabled=!1,d.style.imageRendering="pixelated"),T.emit("resized",p),T.cls(0),t.animate||l(I)}function L(e,t,a,i,n){if(P[e])for(let l of P[e])l(t,a,i,n)}function S(e,t){let a=e(T,t);for(let e in a)T.def(e,a[e])}if(t.global){if(a.ENGINE)throw Error("two global litecanvas detected");Object.assign(a,T),a.ENGINE=T}return u=(d=(d="string"==typeof t.canvas?document.querySelector(t.canvas):t.canvas)||document.createElement("canvas")).getContext("2d"),o(d,"click",()=>a.focus()),d.style="",A(),d.parentNode||document.body.appendChild(d),"loading"===document.readyState?o(a,"DOMContentLoaded",()=>l(C)):l(C),T}})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litecanvas",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "Lightweight HTML5 canvas 2D game engine suitable for small projects and creative coding. Inspired by PICO-8 and P5/Processing.",
5
5
  "license": "MIT",
6
6
  "author": "Luiz Bills <luizbills@pm.me>",
@@ -13,7 +13,7 @@
13
13
  "esbuild": "^0.25.5",
14
14
  "gzip-size": "^7.0.0",
15
15
  "jsdom": "^26.1.0",
16
- "prettier": "^3.6.1",
16
+ "prettier": "^3.6.2",
17
17
  "tap-min": "^3.0.0"
18
18
  },
19
19
  "homepage": "https://litecanvas.github.io/about.html",
package/src/index.js CHANGED
@@ -74,21 +74,15 @@ export default function litecanvas(settings = {}) {
74
74
  _colors = defaultPalette,
75
75
  /** @type {number[]} */
76
76
  _defaultSound = [0.5, 0, 1750, , , 0.3, 1, , , , 600, 0.1],
77
+ /** @type {string} */
78
+ _coreEvents = 'init,update,draw,tap,untap,tapping,tapped,resized',
79
+ /** @type {string} list of functions copied from `Math` module*/
80
+ _mathFunctions =
81
+ 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp',
77
82
  /**
78
- * default game events
79
- *
80
- * @type {Object<string,Set<Function>>}
83
+ * @type {Object<string,Set<Function>>} game event listeners
81
84
  */
82
- _events = {
83
- init: null,
84
- update: null,
85
- draw: null,
86
- resized: null,
87
- tap: null,
88
- untap: null,
89
- tapping: null,
90
- tapped: null,
91
- }
85
+ _eventListeners = {}
92
86
 
93
87
  /** @type {Omit<LitecanvasInstance,'PI'|'sin'|'cos'|'atan2'|'hypot'|'tan'|'abs'|'ceil'|'floor'|'trunc'|'min'|'max'|'pow'|'sqrt'|'sign'|'exp'|'iskeydown'|'iskeypressed'>} */
94
88
  const instance = {
@@ -264,6 +258,7 @@ export default function litecanvas(settings = {}) {
264
258
  DEV: assert(isNumber(value), 'norm: 1st param must be a number')
265
259
  DEV: assert(isNumber(start), 'norm: 2nd param must be a number')
266
260
  DEV: assert(isNumber(stop), 'norm: 3rd param must be a number')
261
+ DEV: assert(start !== stop, 'norm: the 2nd param must be different than the 3rd param')
267
262
 
268
263
  return instance.map(value, start, stop, 0, 1)
269
264
  },
@@ -485,6 +480,66 @@ export default function litecanvas(settings = {}) {
485
480
  instance.fill(color)
486
481
  },
487
482
 
483
+ /**
484
+ * Draw a ellipse outline
485
+ *
486
+ * @param {number} x
487
+ * @param {number} y
488
+ * @param {number} radiusX
489
+ * @param {number} radiusY
490
+ * @param {number} [color=0] the color index
491
+ */
492
+ oval(x, y, radiusX, radiusY, color) {
493
+ DEV: assert(isNumber(x), 'oval: 1st param must be a number')
494
+ DEV: assert(isNumber(y), 'oval: 2nd param must be a number')
495
+ DEV: assert(
496
+ isNumber(radiusX) && radiusX >= 0,
497
+ 'oval: 3rd param must be a positive number or zero'
498
+ )
499
+ DEV: assert(
500
+ isNumber(radiusY) && radiusY >= 0,
501
+ 'oval: 4th param must be a positive number or zero'
502
+ )
503
+ DEV: assert(
504
+ null == color || (isNumber(color) && color >= 0),
505
+ 'oval: 5th param must be a positive number or zero'
506
+ )
507
+
508
+ _ctx.beginPath()
509
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
510
+ instance.stroke(color)
511
+ },
512
+
513
+ /**
514
+ * Draw a color-filled ellipse
515
+ *
516
+ * @param {number} x
517
+ * @param {number} y
518
+ * @param {number} radiusX
519
+ * @param {number} radiusY
520
+ * @param {number} [color=0] the color index
521
+ */
522
+ ovalfill(x, y, radiusX, radiusY, color) {
523
+ DEV: assert(isNumber(x), 'ovalfill: 1st param must be a number')
524
+ DEV: assert(isNumber(y), 'ovalfill: 2nd param must be a number')
525
+ DEV: assert(
526
+ isNumber(radiusX) && radiusX >= 0,
527
+ 'ovalfill: 3rd param must be a positive number or zero'
528
+ )
529
+ DEV: assert(
530
+ isNumber(radiusY) && radiusY >= 0,
531
+ 'ovalfill: 4th param must be a positive number or zero'
532
+ )
533
+ DEV: assert(
534
+ null == color || (isNumber(color) && color >= 0),
535
+ 'ovalfill: 5th param must be a positive number or zero'
536
+ )
537
+
538
+ _ctx.beginPath()
539
+ _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
540
+ instance.fill(color)
541
+ },
542
+
488
543
  /**
489
544
  * Draw a line
490
545
  *
@@ -956,11 +1011,11 @@ export default function litecanvas(settings = {}) {
956
1011
 
957
1012
  eventName = eventName.toLowerCase()
958
1013
 
959
- _events[eventName] = _events[eventName] || new Set()
960
- _events[eventName].add(callback)
1014
+ _eventListeners[eventName] = _eventListeners[eventName] || new Set()
1015
+ _eventListeners[eventName].add(callback)
961
1016
 
962
1017
  // return a function to remove this event listener
963
- return () => _events[eventName].delete(callback)
1018
+ return () => _eventListeners && _eventListeners[eventName].delete(callback)
964
1019
  },
965
1020
 
966
1021
  /**
@@ -1063,7 +1118,7 @@ export default function litecanvas(settings = {}) {
1063
1118
  // 3
1064
1119
  _scale,
1065
1120
  // 4
1066
- _events,
1121
+ _eventListeners,
1067
1122
  // 5
1068
1123
  _colors,
1069
1124
  // 6
@@ -1101,14 +1156,14 @@ export default function litecanvas(settings = {}) {
1101
1156
  // emit "quit" event to manual clean ups
1102
1157
  instance.emit('quit')
1103
1158
 
1159
+ // clear all engine event listeners
1160
+ _eventListeners = {}
1161
+
1104
1162
  // clear all browser event listeners
1105
1163
  for (const removeListener of _browserEventListeners) {
1106
1164
  removeListener()
1107
1165
  }
1108
1166
 
1109
- // clear all engine event listeners
1110
- _events = {}
1111
-
1112
1167
  // maybe clear global context
1113
1168
  if (settings.global) {
1114
1169
  for (const key in instance) {
@@ -1124,7 +1179,7 @@ export default function litecanvas(settings = {}) {
1124
1179
  }
1125
1180
 
1126
1181
  // prettier-ignore
1127
- for (const k of 'PI,sin,cos,atan2,hypot,tan,abs,ceil,floor,trunc,min,max,pow,sqrt,sign,exp'.split(',')) {
1182
+ for (const k of _mathFunctions.split(',')) {
1128
1183
  // import native Math functions
1129
1184
  instance[k] = math[k]
1130
1185
  }
@@ -1132,7 +1187,7 @@ export default function litecanvas(settings = {}) {
1132
1187
  function init() {
1133
1188
  // setup default event listeners
1134
1189
  const source = settings.loop ? settings.loop : root
1135
- for (const event in _events) {
1190
+ for (const event of _coreEvents.split(',')) {
1136
1191
  if (source[event]) instance.listen(event, source[event])
1137
1192
  }
1138
1193
 
@@ -1486,8 +1541,11 @@ export default function litecanvas(settings = {}) {
1486
1541
  const width = settings.width || root.innerWidth,
1487
1542
  height = settings.height || settings.width || root.innerHeight
1488
1543
 
1489
- instance.def('W', (_canvas.width = width))
1490
- instance.def('H', (_canvas.height = height))
1544
+ instance.def('W', width)
1545
+ instance.def('H', height)
1546
+
1547
+ _canvas.width = width
1548
+ _canvas.height = height
1491
1549
 
1492
1550
  if (settings.autoscale) {
1493
1551
  if (!_canvas.style.display) {
@@ -1495,11 +1553,11 @@ export default function litecanvas(settings = {}) {
1495
1553
  _canvas.style.margin = 'auto'
1496
1554
  }
1497
1555
 
1498
- _scale = math.min(root.innerWidth / instance.W, root.innerHeight / instance.H)
1556
+ _scale = math.min(root.innerWidth / width, root.innerHeight / height)
1499
1557
  _scale = (settings.pixelart ? ~~_scale : _scale) || 1
1500
1558
 
1501
- _canvas.style.width = instance.W * _scale + 'px'
1502
- _canvas.style.height = instance.H * _scale + 'px'
1559
+ _canvas.style.width = width * _scale + 'px'
1560
+ _canvas.style.height = height * _scale + 'px'
1503
1561
  }
1504
1562
 
1505
1563
  // restore canvas image rendering properties
@@ -1527,8 +1585,8 @@ export default function litecanvas(settings = {}) {
1527
1585
  * @param {*} arg4
1528
1586
  */
1529
1587
  function triggerEvent(eventName, arg1, arg2, arg3, arg4) {
1530
- if (!_events[eventName]) return
1531
- for (const callback of _events[eventName]) {
1588
+ if (!_eventListeners[eventName]) return
1589
+ for (const callback of _eventListeners[eventName]) {
1532
1590
  callback(arg1, arg2, arg3, arg4)
1533
1591
  }
1534
1592
  }
package/types/index.d.ts CHANGED
@@ -283,6 +283,26 @@ declare global {
283
283
  * @param [color=0] the color index
284
284
  */
285
285
  function circfill(x: number, y: number, radius: number, color?: number): void
286
+ /**
287
+ * Draw a ellipse outline
288
+ *
289
+ * @param x
290
+ * @param y
291
+ * @param radiusX
292
+ * @param radiusY
293
+ * @param [color=0] the color index
294
+ */
295
+ function oval(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
296
+ /**
297
+ * Draw a color-filled ellipse
298
+ *
299
+ * @param x
300
+ * @param y
301
+ * @param radiusX
302
+ * @param radiusY
303
+ * @param [color=0] the color index
304
+ */
305
+ function ovalfill(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
286
306
  /**
287
307
  * Draw a line
288
308
  *
package/types/types.d.ts CHANGED
@@ -274,6 +274,26 @@ type LitecanvasInstance = {
274
274
  * @param [color=0] the color index
275
275
  */
276
276
  circfill(x: number, y: number, radius: number, color?: number): void
277
+ /**
278
+ * Draw a ellipse outline
279
+ *
280
+ * @param x
281
+ * @param y
282
+ * @param radiusX
283
+ * @param radiusY
284
+ * @param [color=0] the color index
285
+ */
286
+ oval(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
287
+ /**
288
+ * Draw a color-filled ellipse
289
+ *
290
+ * @param x
291
+ * @param y
292
+ * @param radiusX
293
+ * @param radiusY
294
+ * @param [color=0] the color index
295
+ */
296
+ ovalfill(x: number, y: number, radiusX: number, radiusY: number, color?: number): void
277
297
  /**
278
298
  * Draw a line
279
299
  *