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.