PageSourceSearch

https://fancymonkey.com/assets/js/vendor/canvas-confetti.js

js fancymonkey.com collected 2026-10-03 19:41:26 UTC 25,779 bytes, 906 lines download raw bytes

1/* ============================================================
2   canvas-confetti v1.9.4 — ISC
3   https://github.com/catdad/canvas-confetti
4
5   VENDORED, not fetched. The site CSP is script-src 'self' with no
6   CDN origins, so a <script src> at unpkg would simply be blocked —
7   the same reason the fonts are self-hosted (DECISIONS #10).
8
9   This file is dist/confetti.module.mjs copied verbatim out of the
10   npm package. Do not edit it. To update:
11
12     npm install --save-dev canvas-confetti@latest
13     cp node_modules/canvas-confetti/dist/confetti.module.mjs \
14        public/assets/js/vendor/canvas-confetti.js
15     (then put this header back)
16
17   The devDependency exists only to record the version and make
18   that copy reproducible; nothing at runtime reads node_modules.
19   ============================================================ */
20// canvas-confetti v1.9.4 built on 2025-10-25T05:14:56.640Z
21var module = {};
vendor: 20,715 bytes, lines 22-758
22
23// source content
24/* globals Map */
25
26(function main(global, module, isWorker, workerSize) {
27  var canUseWorker = !!(
28    global.Worker &&
29    global.Blob &&
30    global.Promise &&
31    global.OffscreenCanvas &&
32    global.OffscreenCanvasRenderingContext2D &&
33    global.HTMLCanvasElement &&
34    global.HTMLCanvasElement.prototype.transferControlToOffscreen &&
35    global.URL &&
36    global.URL.createObjectURL);
37
38  var canUsePaths = typeof Path2D === 'function' && typeof DOMMatrix === 'function';
39  var canDrawBitmap = (function () {
40    // this mostly supports ssr
41    if (!global.OffscreenCanvas) {
42      return false;
43    }
44
45    try {
46      var canvas = new OffscreenCanvas(1, 1);
47      var ctx = canvas.getContext('2d');
48      ctx.fillRect(0, 0, 1, 1);
49      var bitmap = canvas.transferToImageBitmap();
50      ctx.createPattern(bitmap, 'no-repeat');
51    } catch (e) {
52      return false;
53    }
54
55    return true;
56  })();
57
58  function noop() {}
59
60  // create a promise if it exists, otherwise, just
61  // call the function directly
62  function promise(func) {
63    var ModulePromise = module.exports.Promise;
64    var Prom = ModulePromise !== void 0 ? ModulePromise : global.Promise;
65
66    if (typeof Prom === 'function') {
67      return new Prom(func);
68    }
69
70    func(noop, noop);
71
72    return null;
73  }
74
75  var bitmapMapper = (function (skipTransform, map) {
76    // see https://github.com/catdad/canvas-confetti/issues/209
77    // creating canvases is actually pretty expensive, so we should create a
78    // 1:1 map for bitmap:canvas, so that we can animate the confetti in
79    // a performant manner, but also not store them forever so that we don't
80    // have a memory leak
81    return {
82      transform: function(bitmap) {
83        if (skipTransform) {
84          return bitmap;
85        }
86
87        if (map.has(bitmap)) {
88          return map.get(bitmap);
89        }
90
91        var canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
92        var ctx = canvas.getContext('2d');
93        ctx.drawImage(bitmap, 0, 0);
94
95        map.set(bitmap, canvas);
96
97        return canvas;
98      },
99      clear: function () {
100        map.clear();
101      }
102    };
103  })(canDrawBitmap, new Map());
104
105  var raf = (function () {
106    var TIME = Math.floor(1000 / 60);
107    var frame, cancel;
108    var frames = {};
109    var lastFrameTime = 0;
110
111    if (typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function') {
112      frame = function (cb) {
113        var id = Math.random();
114
115        frames[id] = requestAnimationFrame(function onFrame(time) {
116          if (lastFrameTime === time || lastFrameTime + TIME - 1 < time) {
117            lastFrameTime = time;
118            delete frames[id];
119
120            cb();
121          } else {
122            frames[id] = requestAnimationFrame(onFrame);
123          }
124        });
125
126        return id;
127      };
128      cancel = function (id) {
129        if (frames[id]) {
130          cancelAnimationFrame(frames[id]);
131        }
132      };
133    } else {
134      frame = function (cb) {
135        return setTimeout(cb, TIME);
136      };
137      cancel = function (timer) {
138        return clearTimeout(timer);
139      };
140    }
141
142    return { frame: frame, cancel: cancel };
143  }());
144
145  var getWorker = (function () {
146    var worker;
147    var prom;
148    var resolves = {};
149
150    function decorate(worker) {
151      function execute(options, callback) {
152        worker.postMessage({ options: options || {}, callback: callback });
153      }
154      worker.init = function initWorker(canvas) {
155        var offscreen = canvas.transferControlToOffscreen();
156        worker.postMessage({ canvas: offscreen }, [offscreen]);
157      };
158
159      worker.fire = function fireWorker(options, size, done) {
160        if (prom) {
161          execute(options, null);
162          return prom;
163        }
164
165        var id = Math.random().toString(36).slice(2);
166
167        prom = promise(function (resolve) {
168          function workerDone(msg) {
169            if (msg.data.callback !== id) {
170              return;
171            }
172
173            delete resolves[id];
174            worker.removeEventListener('message', workerDone);
175
176            prom = null;
177
178            bitmapMapper.clear();
179
180            done();
181            resolve();
182          }
183
184          worker.addEventListener('message', workerDone);
185          execute(options, id);
186
187          resolves[id] = workerDone.bind(null, { data: { callback: id }});
188        });
189
190        return prom;
191      };
192
193      worker.reset = function resetWorker() {
194        worker.postMessage({ reset: true });
195
196        for (var id in resolves) {
197          resolves[id]();
198          delete resolves[id];
199        }
200      };
201    }
202
203    return function () {
204      if (worker) {
205        return worker;
206      }
207
208      if (!isWorker && canUseWorker) {
209        var code = [
210          'var CONFETTI, SIZE = {}, module = {};',
211          '(' + main.toString() + ')(this, module, true, SIZE);',
212          'onmessage = function(msg) {',
213          '  if (msg.data.options) {',
214          '    CONFETTI(msg.data.options).then(function () {',
215          '      if (msg.data.callback) {',
216          '        postMessage({ callback: msg.data.callback });',
217          '      }',
218          '    });',
219          '  } else if (msg.data.reset) {',
220          '    CONFETTI && CONFETTI.reset();',
221          '  } else if (msg.data.resize) {',
222          '    SIZE.width = msg.data.resize.width;',
223          '    SIZE.height = msg.data.resize.height;',
224          '  } else if (msg.data.canvas) {',
225          '    SIZE.width = msg.data.canvas.width;',
226          '    SIZE.height = msg.data.canvas.height;',
227          '    CONFETTI = module.exports.create(msg.data.canvas);',
228          '  }',
229          '}',
230        ].join('\n');
231        try {
232          worker = new Worker(URL.createObjectURL(new Blob([code])));
233        } catch (e) {
234          // eslint-disable-next-line no-console
235          typeof console !== 'undefined' && typeof console.warn === 'function' ? console.warn('🎊 Could not load worker', e) : null;
236
237          return null;
238        }
239
240        decorate(worker);
241      }
242
243      return worker;
244    };
245  })();
246
247  var defaults = {
248    particleCount: 50,
249    angle: 90,
250    spread: 45,
251    startVelocity: 45,
252    decay: 0.9,
253    gravity: 1,
254    drift: 0,
255    ticks: 200,
256    x: 0.5,
257    y: 0.5,
258    shapes: ['square', 'circle'],
259    zIndex: 100,
260    colors: [
261      '#26ccff',
262      '#a25afd',
263      '#ff5e7e',
264      '#88ff5a',
265      '#fcff42',
266      '#ffa62d',
267      '#ff36ff'
268    ],
269    // probably should be true, but back-compat
270    disableForReducedMotion: false,
271    scalar: 1
272  };
273
274  function convert(val, transform) {
275    return transform ? transform(val) : val;
276  }
277
278  function isOk(val) {
279    return !(val === null || val === undefined);
280  }
281
282  function prop(options, name, transform) {
283    return convert(
284      options && isOk(options[name]) ? options[name] : defaults[name],
285      transform
286    );
287  }
288
289  function onlyPositiveInt(number){
290    return number < 0 ? 0 : Math.floor(number);
291  }
292
293  function randomInt(min, max) {
294    // [min, max)
295    return Math.floor(Math.random() * (max - min)) + min;
296  }
297
298  function toDecimal(str) {
299    return parseInt(str, 16);
300  }
301
302  function colorsToRgb(colors) {
303    return colors.map(hexToRgb);
304  }
305
306  function hexToRgb(str) {
307    var val = String(str).replace(/[^0-9a-f]/gi, '');
308
309    if (val.length < 6) {
310        val = val[0]+val[0]+val[1]+val[1]+val[2]+val[2];
311    }
312
313    return {
314      r: toDecimal(val.substring(0,2)),
315      g: toDecimal(val.substring(2,4)),
316      b: toDecimal(val.substring(4,6))
317    };
318  }
319
320  function getOrigin(options) {
321    var origin = prop(options, 'origin', Object);
322    origin.x = prop(origin, 'x', Number);
323    origin.y = prop(origin, 'y', Number);
324
325    return origin;
326  }
327
328  function setCanvasWindowSize(canvas) {
329    canvas.width = document.documentElement.clientWidth;
330    canvas.height = document.documentElement.clientHeight;
331  }
332
333  function setCanvasRectSize(canvas) {
334    var rect = canvas.getBoundingClientRect();
335    canvas.width = rect.width;
336    canvas.height = rect.height;
337  }
338
339  function getCanvas(zIndex) {
340    var canvas = document.createElement('canvas');
341
342    canvas.style.position = 'fixed';
343    canvas.style.top = '0px';
344    canvas.style.left = '0px';
345    canvas.style.pointerEvents = 'none';
346    canvas.style.zIndex = zIndex;
347
348    return canvas;
349  }
350
351  function ellipse(context, x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
352    context.save();
353    context.translate(x, y);
354    context.rotate(rotation);
355    context.scale(radiusX, radiusY);
356    context.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
357    context.restore();
358  }
359
360  function randomPhysics(opts) {
361    var radAngle = opts.angle * (Math.PI / 180);
362    var radSpread = opts.spread * (Math.PI / 180);
363
364    return {
365      x: opts.x,
366      y: opts.y,
367      wobble: Math.random() * 10,
368      wobbleSpeed: Math.min(0.11, Math.random() * 0.1 + 0.05),
369      velocity: (opts.startVelocity * 0.5) + (Math.random() * opts.startVelocity),
370      angle2D: -radAngle + ((0.5 * radSpread) - (Math.random() * radSpread)),
371      tiltAngle: (Math.random() * (0.75 - 0.25) + 0.25) * Math.PI,
372      color: opts.color,
373      shape: opts.shape,
374      tick: 0,
375      totalTicks: opts.ticks,
376      decay: opts.decay,
377      drift: opts.drift,
378      random: Math.random() + 2,
379      tiltSin: 0,
380      tiltCos: 0,
381      wobbleX: 0,
382      wobbleY: 0,
383      gravity: opts.gravity * 3,
384      ovalScalar: 0.6,
385      scalar: opts.scalar,
386      flat: opts.flat
387    };
388  }
389
390  function updateFetti(context, fetti) {
391    fetti.x += Math.cos(fetti.angle2D) * fetti.velocity + fetti.drift;
392    fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;
393    fetti.velocity *= fetti.decay;
394
395    if (fetti.flat) {
396      fetti.wobble = 0;
397      fetti.wobbleX = fetti.x + (10 * fetti.scalar);
398      fetti.wobbleY = fetti.y + (10 * fetti.scalar);
399
400      fetti.tiltSin = 0;
401      fetti.tiltCos = 0;
402      fetti.random = 1;
403    } else {
404      fetti.wobble += fetti.wobbleSpeed;
405      fetti.wobbleX = fetti.x + ((10 * fetti.scalar) * Math.cos(fetti.wobble));
406      fetti.wobbleY = fetti.y + ((10 * fetti.scalar) * Math.sin(fetti.wobble));
407
408      fetti.tiltAngle += 0.1;
409      fetti.tiltSin = Math.sin(fetti.tiltAngle);
410      fetti.tiltCos = Math.cos(fetti.tiltAngle);
411      fetti.random = Math.random() + 2;
412    }
413
414    var progress = (fetti.tick++) / fetti.totalTicks;
415
416    var x1 = fetti.x + (fetti.random * fetti.tiltCos);
417    var y1 = fetti.y + (fetti.random * fetti.tiltSin);
418    var x2 = fetti.wobbleX + (fetti.random * fetti.tiltCos);
419    var y2 = fetti.wobbleY + (fetti.random * fetti.tiltSin);
420
421    context.fillStyle = 'rgba(' + fetti.color.r + ', ' + fetti.color.g + ', ' + fetti.color.b + ', ' + (1 - progress) + ')';
422
423    context.beginPath();
424
425    if (canUsePaths && fetti.shape.type === 'path' && typeof fetti.shape.path === 'string' && Array.isArray(fetti.shape.matrix)) {
426      context.fill(transformPath2D(
427        fetti.shape.path,
428        fetti.shape.matrix,
429        fetti.x,
430        fetti.y,
431        Math.abs(x2 - x1) * 0.1,
432        Math.abs(y2 - y1) * 0.1,
433        Math.PI / 10 * fetti.wobble
434      ));
435    } else if (fetti.shape.type === 'bitmap') {
436      var rotation = Math.PI / 10 * fetti.wobble;
437      var scaleX = Math.abs(x2 - x1) * 0.1;
438      var scaleY = Math.abs(y2 - y1) * 0.1;
439      var width = fetti.shape.bitmap.width * fetti.scalar;
440      var height = fetti.shape.bitmap.height * fetti.scalar;
441
442      var matrix = new DOMMatrix([
443        Math.cos(rotation) * scaleX,
444        Math.sin(rotation) * scaleX,
445        -Math.sin(rotation) * scaleY,
446        Math.cos(rotation) * scaleY,
447        fetti.x,
448        fetti.y
449      ]);
450
451      // apply the transform matrix from the confetti shape
452      matrix.multiplySelf(new DOMMatrix(fetti.shape.matrix));
453
454      var pattern = context.createPattern(bitmapMapper.transform(fetti.shape.bitmap), 'no-repeat');
455      pattern.setTransform(matrix);
456
457      context.globalAlpha = (1 - progress);
458      context.fillStyle = pattern;
459      context.fillRect(
460        fetti.x - (width / 2),
461        fetti.y - (height / 2),
462        width,
463        height
464      );
465      context.globalAlpha = 1;
466    } else if (fetti.shape === 'circle') {
467      context.ellipse ?
468        context.ellipse(fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI) :
469        ellipse(context, fetti.x, fetti.y, Math.abs(x2 - x1) * fetti.ovalScalar, Math.abs(y2 - y1) * fetti.ovalScalar, Math.PI / 10 * fetti.wobble, 0, 2 * Math.PI);
470    } else if (fetti.shape === 'star') {
471      var rot = Math.PI / 2 * 3;
472      var innerRadius = 4 * fetti.scalar;
473      var outerRadius = 8 * fetti.scalar;
474      var x = fetti.x;
475      var y = fetti.y;
476      var spikes = 5;
477      var step = Math.PI / spikes;
478
479      while (spikes--) {
480        x = fetti.x + Math.cos(rot) * outerRadius;
481        y = fetti.y + Math.sin(rot) * outerRadius;
482        context.lineTo(x, y);
483        rot += step;
484
485        x = fetti.x + Math.cos(rot) * innerRadius;
486        y = fetti.y + Math.sin(rot) * innerRadius;
487        context.lineTo(x, y);
488        rot += step;
489      }
490    } else {
491      context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y));
492      context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1));
493      context.lineTo(Math.floor(x2), Math.floor(y2));
494      context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY));
495    }
496
497    context.closePath();
498    context.fill();
499
500    return fetti.tick < fetti.totalTicks;
501  }
502
503  function animate(canvas, fettis, resizer, size, done) {
504    var animatingFettis = fettis.slice();
505    var context = canvas.getContext('2d');
506    var animationFrame;
507    var destroy;
508
509    var prom = promise(function (resolve) {
510      function onDone() {
511        animationFrame = destroy = null;
512
513        context.clearRect(0, 0, size.width, size.height);
514        bitmapMapper.clear();
515
516        done();
517        resolve();
518      }
519
520      function update() {
521        if (isWorker && !(size.width === workerSize.width && size.height === workerSize.height)) {
522          size.width = canvas.width = workerSize.width;
523          size.height = canvas.height = workerSize.height;
524        }
525
526        if (!size.width && !size.height) {
527          resizer(canvas);
528          size.width = canvas.width;
529          size.height = canvas.height;
530        }
531
532        context.clearRect(0, 0, size.width, size.height);
533
534        animatingFettis = animatingFettis.filter(function (fetti) {
535          return updateFetti(context, fetti);
536        });
537
538        if (animatingFettis.length) {
539          animationFrame = raf.frame(update);
540        } else {
541          onDone();
542        }
543      }
544
545      animationFrame = raf.frame(update);
546      destroy = onDone;
547    });
548
549    return {
550      addFettis: function (fettis) {
551        animatingFettis = animatingFettis.concat(fettis);
552
553        return prom;
554      },
555      canvas: canvas,
556      promise: prom,
557      reset: function () {
558        if (animationFrame) {
559          raf.cancel(animationFrame);
560        }
561
562        if (destroy) {
563          destroy();
564        }
565      }
566    };
567  }
568
569  function confettiCannon(canvas, globalOpts) {
570    var isLibCanvas = !canvas;
571    var allowResize = !!prop(globalOpts || {}, 'resize');
572    var hasResizeEventRegistered = false;
573    var globalDisableForReducedMotion = prop(globalOpts, 'disableForReducedMotion', Boolean);
574    var shouldUseWorker = canUseWorker && !!prop(globalOpts || {}, 'useWorker');
575    var worker = shouldUseWorker ? getWorker() : null;
576    var resizer = isLibCanvas ? setCanvasWindowSize : setCanvasRectSize;
577    var initialized = (canvas && worker) ? !!canvas.__confetti_initialized : false;
578    var preferLessMotion = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion)').matches;
579    var animationObj;
580
581    function fireLocal(options, size, done) {
582      var particleCount = prop(options, 'particleCount', onlyPositiveInt);
583      var angle = prop(options, 'angle', Number);
584      var spread = prop(options, 'spread', Number);
585      var startVelocity = prop(options, 'startVelocity', Number);
586      var decay = prop(options, 'decay', Number);
587      var gravity = prop(options, 'gravity', Number);
588      var drift = prop(options, 'drift', Number);
589      var colors = prop(options, 'colors', colorsToRgb);
590      var ticks = prop(options, 'ticks', Number);
591      var shapes = prop(options, 'shapes');
592      var scalar = prop(options, 'scalar');
593      var flat = !!prop(options, 'flat');
594      var origin = getOrigin(options);
595
596      var temp = particleCount;
597      var fettis = [];
598
599      var startX = canvas.width * origin.x;
600      var startY = canvas.height * origin.y;
601
602      while (temp--) {
603        fettis.push(
604          randomPhysics({
605            x: startX,
606            y: startY,
607            angle: angle,
608            spread: spread,
609            startVelocity: startVelocity,
610            color: colors[temp % colors.length],
611            shape: shapes[randomInt(0, shapes.length)],
612            ticks: ticks,
613            decay: decay,
614            gravity: gravity,
615            drift: drift,
616            scalar: scalar,
617            flat: flat
618          })
619        );
620      }
621
622      // if we have a previous canvas already animating,
623      // add to it
624      if (animationObj) {
625        return animationObj.addFettis(fettis);
626      }
627
628      animationObj = animate(canvas, fettis, resizer, size , done);
629
630      return animationObj.promise;
631    }
632
633    function fire(options) {
634      var disableForReducedMotion = globalDisableForReducedMotion || prop(options, 'disableForReducedMotion', Boolean);
635      var zIndex = prop(options, 'zIndex', Number);
636
637      if (disableForReducedMotion && preferLessMotion) {
638        return promise(function (resolve) {
639          resolve();
640        });
641      }
642
643      if (isLibCanvas && animationObj) {
644        // use existing canvas from in-progress animation
645        canvas = animationObj.canvas;
646      } else if (isLibCanvas && !canvas) {
647        // create and initialize a new canvas
648        canvas = getCanvas(zIndex);
649        document.body.appendChild(canvas);
650      }
651
652      if (allowResize && !initialized) {
653        // initialize the size of a user-supplied canvas
654        resizer(canvas);
655      }
656
657      var size = {
658        width: canvas.width,
659        height: canvas.height
660      };
661
662      if (worker && !initialized) {
663        worker.init(canvas);
664      }
665
666      initialized = true;
667
668      if (worker) {
669        canvas.__confetti_initialized = true;
670      }
671
672      function onResize() {
673        if (worker) {
674          // TODO this really shouldn't be immediate, because it is expensive
675          var obj = {
676            getBoundingClientRect: function () {
677              if (!isLibCanvas) {
678                return canvas.getBoundingClientRect();
679              }
680            }
681          };
682
683          resizer(obj);
684
685          worker.postMessage({
686            resize: {
687              width: obj.width,
688              height: obj.height
689            }
690          });
691          return;
692        }
693
694        // don't actually query the size here, since this
695        // can execute frequently and rapidly
696        size.width = size.height = null;
697      }
698
699      function done() {
700        animationObj = null;
701
702        if (allowResize) {
703          hasResizeEventRegistered = false;
704          global.removeEventListener('resize', onResize);
705        }
706
707        if (isLibCanvas && canvas) {
708          if (document.body.contains(canvas)) {
709            document.body.removeChild(canvas);
710          }
711          canvas = null;
712          initialized = false;
713        }
714      }
715
716      if (allowResize && !hasResizeEventRegistered) {
717        hasResizeEventRegistered = true;
718        global.addEventListener('resize', onResize, false);
719      }
720
721      if (worker) {
722        return worker.fire(options, size, done);
723      }
724
725      return fireLocal(options, size, done);
726    }
727
728    fire.reset = function () {
729      if (worker) {
730        worker.reset();
731      }
732
733      if (animationObj) {
734        animationObj.reset();
735      }
736    };
737
738    return fire;
739  }
740
741  // Make default export lazy to defer worker creation until called.
742  var defaultFire;
743  function getDefaultFire() {
744    if (!defaultFire) {
745      defaultFire = confettiCannon(null, { useWorker: true, resize: true });
746    }
747    return defaultFire;
748  }
749
750  function transformPath2D(pathString, pathMatrix, x, y, scaleX, scaleY, rotation) {
751    var path2d = new Path2D(pathString);
752
753    var t1 = new Path2D();
754    t1.addPath(path2d, new DOMMatrix(pathMatrix));
755
756    var t2 = new Path2D();
757    // see https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix/DOMMatrix
758    t2.addPath(t1, new DOMMatrix([
vendor: 4,132 bytes, lines 759-906
759      Math.cos(rotation) * scaleX,
760      Math.sin(rotation) * scaleX,
761      -Math.sin(rotation) * scaleY,
762      Math.cos(rotation) * scaleY,
763      x,
764      y
765    ]));
766
767    return t2;
768  }
769
770  function shapeFromPath(pathData) {
771    if (!canUsePaths) {
772      throw new Error('path confetti are not supported in this browser');
773    }
774
775    var path, matrix;
776
777    if (typeof pathData === 'string') {
778      path = pathData;
779    } else {
780      path = pathData.path;
781      matrix = pathData.matrix;
782    }
783
784    var path2d = new Path2D(path);
785    var tempCanvas = document.createElement('canvas');
786    var tempCtx = tempCanvas.getContext('2d');
787
788    if (!matrix) {
789      // attempt to figure out the width of the path, up to 1000x1000
790      var maxSize = 1000;
791      var minX = maxSize;
792      var minY = maxSize;
793      var maxX = 0;
794      var maxY = 0;
795      var width, height;
796
797      // do some line skipping... this is faster than checking
798      // every pixel and will be mostly still correct
799      for (var x = 0; x < maxSize; x += 2) {
800        for (var y = 0; y < maxSize; y += 2) {
801          if (tempCtx.isPointInPath(path2d, x, y, 'nonzero')) {
802            minX = Math.min(minX, x);
803            minY = Math.min(minY, y);
804            maxX = Math.max(maxX, x);
805            maxY = Math.max(maxY, y);
806          }
807        }
808      }
809
810      width = maxX - minX;
811      height = maxY - minY;
812
813      var maxDesiredSize = 10;
814      var scale = Math.min(maxDesiredSize/width, maxDesiredSize/height);
815
816      matrix = [
817        scale, 0, 0, scale,
818        -Math.round((width/2) + minX) * scale,
819        -Math.round((height/2) + minY) * scale
820      ];
821    }
822
823    return {
824      type: 'path',
825      path: path,
826      matrix: matrix
827    };
828  }
829
830  function shapeFromText(textData) {
831    var text,
832        scalar = 1,
833        color = '#000000',
834        // see https://nolanlawson.com/2022/04/08/the-struggle-of-using-native-emoji-on-the-web/
835        fontFamily = '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", "EmojiOne Color", "Android Emoji", "Twemoji Mozilla", "system emoji", sans-serif';
836
837    if (typeof textData === 'string') {
838      text = textData;
839    } else {
840      text = textData.text;
841      scalar = 'scalar' in textData ? textData.scalar : scalar;
842      fontFamily = 'fontFamily' in textData ? textData.fontFamily : fontFamily;
843      color = 'color' in textData ? textData.color : color;
844    }
845
846    // all other confetti are 10 pixels,
847    // so this pixel size is the de-facto 100% scale confetti
848    var fontSize = 10 * scalar;
849    var font = '' + fontSize + 'px ' + fontFamily;
850
851    var canvas = new OffscreenCanvas(fontSize, fontSize);
852    var ctx = canvas.getContext('2d');
853
854    ctx.font = font;
855    var size = ctx.measureText(text);
856    var width = Math.ceil(size.actualBoundingBoxRight + size.actualBoundingBoxLeft);
857    var height = Math.ceil(size.actualBoundingBoxAscent + size.actualBoundingBoxDescent);
858
859    var padding = 2;
860    var x = size.actualBoundingBoxLeft + padding;
861    var y = size.actualBoundingBoxAscent + padding;
862    width += padding + padding;
863    height += padding + padding;
864
865    canvas = new OffscreenCanvas(width, height);
866    ctx = canvas.getContext('2d');
867    ctx.font = font;
868    ctx.fillStyle = color;
869
870    ctx.fillText(text, x, y);
871
872    var scale = 1 / scalar;
873
874    return {
875      type: 'bitmap',
876      // TODO these probably need to be transfered for workers
877      bitmap: canvas.transferToImageBitmap(),
878      matrix: [scale, 0, 0, scale, -width * scale / 2, -height * scale / 2]
879    };
880  }
881
882  module.exports = function() {
883    return getDefaultFire().apply(this, arguments);
884  };
885  module.exports.reset = function() {
886    getDefaultFire().reset();
887  };
888  module.exports.create = confettiCannon;
889  module.exports.shapeFromPath = shapeFromPath;
890  module.exports.shapeFromText = shapeFromText;
891}((function () {
892  if (typeof window !== 'undefined') {
893    return window;
894  }
895
896  if (typeof self !== 'undefined') {
897    return self;
898  }
899
900  return this || {};
901})(), module, false));
902
903// end source content
904
905export default module.exports;
906export var create = module.exports.create;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.