litecanvas 0.86.0 → 0.88.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/src/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { setupZzFX } from './zzfx.js'
3
3
  import { defaultPalette } from './palette.js'
4
4
  import { assert } from './dev.js'
5
+ import { version } from '../version.js'
5
6
 
6
7
  /**
7
8
  * The litecanvas constructor
@@ -22,6 +23,8 @@ export default function litecanvas(settings = {}) {
22
23
  elem.addEventListener(evt, callback, false)
23
24
  _browserEventListeners.push(() => elem.removeEventListener(evt, callback, false))
24
25
  },
26
+ /** @type {(c: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D) => void} */
27
+ beginPath = (c) => c.beginPath(),
25
28
  isNumber = Number.isFinite,
26
29
  zzfx = setupZzFX(root),
27
30
  /** @type {LitecanvasOptions} */
@@ -62,7 +65,7 @@ export default function litecanvas(settings = {}) {
62
65
  _deltaTime = 1 / 60,
63
66
  /** @type {number} */
64
67
  _accumulated = 0,
65
- /** @type {number|null} */
68
+ /** @type {number?} */
66
69
  _rafid,
67
70
  /** @type {string} */
68
71
  _fontFamily = 'sans-serif',
@@ -131,9 +134,9 @@ export default function litecanvas(settings = {}) {
131
134
  * @tutorial https://gamedev.net/tutorials/programming/general-and-gameplay-programming/a-brief-introduction-to-lerp-r4954/
132
135
  */
133
136
  lerp: (start, end, t) => {
134
- DEV: assert(isNumber(start), 'lerp: 1st param must be a number')
135
- DEV: assert(isNumber(end), 'lerp: 2nd param must be a number')
136
- DEV: assert(isNumber(t), 'lerp: 3rd param must be a number')
137
+ DEV: assert(isNumber(start), '[litecanvas] lerp() 1st param must be a number')
138
+ DEV: assert(isNumber(end), '[litecanvas] lerp() 2nd param must be a number')
139
+ DEV: assert(isNumber(t), '[litecanvas] lerp() 3rd param must be a number')
137
140
 
138
141
  return t * (end - start) + start
139
142
  },
@@ -172,10 +175,10 @@ export default function litecanvas(settings = {}) {
172
175
  * @returns {number} rounded number.
173
176
  */
174
177
  round: (n, precision = 0) => {
175
- DEV: assert(isNumber(n), 'round: 1st param must be a number')
178
+ DEV: assert(isNumber(n), '[litecanvas] round() 1st param must be a number')
176
179
  DEV: assert(
177
180
  null == precision || (isNumber(precision) && precision >= 0),
178
- 'round: 2nd param must be a positive number or zero'
181
+ '[litecanvas] round() 2nd param must be a positive number or zero'
179
182
  )
180
183
  if (!precision) {
181
184
  return math.round(n)
@@ -193,10 +196,13 @@ export default function litecanvas(settings = {}) {
193
196
  * @returns {number}
194
197
  */
195
198
  clamp: (value, min, max) => {
196
- DEV: assert(isNumber(value), 'clamp: 1st param must be a number')
197
- DEV: assert(isNumber(min), 'clamp: 2nd param must be a number')
198
- DEV: assert(isNumber(max), 'clamp: 3rd param must be a number')
199
- DEV: assert(max > min, 'clamp: the 2nd param must be less than the 3rd param')
199
+ DEV: assert(isNumber(value), '[litecanvas] clamp() 1st param must be a number')
200
+ DEV: assert(isNumber(min), '[litecanvas] clamp() 2nd param must be a number')
201
+ DEV: assert(isNumber(max), '[litecanvas] clamp() 3rd param must be a number')
202
+ DEV: assert(
203
+ max > min,
204
+ '[litecanvas] clamp() the 2nd param must be less than the 3rd param'
205
+ )
200
206
 
201
207
  if (value < min) return min
202
208
  if (value > max) return max
@@ -212,10 +218,13 @@ export default function litecanvas(settings = {}) {
212
218
  * @returns {number}
213
219
  */
214
220
  wrap: (value, min, max) => {
215
- DEV: assert(isNumber(value), 'wrap: 1st param must be a number')
216
- DEV: assert(isNumber(min), 'wrap: 2nd param must be a number')
217
- DEV: assert(isNumber(max), 'wrap: 3rd param must be a number')
218
- DEV: assert(max > min, 'wrap: the 2nd param must be less than the 3rd param')
221
+ DEV: assert(isNumber(value), '[litecanvas] wrap() 1st param must be a number')
222
+ DEV: assert(isNumber(min), '[litecanvas] wrap() 2nd param must be a number')
223
+ DEV: assert(isNumber(max), '[litecanvas] wrap() 3rd param must be a number')
224
+ DEV: assert(
225
+ max > min,
226
+ '[litecanvas] wrap() the 2nd param must be less than the 3rd param'
227
+ )
219
228
 
220
229
  return value - (max - min) * math.floor((value - min) / (max - min))
221
230
  },
@@ -232,12 +241,15 @@ export default function litecanvas(settings = {}) {
232
241
  * @returns {number} the remapped number
233
242
  */
234
243
  map(value, start1, stop1, start2, stop2, withinBounds) {
235
- DEV: assert(isNumber(value), 'map: 1st param must be a number')
236
- DEV: assert(isNumber(start1), 'map: 2nd param must be a number')
237
- DEV: assert(isNumber(stop1), 'map: 3rd param must be a number')
238
- DEV: assert(isNumber(start2), 'map: 4th param must be a number')
239
- DEV: assert(isNumber(stop2), 'map: 5th param must be a number')
240
- DEV: assert(stop1 !== start1, 'map: the 2nd param must be different than the 3rd param')
244
+ DEV: assert(isNumber(value), '[litecanvas] map() 1st param must be a number')
245
+ DEV: assert(isNumber(start1), '[litecanvas] map() 2nd param must be a number')
246
+ DEV: assert(isNumber(stop1), '[litecanvas] map() 3rd param must be a number')
247
+ DEV: assert(isNumber(start2), '[litecanvas] map() 4th param must be a number')
248
+ DEV: assert(isNumber(stop2), '[litecanvas] map() 5th param must be a number')
249
+ DEV: assert(
250
+ stop1 !== start1,
251
+ '[litecanvas] map() the 2nd param must be different than the 3rd param'
252
+ )
241
253
 
242
254
  // prettier-ignore
243
255
  const result = ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2
@@ -255,10 +267,13 @@ export default function litecanvas(settings = {}) {
255
267
  * @returns {number} the normalized number.
256
268
  */
257
269
  norm: (value, start, stop) => {
258
- DEV: assert(isNumber(value), 'norm: 1st param must be a number')
259
- DEV: assert(isNumber(start), 'norm: 2nd param must be a number')
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')
270
+ DEV: assert(isNumber(value), '[litecanvas] norm() 1st param must be a number')
271
+ DEV: assert(isNumber(start), '[litecanvas] norm() 2nd param must be a number')
272
+ DEV: assert(isNumber(stop), '[litecanvas] norm() 3rd param must be a number')
273
+ DEV: assert(
274
+ start !== stop,
275
+ '[litecanvas] norm() the 2nd param must be different than the 3rd param'
276
+ )
262
277
 
263
278
  return instance.map(value, start, stop, 0, 1)
264
279
  },
@@ -272,12 +287,12 @@ export default function litecanvas(settings = {}) {
272
287
  * @param {(n: number) => number} [fn] - the periodic function (which default to `Math.sin`)
273
288
  */
274
289
  wave: (from, to, t, fn = Math.sin) => {
275
- DEV: assert(isNumber(from), 'wave: 1st param must be a number')
276
- DEV: assert(isNumber(to), 'wave: 2nd param must be a number')
277
- DEV: assert(isNumber(t), 'wave: 3rd param must be a number')
290
+ DEV: assert(isNumber(from), '[litecanvas] wave() 1st param must be a number')
291
+ DEV: assert(isNumber(to), '[litecanvas] wave() 2nd param must be a number')
292
+ DEV: assert(isNumber(t), '[litecanvas] wave() 3rd param must be a number')
278
293
  DEV: assert(
279
294
  'function' === typeof fn,
280
- 'wave: 4rd param must be a function (n: number) => number'
295
+ '[litecanvas] wave() 4rd param must be a function (n: number) => number'
281
296
  )
282
297
  return from + ((fn(t) + 1) / 2) * (to - from)
283
298
  },
@@ -292,9 +307,12 @@ export default function litecanvas(settings = {}) {
292
307
  * @returns {number} the random number
293
308
  */
294
309
  rand: (min = 0.0, max = 1.0) => {
295
- DEV: assert(isNumber(min), 'rand: 1st param must be a number')
296
- DEV: assert(isNumber(max), 'rand: 2nd param must be a number')
297
- DEV: assert(max > min, 'rand: the 1st param must be less than the 2nd param')
310
+ DEV: assert(isNumber(min), '[litecanvas] rand() 1st param must be a number')
311
+ DEV: assert(isNumber(max), '[litecanvas] rand() 2nd param must be a number')
312
+ DEV: assert(
313
+ max > min,
314
+ '[litecanvas] rand() the 1st param must be less than the 2nd param'
315
+ )
298
316
 
299
317
  const a = 1664525
300
318
  const c = 1013904223
@@ -313,9 +331,12 @@ export default function litecanvas(settings = {}) {
313
331
  * @returns {number} the random number
314
332
  */
315
333
  randi: (min = 0, max = 1) => {
316
- DEV: assert(isNumber(min), 'randi: 1st param must be a number')
317
- DEV: assert(isNumber(max), 'randi: 2nd param must be a number')
318
- DEV: assert(max > min, 'randi: the 1st param must be less than the 2nd param')
334
+ DEV: assert(isNumber(min), '[litecanvas] randi() 1st param must be a number')
335
+ DEV: assert(isNumber(max), '[litecanvas] randi() 2nd param must be a number')
336
+ DEV: assert(
337
+ max > min,
338
+ '[litecanvas] randi() the 1st param must be less than the 2nd param'
339
+ )
319
340
 
320
341
  return math.floor(instance.rand(min, max + 1))
321
342
  },
@@ -330,7 +351,7 @@ export default function litecanvas(settings = {}) {
330
351
  rseed(value) {
331
352
  DEV: assert(
332
353
  null == value || (isNumber(value) && value >= 0),
333
- 'rseed: 1st param must be a positive number or zero'
354
+ '[litecanvas] rseed() 1st param must be a positive number or zero'
334
355
  )
335
356
 
336
357
  _rngSeed = ~~value
@@ -345,7 +366,7 @@ export default function litecanvas(settings = {}) {
345
366
  cls(color) {
346
367
  DEV: assert(
347
368
  null == color || (isNumber(color) && color >= 0),
348
- 'cls: 1st param must be a positive number or zero or undefined'
369
+ '[litecanvas] cls() 1st param must be a positive number or zero or undefined'
349
370
  )
350
371
 
351
372
  if (null == color) {
@@ -368,23 +389,26 @@ export default function litecanvas(settings = {}) {
368
389
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect
369
390
  */
370
391
  rect(x, y, width, height, color, radii) {
371
- DEV: assert(isNumber(x), 'rect: 1st param must be a number')
372
- DEV: assert(isNumber(y), 'rect: 2nd param must be a number')
373
- DEV: assert(isNumber(width) && width > 0, 'rect: 3rd param must be a positive number')
392
+ DEV: assert(isNumber(x), '[litecanvas] rect() 1st param must be a number')
393
+ DEV: assert(isNumber(y), '[litecanvas] rect() 2nd param must be a number')
394
+ DEV: assert(
395
+ isNumber(width) && width > 0,
396
+ '[litecanvas] rect() 3rd param must be a positive number'
397
+ )
374
398
  DEV: assert(
375
399
  isNumber(height) && height >= 0,
376
- 'rect: 4th param must be a positive number or zero'
400
+ '[litecanvas] rect() 4th param must be a positive number or zero'
377
401
  )
378
402
  DEV: assert(
379
403
  null == color || (isNumber(color) && color >= 0),
380
- 'rect: 5th param must be a positive number or zero'
404
+ '[litecanvas] rect() 5th param must be a positive number or zero'
381
405
  )
382
406
  DEV: assert(
383
407
  null == radii || isNumber(radii) || (Array.isArray(radii) && radii.length >= 1),
384
- 'rect: 6th param must be a number or array of numbers'
408
+ '[litecanvas] rect() 6th param must be a number or array of numbers'
385
409
  )
386
410
 
387
- _ctx.beginPath()
411
+ beginPath(_ctx)
388
412
  _ctx[radii ? 'roundRect' : 'rect'](
389
413
  ~~x - _outline_fix,
390
414
  ~~y - _outline_fix,
@@ -406,26 +430,26 @@ export default function litecanvas(settings = {}) {
406
430
  * @param {number|number[]} [radii] A number or list specifying the radii used to draw a rounded-borders rectangle
407
431
  */
408
432
  rectfill(x, y, width, height, color, radii) {
409
- DEV: assert(isNumber(x), 'rectfill: 1st param must be a number')
410
- DEV: assert(isNumber(y), 'rectfill: 2nd param must be a number')
433
+ DEV: assert(isNumber(x), '[litecanvas] rectfill() 1st param must be a number')
434
+ DEV: assert(isNumber(y), '[litecanvas] rectfill() 2nd param must be a number')
411
435
  DEV: assert(
412
436
  isNumber(width) && width >= 0,
413
- 'rectfill: 3rd param must be a positive number or zero'
437
+ '[litecanvas] rectfill() 3rd param must be a positive number or zero'
414
438
  )
415
439
  DEV: assert(
416
440
  isNumber(height) && height >= 0,
417
- 'rectfill: 4th param must be a positive number or zero'
441
+ '[litecanvas] rectfill() 4th param must be a positive number or zero'
418
442
  )
419
443
  DEV: assert(
420
444
  null == color || (isNumber(color) && color >= 0),
421
- 'rectfill: 5th param must be a positive number or zero'
445
+ '[litecanvas] rectfill() 5th param must be a positive number or zero'
422
446
  )
423
447
  DEV: assert(
424
448
  null == radii || isNumber(radii) || (Array.isArray(radii) && radii.length >= 1),
425
- 'rectfill: 6th param must be a number or array of at least 2 numbers'
449
+ '[litecanvas] rectfill() 6th param must be a number or array of at least 2 numbers'
426
450
  )
427
451
 
428
- _ctx.beginPath()
452
+ beginPath(_ctx)
429
453
  _ctx[radii ? 'roundRect' : 'rect'](~~x, ~~y, ~~width, ~~height, radii)
430
454
  instance.fill(color)
431
455
  },
@@ -439,18 +463,18 @@ export default function litecanvas(settings = {}) {
439
463
  * @param {number} [color=0] the color index
440
464
  */
441
465
  circ(x, y, radius, color) {
442
- DEV: assert(isNumber(x), 'circ: 1st param must be a number')
443
- DEV: assert(isNumber(y), 'circ: 2nd param must be a number')
466
+ DEV: assert(isNumber(x), '[litecanvas] circ() 1st param must be a number')
467
+ DEV: assert(isNumber(y), '[litecanvas] circ() 2nd param must be a number')
444
468
  DEV: assert(
445
469
  isNumber(radius) && radius >= 0,
446
- 'circ: 3rd param must be a positive number or zero'
470
+ '[litecanvas] circ() 3rd param must be a positive number or zero'
447
471
  )
448
472
  DEV: assert(
449
473
  null == color || (isNumber(color) && color >= 0),
450
- 'circ: 4th param must be a positive number or zero'
474
+ '[litecanvas] circ() 4th param must be a positive number or zero'
451
475
  )
452
476
 
453
- _ctx.beginPath()
477
+ beginPath(_ctx)
454
478
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI)
455
479
  instance.stroke(color)
456
480
  },
@@ -464,18 +488,18 @@ export default function litecanvas(settings = {}) {
464
488
  * @param {number} [color=0] the color index
465
489
  */
466
490
  circfill(x, y, radius, color) {
467
- DEV: assert(isNumber(x), 'circfill: 1st param must be a number')
468
- DEV: assert(isNumber(y), 'circfill: 2nd param must be a number')
491
+ DEV: assert(isNumber(x), '[litecanvas] circfill() 1st param must be a number')
492
+ DEV: assert(isNumber(y), '[litecanvas] circfill() 2nd param must be a number')
469
493
  DEV: assert(
470
494
  isNumber(radius) && radius >= 0,
471
- 'circfill: 3rd param must be a positive number or zero'
495
+ '[litecanvas] circfill() 3rd param must be a positive number or zero'
472
496
  )
473
497
  DEV: assert(
474
498
  null == color || (isNumber(color) && color >= 0),
475
- 'circfill: 4th param must be a positive number or zero'
499
+ '[litecanvas] circfill() 4th param must be a positive number or zero'
476
500
  )
477
501
 
478
- _ctx.beginPath()
502
+ beginPath(_ctx)
479
503
  _ctx.arc(~~x, ~~y, ~~radius, 0, TWO_PI)
480
504
  instance.fill(color)
481
505
  },
@@ -490,22 +514,22 @@ export default function litecanvas(settings = {}) {
490
514
  * @param {number} [color=0] the color index
491
515
  */
492
516
  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')
517
+ DEV: assert(isNumber(x), '[litecanvas] oval() 1st param must be a number')
518
+ DEV: assert(isNumber(y), '[litecanvas] oval() 2nd param must be a number')
495
519
  DEV: assert(
496
520
  isNumber(radiusX) && radiusX >= 0,
497
- 'oval: 3rd param must be a positive number or zero'
521
+ '[litecanvas] oval() 3rd param must be a positive number or zero'
498
522
  )
499
523
  DEV: assert(
500
524
  isNumber(radiusY) && radiusY >= 0,
501
- 'oval: 4th param must be a positive number or zero'
525
+ '[litecanvas] oval() 4th param must be a positive number or zero'
502
526
  )
503
527
  DEV: assert(
504
528
  null == color || (isNumber(color) && color >= 0),
505
- 'oval: 5th param must be a positive number or zero'
529
+ '[litecanvas] oval() 5th param must be a positive number or zero'
506
530
  )
507
531
 
508
- _ctx.beginPath()
532
+ beginPath(_ctx)
509
533
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
510
534
  instance.stroke(color)
511
535
  },
@@ -520,22 +544,22 @@ export default function litecanvas(settings = {}) {
520
544
  * @param {number} [color=0] the color index
521
545
  */
522
546
  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')
547
+ DEV: assert(isNumber(x), '[litecanvas] ovalfill() 1st param must be a number')
548
+ DEV: assert(isNumber(y), '[litecanvas] ovalfill() 2nd param must be a number')
525
549
  DEV: assert(
526
550
  isNumber(radiusX) && radiusX >= 0,
527
- 'ovalfill: 3rd param must be a positive number or zero'
551
+ '[litecanvas] ovalfill() 3rd param must be a positive number or zero'
528
552
  )
529
553
  DEV: assert(
530
554
  isNumber(radiusY) && radiusY >= 0,
531
- 'ovalfill: 4th param must be a positive number or zero'
555
+ '[litecanvas] ovalfill() 4th param must be a positive number or zero'
532
556
  )
533
557
  DEV: assert(
534
558
  null == color || (isNumber(color) && color >= 0),
535
- 'ovalfill: 5th param must be a positive number or zero'
559
+ '[litecanvas] ovalfill() 5th param must be a positive number or zero'
536
560
  )
537
561
 
538
- _ctx.beginPath()
562
+ beginPath(_ctx)
539
563
  _ctx.ellipse(~~x, ~~y, ~~radiusX, ~~radiusY, 0, 0, TWO_PI)
540
564
  instance.fill(color)
541
565
  },
@@ -550,16 +574,22 @@ export default function litecanvas(settings = {}) {
550
574
  * @param {number} [color=0] the color index
551
575
  */
552
576
  line(x1, y1, x2, y2, color) {
553
- DEV: assert(isNumber(x1), 'line: 1st param must be a number')
554
- DEV: assert(isNumber(y1), 'line: 2nd param must be a number')
555
- DEV: assert(isNumber(x2), 'line: 3rd param must be a positive number or zero')
556
- DEV: assert(isNumber(y2), 'line: 4th param must be a positive number or zero')
577
+ DEV: assert(isNumber(x1), '[litecanvas] line() 1st param must be a number')
578
+ DEV: assert(isNumber(y1), '[litecanvas] line() 2nd param must be a number')
579
+ DEV: assert(
580
+ isNumber(x2),
581
+ '[litecanvas] line() 3rd param must be a positive number or zero'
582
+ )
583
+ DEV: assert(
584
+ isNumber(y2),
585
+ '[litecanvas] line() 4th param must be a positive number or zero'
586
+ )
557
587
  DEV: assert(
558
588
  null == color || (isNumber(color) && color >= 0),
559
- 'line: 5th param must be a positive number or zero'
589
+ '[litecanvas] line() 5th param must be a positive number or zero'
560
590
  )
561
591
 
562
- _ctx.beginPath()
592
+ beginPath(_ctx)
563
593
 
564
594
  let xfix = _outline_fix !== 0 && ~~x1 === ~~x2 ? 0.5 : 0
565
595
  let yfix = _outline_fix !== 0 && ~~y1 === ~~y2 ? 0.5 : 0
@@ -579,7 +609,7 @@ export default function litecanvas(settings = {}) {
579
609
  linewidth(value) {
580
610
  DEV: assert(
581
611
  isNumber(value) && ~~value > 0,
582
- 'linewidth: 1st param must be a positive number'
612
+ '[litecanvas] linewidth() 1st param must be a positive number'
583
613
  )
584
614
 
585
615
  _ctx.lineWidth = ~~value
@@ -597,9 +627,9 @@ export default function litecanvas(settings = {}) {
597
627
  linedash(segments, offset = 0) {
598
628
  DEV: assert(
599
629
  Array.isArray(segments) && segments.length > 0,
600
- 'linedash: 1st param must be an array of numbers'
630
+ '[litecanvas] linedash() 1st param must be an array of numbers'
601
631
  )
602
- DEV: assert(isNumber(offset), 'linedash: 2nd param must be a number')
632
+ DEV: assert(isNumber(offset), '[litecanvas] linedash() 2nd param must be a number')
603
633
 
604
634
  _ctx.setLineDash(segments)
605
635
  _ctx.lineDashOffset = offset
@@ -616,13 +646,16 @@ export default function litecanvas(settings = {}) {
616
646
  * @param {string} [fontStyle] can be "normal" (default), "italic" and/or "bold".
617
647
  */
618
648
  text(x, y, message, color = 3, fontStyle = 'normal') {
619
- DEV: assert(isNumber(x), 'text: 1st param must be a number')
620
- DEV: assert(isNumber(y), 'text: 2nd param must be a number')
649
+ DEV: assert(isNumber(x), '[litecanvas] text() 1st param must be a number')
650
+ DEV: assert(isNumber(y), '[litecanvas] text() 2nd param must be a number')
621
651
  DEV: assert(
622
652
  null == color || (isNumber(color) && color >= 0),
623
- 'text: 4th param must be a positive number or zero'
653
+ '[litecanvas] text() 4th param must be a positive number or zero'
654
+ )
655
+ DEV: assert(
656
+ 'string' === typeof fontStyle,
657
+ '[litecanvas] text() 5th param must be a string'
624
658
  )
625
- DEV: assert('string' === typeof fontStyle, 'text: 5th param must be a string')
626
659
 
627
660
  _ctx.font = `${fontStyle} ${_fontSize}px ${_fontFamily}`
628
661
  _ctx.fillStyle = _colors[~~color % _colors.length]
@@ -635,7 +668,10 @@ export default function litecanvas(settings = {}) {
635
668
  * @param {string} family
636
669
  */
637
670
  textfont(family) {
638
- DEV: assert('string' === typeof family, 'textfont: 1st param must be a string')
671
+ DEV: assert(
672
+ 'string' === typeof family,
673
+ '[litecanvas] textfont() 1st param must be a string'
674
+ )
639
675
 
640
676
  _fontFamily = family
641
677
  },
@@ -646,7 +682,7 @@ export default function litecanvas(settings = {}) {
646
682
  * @param {number} size
647
683
  */
648
684
  textsize(size) {
649
- DEV: assert(isNumber(size), 'textsize: 1st param must be a number')
685
+ DEV: assert(isNumber(size), '[litecanvas] textsize() 1st param must be a number')
650
686
 
651
687
  _fontSize = size
652
688
  },
@@ -662,14 +698,14 @@ export default function litecanvas(settings = {}) {
662
698
  textalign(align, baseline) {
663
699
  DEV: assert(
664
700
  null == align || ['left', 'right', 'center', 'start', 'end'].includes(align),
665
- 'textalign: 1st param must be null or one of the following strings: center, left, right, start or end.'
701
+ '[litecanvas] textalign() 1st param must be null or one of the following strings: center, left, right, start or end.'
666
702
  )
667
703
  DEV: assert(
668
704
  null == baseline ||
669
705
  ['top', 'bottom', 'middle', 'hanging', 'alphabetic', 'ideographic'].includes(
670
706
  baseline
671
707
  ),
672
- 'textalign: 2nd param must be null or one of the following strings: middle, top, bottom, hanging, alphabetic or ideographic.'
708
+ '[litecanvas] textalign() 2nd param must be null or one of the following strings: middle, top, bottom, hanging, alphabetic or ideographic.'
673
709
  )
674
710
 
675
711
  if (align) _ctx.textAlign = align
@@ -685,8 +721,8 @@ export default function litecanvas(settings = {}) {
685
721
  * @param {OffscreenCanvas|HTMLImageElement|HTMLCanvasElement} source
686
722
  */
687
723
  image(x, y, source) {
688
- DEV: assert(isNumber(x), 'image: 1st param must be a number')
689
- DEV: assert(isNumber(y), 'image: 2nd param must be a number')
724
+ DEV: assert(isNumber(x), '[litecanvas] image() 1st param must be a number')
725
+ DEV: assert(isNumber(y), '[litecanvas] image() 2nd param must be a number')
690
726
 
691
727
  _ctx.drawImage(source, ~~x, ~~y)
692
728
  },
@@ -704,22 +740,25 @@ export default function litecanvas(settings = {}) {
704
740
  * @see https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
705
741
  */
706
742
  paint(width, height, drawing, options = {}) {
707
- DEV: assert(isNumber(width) && width >= 1, 'paint: 1st param must be a positive number')
743
+ DEV: assert(
744
+ isNumber(width) && width >= 1,
745
+ '[litecanvas] paint() 1st param must be a positive number'
746
+ )
708
747
  DEV: assert(
709
748
  isNumber(height) && height >= 1,
710
- 'paint: 2nd param must be a positive number'
749
+ '[litecanvas] paint() 2nd param must be a positive number'
711
750
  )
712
751
  DEV: assert(
713
752
  'function' === typeof drawing || Array.isArray(drawing),
714
- 'paint: 3rd param must be a function or array'
753
+ '[litecanvas] paint() 3rd param must be a function or array'
715
754
  )
716
755
  DEV: assert(
717
756
  (options && null == options.scale) || isNumber(options.scale),
718
- 'paint: 4th param (options.scale) must be a number'
757
+ '[litecanvas] paint() 4th param (options.scale) must be a number'
719
758
  )
720
759
  DEV: assert(
721
760
  (options && null == options.canvas) || options.canvas instanceof OffscreenCanvas,
722
- 'paint: 4th param (options.canvas) must be an OffscreenCanvas'
761
+ '[litecanvas] paint() 4th param (options.canvas) must be an OffscreenCanvas'
723
762
  )
724
763
 
725
764
  const /** @type {OffscreenCanvas} */
@@ -796,8 +835,8 @@ export default function litecanvas(settings = {}) {
796
835
  * @param {number} y
797
836
  */
798
837
  translate: (x, y) => {
799
- DEV: assert(isNumber(x), 'translate: 1st param must be a number')
800
- DEV: assert(isNumber(y), 'translate: 2nd param must be a number')
838
+ DEV: assert(isNumber(x), '[litecanvas] translate() 1st param must be a number')
839
+ DEV: assert(isNumber(y), '[litecanvas] translate() 2nd param must be a number')
801
840
 
802
841
  return _ctx.translate(~~x, ~~y)
803
842
  },
@@ -809,8 +848,8 @@ export default function litecanvas(settings = {}) {
809
848
  * @param {number} [y]
810
849
  */
811
850
  scale: (x, y) => {
812
- DEV: assert(isNumber(x), 'scale: 1st param must be a number')
813
- DEV: assert(null == y || isNumber(y), 'scale: 2nd param must be a number')
851
+ DEV: assert(isNumber(x), '[litecanvas] scale() 1st param must be a number')
852
+ DEV: assert(null == y || isNumber(y), '[litecanvas] scale() 2nd param must be a number')
814
853
 
815
854
  return _ctx.scale(x, y || x)
816
855
  },
@@ -821,7 +860,7 @@ export default function litecanvas(settings = {}) {
821
860
  * @param {number} radians
822
861
  */
823
862
  rotate: (radians) => {
824
- DEV: assert(isNumber(radians), 'rotate: 1st param must be a number')
863
+ DEV: assert(isNumber(radians), '[litecanvas] rotate() 1st param must be a number')
825
864
 
826
865
  return _ctx.rotate(radians)
827
866
  },
@@ -833,89 +872,56 @@ export default function litecanvas(settings = {}) {
833
872
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalAlpha
834
873
  */
835
874
  alpha(value) {
836
- DEV: assert(isNumber(value), 'alpha: 1st param must be a number')
875
+ DEV: assert(isNumber(value), '[litecanvas] alpha() 1st param must be a number')
837
876
 
838
877
  _ctx.globalAlpha = instance.clamp(value, 0, 1)
839
878
  },
840
879
 
841
880
  /**
842
- * Returns a newly instantiated Path2D object, optionally with another
843
- * path as an argument (creates a copy), or optionally with a string
844
- * consisting of SVG path data.
845
- *
846
- * @param {Path2D|string} [arg]
847
- * @returns Path2D
848
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
849
- */
850
- path: (arg) => {
851
- DEV: assert(
852
- null == arg || 'string' === typeof arg || arg instanceof Path2D,
853
- 'path: 1st param must be a string or a Path2D instance'
854
- )
855
-
856
- return new Path2D(arg)
857
- },
858
-
859
- /**
860
- * Fills the current or given path with a given color.
881
+ * Fills the current path with a given color.
861
882
  *
862
883
  * @param {number} [color=0]
863
- * @param {Path2D} [path]
864
884
  */
865
- fill(color, path) {
885
+ fill(color) {
866
886
  DEV: assert(
867
887
  null == color || (isNumber(color) && color >= 0),
868
- 'fill: 1st param must be a positive number or zero'
869
- )
870
- DEV: assert(
871
- null == path || path instanceof Path2D,
872
- 'fill: 2nd param must be a Path2D instance'
888
+ '[litecanvas] fill() 1st param must be a positive number or zero'
873
889
  )
874
890
 
875
891
  _ctx.fillStyle = _colors[~~color % _colors.length]
876
- if (path) {
877
- _ctx.fill(path)
878
- } else {
879
- _ctx.fill()
880
- }
892
+ _ctx.fill()
881
893
  },
882
894
 
883
895
  /**
884
- * Outlines the current or given path with a given color.
896
+ * Outlines the current path with a given color.
885
897
  *
886
898
  * @param {number} [color=0]
887
- * @param {Path2D} [path]
888
899
  */
889
- stroke(color, path) {
900
+ stroke(color) {
890
901
  DEV: assert(
891
902
  null == color || (isNumber(color) && color >= 0),
892
- 'stroke: 1st param must be a positive number or zero'
893
- )
894
- DEV: assert(
895
- null == path || path instanceof Path2D,
896
- 'stroke: 2nd param must be a Path2D instance'
903
+ '[litecanvas] stroke() 1st param must be a positive number or zero'
897
904
  )
898
905
 
899
906
  _ctx.strokeStyle = _colors[~~color % _colors.length]
900
- if (path) {
901
- _ctx.stroke(path)
902
- } else {
903
- _ctx.stroke()
904
- }
907
+ _ctx.stroke()
905
908
  },
906
909
 
907
910
  /**
908
- * Turn given path into a clipping region.
911
+ * Turns a path (in the callback) into the current clipping region.
909
912
  *
910
- * Note: always call `push()` before and `pop()` after.
911
- *
912
- * @param {Path2D} path
913
+ * @param {clipCallback} callback
913
914
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
914
915
  */
915
- clip(path) {
916
- DEV: assert(path instanceof Path2D, 'clip: 1st param must be a Path2D instance')
916
+ clip(callback) {
917
+ DEV: assert(
918
+ 'function' === typeof callback,
919
+ '[litecanvas] clip() 1st param must be a function'
920
+ )
917
921
 
918
- _ctx.clip(path)
922
+ beginPath(_ctx)
923
+ callback(_ctx)
924
+ _ctx.clip()
919
925
  },
920
926
 
921
927
  /** SOUND API */
@@ -933,10 +939,10 @@ export default function litecanvas(settings = {}) {
933
939
  sfx(zzfxParams, pitchSlide = 0, volumeFactor = 1) {
934
940
  DEV: assert(
935
941
  null == zzfxParams || Array.isArray(zzfxParams),
936
- 'sfx: 1st param must be an array'
942
+ '[litecanvas] sfx() 1st param must be an array'
937
943
  )
938
- DEV: assert(isNumber(pitchSlide), 'sfx: 2nd param must be a number')
939
- DEV: assert(isNumber(volumeFactor), 'sfx: 3rd param must be a number')
944
+ DEV: assert(isNumber(pitchSlide), '[litecanvas] sfx() 2nd param must be a number')
945
+ DEV: assert(isNumber(volumeFactor), '[litecanvas] sfx() 3rd param must be a number')
940
946
 
941
947
  if (
942
948
  // @ts-ignore
@@ -967,7 +973,7 @@ export default function litecanvas(settings = {}) {
967
973
  * @param {number} value
968
974
  */
969
975
  volume(value) {
970
- DEV: assert(isNumber(value), 'volume: 1st param must be a number')
976
+ DEV: assert(isNumber(value), '[litecanvas] volume() 1st param must be a number')
971
977
 
972
978
  // @ts-ignore
973
979
  root.zzfxV = value
@@ -980,14 +986,21 @@ export default function litecanvas(settings = {}) {
980
986
  * @returns {HTMLCanvasElement}
981
987
  */
982
988
  canvas: () => _canvas,
989
+
983
990
  /**
984
991
  * Prepares a plugin to be loaded
985
992
  *
986
993
  * @param {pluginCallback} callback
987
994
  */
988
995
  use(callback, config = {}) {
989
- DEV: assert('function' === typeof callback, 'use: 1st param must be a function')
990
- DEV: assert('object' === typeof config, 'use: 2nd param must be an object')
996
+ DEV: assert(
997
+ 'function' === typeof callback,
998
+ '[litecanvas] use() 1st param must be a function'
999
+ )
1000
+ DEV: assert(
1001
+ 'object' === typeof config,
1002
+ '[litecanvas] use() 2nd param must be an object'
1003
+ )
991
1004
 
992
1005
  if (_initialized) {
993
1006
  // load the plugin now
@@ -1006,8 +1019,14 @@ export default function litecanvas(settings = {}) {
1006
1019
  * @returns {Function} a function to remove the listener
1007
1020
  */
1008
1021
  listen(eventName, callback) {
1009
- DEV: assert('string' === typeof eventName, 'listen: 1st param must be a string')
1010
- DEV: assert('function' === typeof callback, 'listen: 2nd param must be a function')
1022
+ DEV: assert(
1023
+ 'string' === typeof eventName,
1024
+ '[litecanvas] listen() 1st param must be a string'
1025
+ )
1026
+ DEV: assert(
1027
+ 'function' === typeof callback,
1028
+ '[litecanvas] listen() 2nd param must be a function'
1029
+ )
1011
1030
 
1012
1031
  eventName = eventName.toLowerCase()
1013
1032
 
@@ -1028,7 +1047,10 @@ export default function litecanvas(settings = {}) {
1028
1047
  * @param {*} [arg4] any data to be passed over the listeners
1029
1048
  */
1030
1049
  emit(eventName, arg1, arg2, arg3, arg4) {
1031
- DEV: assert('string' === typeof eventName, 'emit: 1st param must be a string')
1050
+ DEV: assert(
1051
+ 'string' === typeof eventName,
1052
+ '[litecanvas] emit() 1st param must be a string'
1053
+ )
1032
1054
  if (_initialized) {
1033
1055
  eventName = eventName.toLowerCase()
1034
1056
 
@@ -1046,7 +1068,7 @@ export default function litecanvas(settings = {}) {
1046
1068
  pal(colors = defaultPalette) {
1047
1069
  DEV: assert(
1048
1070
  Array.isArray(colors) && colors.length > 0,
1049
- 'pal: 1st param must be a array of strings'
1071
+ '[litecanvas] pal() 1st param must be a array of strings'
1050
1072
  )
1051
1073
  _colors = colors
1052
1074
  },
@@ -1058,7 +1080,7 @@ export default function litecanvas(settings = {}) {
1058
1080
  * @param {*} value
1059
1081
  */
1060
1082
  def(key, value) {
1061
- DEV: assert('string' === typeof key, 'def: 1st param must be a string')
1083
+ DEV: assert('string' === typeof key, '[litecanvas] def() 1st param must be a string')
1062
1084
  DEV: if (null == value) {
1063
1085
  console.warn(`def: key "${key}" was defined as ${value} but now is null`)
1064
1086
  }
@@ -1079,7 +1101,7 @@ export default function litecanvas(settings = {}) {
1079
1101
  timescale(value) {
1080
1102
  DEV: assert(
1081
1103
  isNumber(value) && value >= 0,
1082
- 'timescale: 1st param must be a positive number or zero'
1104
+ '[litecanvas] timescale() 1st param must be a positive number or zero'
1083
1105
  )
1084
1106
 
1085
1107
  _timeScale = value
@@ -1093,7 +1115,7 @@ export default function litecanvas(settings = {}) {
1093
1115
  framerate(value) {
1094
1116
  DEV: assert(
1095
1117
  isNumber(value) && value >= 1,
1096
- 'framerate: 1st param must be a positive number'
1118
+ '[litecanvas] framerate() 1st param must be a positive number'
1097
1119
  )
1098
1120
 
1099
1121
  _deltaTime = 1 / ~~value
@@ -1106,7 +1128,7 @@ export default function litecanvas(settings = {}) {
1106
1128
  * @returns {any}
1107
1129
  */
1108
1130
  stat(n) {
1109
- DEV: assert(isNumber(n) && n >= 0, 'stat: 1st param must be a number')
1131
+ DEV: assert(isNumber(n) && n >= 0, '[litecanvas] stat() 1st param must be a number')
1110
1132
 
1111
1133
  const list = [
1112
1134
  // 0
@@ -1114,7 +1136,7 @@ export default function litecanvas(settings = {}) {
1114
1136
  // 1
1115
1137
  _initialized,
1116
1138
  // 2
1117
- _rafid,
1139
+ _deltaTime,
1118
1140
  // 3
1119
1141
  _scale,
1120
1142
  // 4
@@ -1149,9 +1171,7 @@ export default function litecanvas(settings = {}) {
1149
1171
  */
1150
1172
  quit() {
1151
1173
  // stop the game loop (update & draw)
1152
- cancelAnimationFrame(_rafid)
1153
-
1154
- _rafid = 0
1174
+ instance.pause()
1155
1175
 
1156
1176
  // emit "quit" event to manual clean ups
1157
1177
  instance.emit('quit')
@@ -1176,6 +1196,32 @@ export default function litecanvas(settings = {}) {
1176
1196
  // unset that flag
1177
1197
  _initialized = false
1178
1198
  },
1199
+
1200
+ /**
1201
+ * Pauses the engine loop (update & draw).
1202
+ */
1203
+ pause() {
1204
+ cancelAnimationFrame(_rafid)
1205
+ _rafid = 0
1206
+ },
1207
+
1208
+ /**
1209
+ * Resumes (if paused) the engine loop.
1210
+ */
1211
+ resume() {
1212
+ if (!_rafid && _initialized) {
1213
+ _rafid = raf(drawFrame)
1214
+ }
1215
+ },
1216
+
1217
+ /**
1218
+ * Returns `true` if the engine loop is paused.
1219
+ *
1220
+ * @returns {boolean}
1221
+ */
1222
+ paused() {
1223
+ return !_rafid
1224
+ },
1179
1225
  }
1180
1226
 
1181
1227
  // prettier-ignore
@@ -1188,6 +1234,9 @@ export default function litecanvas(settings = {}) {
1188
1234
  // setup default event listeners
1189
1235
  const source = settings.loop ? settings.loop : root
1190
1236
  for (const event of _coreEvents.split(',')) {
1237
+ DEV: if (root === source && source[event]) {
1238
+ console.info(`[litecanvas] using window.${event}()`)
1239
+ }
1191
1240
  if (source[event]) instance.listen(event, source[event])
1192
1241
  }
1193
1242
 
@@ -1423,7 +1472,7 @@ export default function litecanvas(settings = {}) {
1423
1472
  (key) => {
1424
1473
  DEV: assert(
1425
1474
  null == key || 'string' === typeof key,
1426
- 'iskeydown: 1st param must be a string or undefined'
1475
+ '[litecanvas] iskeydown() 1st param must be a string or undefined'
1427
1476
  )
1428
1477
  return keyCheck(_keysDown, key)
1429
1478
  }
@@ -1441,64 +1490,65 @@ export default function litecanvas(settings = {}) {
1441
1490
  (key) => {
1442
1491
  DEV: assert(
1443
1492
  null == key || 'string' === typeof key,
1444
- 'iskeypressed: 1st param must be a string or undefined'
1493
+ '[litecanvas] iskeypressed() 1st param must be a string or undefined'
1445
1494
  )
1446
1495
  return keyCheck(_keysPress, key)
1447
1496
  }
1448
1497
  )
1449
1498
  }
1450
1499
 
1500
+ // start the engine
1451
1501
  _initialized = true
1452
-
1453
- // start the game loop
1454
1502
  instance.emit('init', instance)
1455
1503
 
1504
+ // set the default text align and baseline
1505
+ instance.textalign('start', 'top')
1506
+
1456
1507
  _lastFrameTime = performance.now()
1457
- _rafid = raf(drawFrame)
1508
+ instance.resume()
1458
1509
  }
1459
1510
 
1460
1511
  /**
1461
1512
  * @param {DOMHighResTimeStamp} now
1462
1513
  */
1463
1514
  function drawFrame(now) {
1515
+ if (!settings.animate) {
1516
+ return instance.emit('draw')
1517
+ }
1518
+
1464
1519
  let updated = 0
1520
+ let frameTime = (now - _lastFrameTime) / 1000
1465
1521
 
1466
- if (settings.animate) {
1467
- // prevents too long frames
1468
- _accumulated += math.min(0.1, (now - _lastFrameTime) / 1000)
1469
- _lastFrameTime = now
1522
+ _lastFrameTime = now
1470
1523
 
1524
+ if (frameTime < 0.1) {
1525
+ _accumulated += frameTime
1471
1526
  while (_accumulated >= _deltaTime) {
1472
1527
  updated++
1473
1528
  instance.emit('update', _deltaTime * _timeScale, updated)
1474
1529
  instance.def('T', instance.T + _deltaTime * _timeScale)
1475
1530
  _accumulated -= _deltaTime
1476
1531
  }
1477
-
1478
- // request the next frame
1479
- // check if the last ID exists, because
1480
- // quit() delete it (sets to zero)
1481
- if (_rafid) {
1482
- _rafid = raf(drawFrame)
1483
- }
1484
- } else {
1485
- // when the canvas is not animated
1486
- // we force one frame when redraws are triggered
1487
- updated = 1
1488
1532
  }
1489
1533
 
1490
1534
  if (updated) {
1491
- // always set default values for
1492
- // _ctx.textAlign and _ctx.textBaseline before draw
1493
- instance.textalign('start', 'top')
1494
1535
  instance.emit('draw')
1495
1536
  }
1537
+
1538
+ // request the next frame
1539
+ // only when the engine loop are not paused (_rafid >= 1)
1540
+ if (_rafid) {
1541
+ _rafid = raf(drawFrame)
1542
+ }
1496
1543
  }
1497
1544
 
1498
1545
  function setupCanvas() {
1499
1546
  if ('string' === typeof settings.canvas) {
1500
1547
  _canvas = document.querySelector(settings.canvas)
1501
- DEV: assert(null != _canvas, 'Litecanvas\' option "canvas" is an invalid CSS selector')
1548
+ DEV: assert(
1549
+ null != _canvas,
1550
+ '[litecanvas] litecanvas() option "canvas" is an invalid CSS selector'
1551
+ )
1502
1552
  } else {
1503
1553
  _canvas = settings.canvas
1504
1554
  }
@@ -1507,7 +1557,7 @@ export default function litecanvas(settings = {}) {
1507
1557
 
1508
1558
  DEV: assert(
1509
1559
  'CANVAS' === _canvas.tagName,
1510
- 'Litecanvas\' option "canvas" should be a canvas element or string (CSS selector)'
1560
+ '[litecanvas] litecanvas() option "canvas" should be a canvas element or string (CSS selector)'
1511
1561
  )
1512
1562
 
1513
1563
  _ctx = _canvas.getContext('2d')
@@ -1527,15 +1577,15 @@ export default function litecanvas(settings = {}) {
1527
1577
  function resizeCanvas() {
1528
1578
  DEV: assert(
1529
1579
  null == settings.width || (isNumber(settings.width) && settings.width > 0),
1530
- 'Litecanvas\' option "width" should be a positive number when defined'
1580
+ '[litecanvas] litecanvas() option "width" should be a positive number when defined'
1531
1581
  )
1532
1582
  DEV: assert(
1533
1583
  null == settings.height || (isNumber(settings.height) && settings.height > 0),
1534
- 'Litecanvas\' option "height" should be a positive number when defined'
1584
+ '[litecanvas] litecanvas() option "height" should be a positive number when defined'
1535
1585
  )
1536
1586
  DEV: assert(
1537
1587
  null == settings.height || (settings.width > 0 && settings.height > 0),
1538
- 'Litecanvas\' option "width" is required when the option "height" is defined'
1588
+ '[litecanvas] litecanvas() option "width" is required when the option "height" is defined'
1539
1589
  )
1540
1590
 
1541
1591
  const width = settings.width || root.innerWidth,
@@ -1601,7 +1651,7 @@ export default function litecanvas(settings = {}) {
1601
1651
 
1602
1652
  DEV: assert(
1603
1653
  null == pluginData || 'object' === typeof pluginData,
1604
- 'Litecanvas plugins should return an object or nothing'
1654
+ '[litecanvas] litecanvas() plugins should return an object or nothing'
1605
1655
  )
1606
1656
 
1607
1657
  for (const key in pluginData) {
@@ -1612,13 +1662,16 @@ export default function litecanvas(settings = {}) {
1612
1662
  if (settings.global) {
1613
1663
  // @ts-ignore
1614
1664
  if (root.ENGINE) {
1615
- throw new Error('two global litecanvas detected')
1665
+ throw new Error('only one global litecanvas is allowed')
1616
1666
  }
1617
1667
  Object.assign(root, instance)
1618
1668
  // @ts-ignore
1619
1669
  root.ENGINE = instance
1620
1670
  }
1621
1671
 
1672
+ DEV: console.info(`[litecanvas] version ${version} started`)
1673
+ DEV: console.debug(`[litecanvas] litecanvas() options =`, settings)
1674
+
1622
1675
  setupCanvas()
1623
1676
 
1624
1677
  if ('loading' === document.readyState) {