@tsparticles/preset-confetti-falling 4.4.0 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,35 @@
1
- (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.4.0 */
3
- (function (global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.presets = global.__tsParticlesInternals.presets || {}, global.__tsParticlesInternals.presets.confettiFalling = global.__tsParticlesInternals.presets.confettiFalling || {})));
7
- })(this, (function (exports) { 'use strict';
8
-
9
- const generatedAttribute = "generated", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
10
- x: 0,
11
- y: 0,
12
- z: 0,
13
- }, defaultTransform = {
14
- a: 1,
15
- b: 0,
16
- c: 0,
17
- d: 1,
18
- }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity$1 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
1
+ (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
+ (function (g) {
3
+ /* Preset v4.5.1 */
4
+ g.__tsParticlesInternals = g.__tsParticlesInternals || {};
5
+ g.__tsParticlesInternals.presets = g.__tsParticlesInternals.presets || {};
6
+ (function (exports) {
7
+ 'use strict';
8
+
9
+ const errorPrefix = "tsParticles - Error", codedErrorPattern = /^\[tsParticles Error (TSP-\d+)\]\s*/, fullMatchIndex = 0, codeIndex = 1;
10
+ const wrap = (fn) => (...args) => {
11
+ fn(...args);
12
+ }, _logger = {
13
+ debug: wrap(console.debug),
14
+ error: (message, ...optionalParams) => {
15
+ if (typeof message === "string") {
16
+ const match = codedErrorPattern.exec(message);
17
+ if (match?.[codeIndex]) {
18
+ console.error(`tsParticles - ${match[codeIndex]} - ${message.slice(match[fullMatchIndex].length)}`, ...optionalParams);
19
+ return;
20
+ }
21
+ }
22
+ console.error(`${errorPrefix} - ${message}`, ...optionalParams);
23
+ },
24
+ info: wrap(console.info),
25
+ log: wrap(console.log),
26
+ trace: wrap(console.trace),
27
+ verbose: wrap(console.log),
28
+ warning: wrap(console.warn),
29
+ };
30
+ function getLogger() {
31
+ return _logger;
32
+ }
19
33
 
20
34
  var MoveDirection;
21
35
  (function (MoveDirection) {
@@ -32,6 +46,17 @@
32
46
  MoveDirection["inside"] = "inside";
33
47
  })(MoveDirection || (MoveDirection = {}));
34
48
 
49
+ const generatedAttribute = "generated", fullPercent = "100%", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
50
+ x: 0,
51
+ y: 0,
52
+ z: 0,
53
+ }, defaultTransform = {
54
+ a: 1,
55
+ b: 0,
56
+ c: 0,
57
+ d: 1,
58
+ }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, minRadius = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity$1 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
59
+
35
60
  function getZ(source) {
36
61
  return "z" in source ? source.z : originPoint.z;
37
62
  }
@@ -147,6 +172,12 @@
147
172
  function isNumber(arg) {
148
173
  return typeof arg === "number";
149
174
  }
175
+ function isEnumValue(enumObject, arg) {
176
+ return Object.values(enumObject).some(enumValue => enumValue === arg);
177
+ }
178
+ function toEnumValue(enumObject, arg, defaultValue) {
179
+ return isEnumValue(enumObject, arg) ? arg : defaultValue;
180
+ }
150
181
  function isObject(arg) {
151
182
  return typeof arg === "object" && arg !== null;
152
183
  }
@@ -270,11 +301,61 @@
270
301
  };
271
302
  }
272
303
  function parseAlpha(input) {
273
- const defaultAlpha = 1;
274
304
  if (!input) {
275
305
  return defaultAlpha;
276
306
  }
277
- return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
307
+ return input.endsWith("%") ? Number.parseFloat(input) / percentDenominator : Number.parseFloat(input);
308
+ }
309
+
310
+ const ErrorCodes$1 = {
311
+ engineVersionMismatch: "TSP-1001",
312
+ particlePositionNotFound: "TSP-1002",
313
+ pluginRegisterAfterLoad: "TSP-1003",
314
+ offscreenCanvasUnsupported: "TSP-1101",
315
+ offscreenCanvasTransferFailed: "TSP-1102",
316
+ hdrEnableInvalid: "TSP-1201",
317
+ hdrModeInvalid: "TSP-1202",
318
+ hdrPeakNitsInvalid: "TSP-1203",
319
+ };
320
+
321
+ const messages = {
322
+ [ErrorCodes$1.engineVersionMismatch]: "The tsParticles version is different from the loaded plugins version. Engine version: {0}. Plugin version: {1}",
323
+ [ErrorCodes$1.particlePositionNotFound]: "a valid position cannot be found for particle",
324
+ [ErrorCodes$1.pluginRegisterAfterLoad]: "Register plugins can only be done before calling tsParticles.load()",
325
+ [ErrorCodes$1.offscreenCanvasUnsupported]: "OffscreenCanvas is required but not supported by this browser",
326
+ [ErrorCodes$1.offscreenCanvasTransferFailed]: "OffscreenCanvas transfer failed",
327
+ [ErrorCodes$1.hdrEnableInvalid]: 'Invalid HDR "enable" value: expected a boolean, got "{0}"',
328
+ [ErrorCodes$1.hdrModeInvalid]: 'Invalid HDR "mode" value: expected one of {0}, got "{1}"',
329
+ [ErrorCodes$1.hdrPeakNitsInvalid]: 'Invalid HDR "peakNits" value: expected a positive number, got "{0}"',
330
+ };
331
+
332
+ let registeredMessages;
333
+ const ensureRegisteredMessages = () => {
334
+ registeredMessages ??= new Map(Object.entries(messages));
335
+ return registeredMessages;
336
+ }, toStringValue$1 = (value) => String(value), format = (template, args) => {
337
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
338
+ const arg = args[Number(index)];
339
+ return arg === undefined ? match : toStringValue$1(arg);
340
+ });
341
+ };
342
+ function addErrorMessages(extraMessages) {
343
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
344
+ return;
345
+ }
346
+ const catalog = ensureRegisteredMessages();
347
+ for (const [code, message] of Object.entries(extraMessages)) {
348
+ if (message !== undefined) {
349
+ catalog.set(code, message);
350
+ }
351
+ }
352
+ }
353
+ function getErrorMessage(code, ...args) {
354
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
355
+ return `[tsParticles Error ${code}] https://particles.js.org/errors/#${code}`;
356
+ }
357
+ const template = ensureRegisteredMessages().get(code) ?? `Unknown error code ${code}`;
358
+ return format(template, args);
278
359
  }
279
360
 
280
361
  var OutModeDirection;
@@ -291,7 +372,6 @@
291
372
  PixelMode["percent"] = "percent";
292
373
  })(PixelMode || (PixelMode = {}));
293
374
 
294
- const minRadius = 0;
295
375
  function isForbiddenKey(key) {
296
376
  return key === "__proto__" || key === "constructor" || key === "prototype";
297
377
  }
@@ -487,22 +567,84 @@
487
567
  element.removeEventListener(event, handler, removeOptions);
488
568
  }
489
569
  }
490
- async function getItemsFromInitializer(container, map, initializers, force = false) {
491
- let res = map.get(container);
492
- if (!res || force) {
493
- res = await Promise.all([...initializers.values()].map(t => t(container)));
494
- map.set(container, res);
570
+ async function getItemsFromInitializer(container, initializers) {
571
+ return await Promise.all([...initializers.values()].map(t => t(container)));
572
+ }
573
+ async function getItemMapFromInitializer(container, initializers) {
574
+ const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
575
+ return new Map(entries);
576
+ }
577
+ async function getDataFromUrl(data) {
578
+ const url = itemFromSingleOrMultiple(data.url, data.index);
579
+ if (!url) {
580
+ return data.fallback;
495
581
  }
496
- return res;
582
+ try {
583
+ const response = await fetch(url);
584
+ if (response.ok) {
585
+ const current = (await response.json());
586
+ if (isSourceOptionsValue(current)) {
587
+ return current;
588
+ }
589
+ getLogger().error("invalid configuration data while retrieving config file");
590
+ }
591
+ else {
592
+ getLogger().error(`${response.status.toString()} while retrieving config file`);
593
+ }
594
+ }
595
+ catch (error) {
596
+ getLogger().error(`error while retrieving config file: ${error}`);
597
+ }
598
+ return data.fallback;
497
599
  }
498
- async function getItemMapFromInitializer(container, map, initializers, force = false) {
499
- let res = map.get(container);
500
- if (!res || force) {
501
- const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
502
- res = new Map(entries);
503
- map.set(container, res);
600
+ function isSourceOptionsValue(value) {
601
+ if (isArray(value)) {
602
+ return value.every((entry) => isObject(entry) && !isArray(entry));
504
603
  }
505
- return res;
604
+ return isObject(value) && !isArray(value);
605
+ }
606
+ function getCanvasFromContainer(domContainer) {
607
+ let canvasEl;
608
+ const documentSafe = safeDocument(), isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
609
+ if (isCanvas) {
610
+ canvasEl = domContainer;
611
+ canvasEl.dataset[generatedAttribute] ??= generatedFalse;
612
+ if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
613
+ canvasEl.style.width ||= fullPercent;
614
+ canvasEl.style.height ||= fullPercent;
615
+ canvasEl.style.pointerEvents = "none";
616
+ canvasEl.style.setProperty("pointer-events", "none");
617
+ }
618
+ }
619
+ else {
620
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
621
+ if (foundCanvas) {
622
+ canvasEl = foundCanvas;
623
+ canvasEl.dataset[generatedAttribute] = generatedFalse;
624
+ }
625
+ else {
626
+ canvasEl = documentSafe.createElement(canvasTag);
627
+ canvasEl.dataset[generatedAttribute] = generatedTrue;
628
+ domContainer.appendChild(canvasEl);
629
+ }
630
+ canvasEl.style.width ||= fullPercent;
631
+ canvasEl.style.height ||= fullPercent;
632
+ canvasEl.style.pointerEvents = "none";
633
+ canvasEl.style.setProperty("pointer-events", "none");
634
+ }
635
+ return canvasEl;
636
+ }
637
+ function getDomContainer(id, source) {
638
+ const documentSafe = safeDocument();
639
+ let domContainer = source ?? documentSafe.getElementById(id);
640
+ if (domContainer) {
641
+ return domContainer;
642
+ }
643
+ domContainer = documentSafe.createElement("canvas");
644
+ domContainer.id = id;
645
+ domContainer.dataset[generatedAttribute] = generatedTrue;
646
+ documentSafe.body.append(domContainer);
647
+ return domContainer;
506
648
  }
507
649
 
508
650
  class EventDispatcher {
@@ -512,12 +654,8 @@
512
654
  }
513
655
  addEventListener(type, listener) {
514
656
  this.removeEventListener(type, listener);
515
- let arr = this.#listeners.get(type);
516
- if (!arr) {
517
- arr = [];
518
- this.#listeners.set(type, arr);
519
- }
520
- arr.push(listener);
657
+ const arr = this.#listeners.get(type);
658
+ this.#listeners.set(type, arr ? [...arr, listener] : [listener]);
521
659
  }
522
660
  dispatchEvent(type, args) {
523
661
  const listeners = this.#listeners.get(type);
@@ -549,7 +687,7 @@
549
687
  this.#listeners.delete(type);
550
688
  }
551
689
  else {
552
- arr.splice(idx, deleteCount);
690
+ this.#listeners.set(type, [...arr.slice(minIndex, idx), ...arr.slice(idx + deleteCount)]);
553
691
  }
554
692
  }
555
693
  }
@@ -573,7 +711,6 @@
573
711
  class PluginManager {
574
712
  colorManagers = new Map();
575
713
  easingFunctions = new Map();
576
- effectDrawers = new Map();
577
714
  initializers = {
578
715
  effects: new Map(),
579
716
  shapes: new Map(),
@@ -582,8 +719,6 @@
582
719
  palettes = new Map();
583
720
  plugins = [];
584
721
  presets = new Map();
585
- shapeDrawers = new Map();
586
- updaters = new Map();
587
722
  #allLoadersSet = new Set();
588
723
  #configs = new Map();
589
724
  #engine;
@@ -601,57 +736,79 @@
601
736
  }
602
737
  return res;
603
738
  }
739
+ get initialized() {
740
+ return this.#initialized;
741
+ }
604
742
  addColorManager(name, manager) {
743
+ if (this.#initialized) {
744
+ return;
745
+ }
605
746
  this.colorManagers.set(name, manager);
606
747
  }
607
748
  addConfig(config) {
749
+ if (this.#initialized) {
750
+ return;
751
+ }
608
752
  const key = config.key ?? config.name ?? "default";
609
753
  this.#configs.set(key, config);
610
754
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
611
755
  }
612
756
  addEasing(name, easing) {
757
+ if (this.#initialized) {
758
+ return;
759
+ }
613
760
  if (this.easingFunctions.get(name)) {
614
761
  return;
615
762
  }
616
763
  this.easingFunctions.set(name, easing);
617
764
  }
618
765
  addEffect(effect, drawer) {
766
+ if (this.#initialized) {
767
+ return;
768
+ }
619
769
  this.initializers.effects.set(effect, drawer);
620
770
  }
621
771
  addPalette(name, palette) {
772
+ if (this.#initialized) {
773
+ return;
774
+ }
622
775
  this.palettes.set(name, palette);
623
776
  }
624
777
  addParticleUpdater(name, updaterInitializer) {
778
+ if (this.#initialized) {
779
+ return;
780
+ }
625
781
  this.initializers.updaters.set(name, updaterInitializer);
626
782
  }
627
783
  addPlugin(plugin) {
784
+ if (this.#initialized) {
785
+ return;
786
+ }
628
787
  if (this.getPlugin(plugin.id)) {
629
788
  return;
630
789
  }
631
790
  this.plugins.push(plugin);
632
791
  }
633
792
  addPreset(preset, options, override = false) {
793
+ if (this.#initialized) {
794
+ return;
795
+ }
634
796
  if (!(override || !this.getPreset(preset))) {
635
797
  return;
636
798
  }
637
799
  this.presets.set(preset, options);
638
800
  }
639
801
  addShape(shapes, drawer) {
802
+ if (this.#initialized) {
803
+ return;
804
+ }
640
805
  for (const shape of shapes) {
641
806
  this.initializers.shapes.set(shape, drawer);
642
807
  }
643
808
  }
644
- clearPlugins(container) {
645
- this.effectDrawers.delete(container);
646
- this.shapeDrawers.delete(container);
647
- this.updaters.delete(container);
648
- }
649
809
  getEasing(name) {
650
810
  return this.easingFunctions.get(name) ?? ((value) => value);
651
811
  }
652
- getEffectDrawers(container, force = false) {
653
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
654
- }
655
812
  getPalette(name) {
656
813
  return this.palettes.get(name);
657
814
  }
@@ -661,12 +818,6 @@
661
818
  getPreset(preset) {
662
819
  return this.presets.get(preset);
663
820
  }
664
- async getShapeDrawers(container, force = false) {
665
- return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
666
- }
667
- async getUpdaters(container, force = false) {
668
- return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
669
- }
670
821
  async init() {
671
822
  if (this.#initialized || this.#isRunningLoaders) {
672
823
  return;
@@ -685,16 +836,9 @@
685
836
  this.#initialized = true;
686
837
  }
687
838
  }
688
- loadParticlesOptions(container, options, ...sourceOptions) {
689
- const updaters = this.updaters.get(container);
690
- if (!updaters) {
691
- return;
692
- }
693
- updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
694
- }
695
839
  async register(...loaders) {
696
840
  if (this.#initialized) {
697
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
841
+ throw new Error(getErrorMessage(ErrorCodes$1.pluginRegisterAfterLoad));
698
842
  }
699
843
  for (const loader of loaders) {
700
844
  if (this.#isRunningLoaders) {
@@ -706,88 +850,15 @@
706
850
  }
707
851
  }
708
852
  async #runLoader(loader, executed, allLoaders) {
709
- if (executed.has(loader))
853
+ if (executed.has(loader)) {
710
854
  return;
855
+ }
711
856
  executed.add(loader);
712
857
  allLoaders.add(loader);
713
858
  await loader(this.#engine);
714
859
  }
715
860
  }
716
861
 
717
- const errorPrefix = "tsParticles - Error";
718
- const wrap = (fn) => (...args) => {
719
- fn(...args);
720
- }, _logger = {
721
- debug: wrap(console.debug),
722
- error: (message, ...optionalParams) => {
723
- console.error(`${errorPrefix} - ${message}`, ...optionalParams);
724
- },
725
- info: wrap(console.info),
726
- log: wrap(console.log),
727
- trace: wrap(console.trace),
728
- verbose: wrap(console.log),
729
- warning: wrap(console.warn),
730
- };
731
- function getLogger() {
732
- return _logger;
733
- }
734
-
735
- const fullPercent = "100%";
736
- async function getDataFromUrl(data) {
737
- const url = itemFromSingleOrMultiple(data.url, data.index);
738
- if (!url) {
739
- return data.fallback;
740
- }
741
- const response = await fetch(url);
742
- if (response.ok) {
743
- return (await response.json());
744
- }
745
- getLogger().error(`${response.status.toString()} while retrieving config file`);
746
- return data.fallback;
747
- }
748
- const getCanvasFromContainer = (domContainer) => {
749
- const documentSafe = safeDocument();
750
- let canvasEl;
751
- const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
752
- if (isCanvas) {
753
- canvasEl = domContainer;
754
- canvasEl.dataset[generatedAttribute] ??= generatedFalse;
755
- if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
756
- canvasEl.style.width ||= fullPercent;
757
- canvasEl.style.height ||= fullPercent;
758
- canvasEl.style.pointerEvents = "none";
759
- canvasEl.style.setProperty("pointer-events", "none");
760
- }
761
- }
762
- else {
763
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
764
- if (foundCanvas) {
765
- canvasEl = foundCanvas;
766
- canvasEl.dataset[generatedAttribute] = generatedFalse;
767
- }
768
- else {
769
- canvasEl = documentSafe.createElement(canvasTag);
770
- canvasEl.dataset[generatedAttribute] = generatedTrue;
771
- domContainer.appendChild(canvasEl);
772
- }
773
- canvasEl.style.width ||= fullPercent;
774
- canvasEl.style.height ||= fullPercent;
775
- canvasEl.style.pointerEvents = "none";
776
- canvasEl.style.setProperty("pointer-events", "none");
777
- }
778
- return canvasEl;
779
- }, getDomContainer = (id, source) => {
780
- const documentSafe = safeDocument();
781
- let domContainer = source ?? documentSafe.getElementById(id);
782
- if (domContainer) {
783
- return domContainer;
784
- }
785
- domContainer = documentSafe.createElement("canvas");
786
- domContainer.id = id;
787
- domContainer.dataset[generatedAttribute] = generatedTrue;
788
- documentSafe.body.append(domContainer);
789
- return domContainer;
790
- };
791
862
  class Engine {
792
863
  pluginManager = new PluginManager(this);
793
864
  #domArray = [];
@@ -797,7 +868,7 @@
797
868
  return this.#domArray;
798
869
  }
799
870
  get version() {
800
- return "4.4.0";
871
+ return "4.5.1";
801
872
  }
802
873
  addEventListener(type, listener) {
803
874
  this.#eventDispatcher.addEventListener(type, listener);
@@ -806,7 +877,7 @@
806
877
  if (this.version === pluginVersion) {
807
878
  return;
808
879
  }
809
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
880
+ throw new Error(getErrorMessage(ErrorCodes$1.engineVersionMismatch, this.version, pluginVersion));
810
881
  }
811
882
  dispatchEvent(type, args) {
812
883
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1055,20 +1126,6 @@
1055
1126
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1056
1127
  })(DrawLayer || (DrawLayer = {}));
1057
1128
 
1058
- class OptionLoader {
1059
- load(data) {
1060
- if (isNull(data)) {
1061
- return;
1062
- }
1063
- this.doLoad(data);
1064
- }
1065
- }
1066
- function loadOptions(options, ...sourceOptionsArr) {
1067
- for (const sourceOptions of sourceOptionsArr) {
1068
- options.load(sourceOptions);
1069
- }
1070
- }
1071
-
1072
1129
  function loadProperty(obj, key, value) {
1073
1130
  if (value !== undefined) {
1074
1131
  obj[key] = value;
@@ -1100,6 +1157,20 @@
1100
1157
  }
1101
1158
  }
1102
1159
 
1160
+ class OptionLoader {
1161
+ load(data) {
1162
+ if (isNull(data)) {
1163
+ return;
1164
+ }
1165
+ this.doLoad(data);
1166
+ }
1167
+ }
1168
+ function loadOptions(options, ...sourceOptionsArr) {
1169
+ for (const sourceOptions of sourceOptionsArr) {
1170
+ options.load(sourceOptions);
1171
+ }
1172
+ }
1173
+
1103
1174
  class AnimationOptions extends OptionLoader {
1104
1175
  count = 0;
1105
1176
  decay = 0;
@@ -1250,15 +1321,14 @@
1250
1321
  peakNits = defaultPeakNits;
1251
1322
  doLoad(data) {
1252
1323
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1253
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1324
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrEnableInvalid, String(data.enable)));
1254
1325
  }
1255
- if (data.mode !== undefined &&
1256
- (typeof data.mode !== "string" || !Object.values(HdrMode).includes(data.mode))) {
1257
- throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
1326
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1327
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1258
1328
  }
1259
1329
  if (data.peakNits !== undefined &&
1260
1330
  (!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
1261
- throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
1331
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrPeakNitsInvalid, String(data.peakNits)));
1262
1332
  }
1263
1333
  loadProperty(this, "enable", data.enable);
1264
1334
  loadProperty(this, "mode", data.mode);
@@ -1636,12 +1706,10 @@
1636
1706
  plugin.loadParticlesOptions(this.#container, this, data);
1637
1707
  }
1638
1708
  }
1639
- const updaters = this.#pluginManager.updaters.get(this.#container);
1640
- if (updaters) {
1641
- for (const updater of updaters) {
1642
- if (updater.loadOptions) {
1643
- updater.loadOptions(this, data);
1644
- }
1709
+ const updaters = this.#container.particleUpdaters;
1710
+ for (const updater of updaters) {
1711
+ if (updater.loadOptions) {
1712
+ updater.loadOptions(this, data);
1645
1713
  }
1646
1714
  }
1647
1715
  }
@@ -1974,9 +2042,9 @@
1974
2042
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1975
2043
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1976
2044
  }
1977
- const temp1 = lNormalized < half
2045
+ const temp2 = lNormalized < half
1978
2046
  ? lNormalized * (sNormalizedOffset + sNormalized)
1979
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2047
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1980
2048
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1981
2049
  }
1982
2050
  function hslToRgbFloat(hsl) {
@@ -1985,9 +2053,9 @@
1985
2053
  const grayscaleValue = lNormalized * rgbMax;
1986
2054
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1987
2055
  }
1988
- const temp1 = lNormalized < half
2056
+ const temp2 = lNormalized < half
1989
2057
  ? lNormalized * (sNormalizedOffset + sNormalized)
1990
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2058
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1991
2059
  return { r: red, g: green, b: blue };
1992
2060
  }
1993
2061
  function hslaToRgba(hsla) {
@@ -2201,7 +2269,7 @@
2201
2269
  }
2202
2270
 
2203
2271
  async function loadBlendPlugin(engine) {
2204
- engine.checkVersion("4.4.0");
2272
+ engine.checkVersion("4.5.1");
2205
2273
  await engine.pluginManager.register(e => {
2206
2274
  e.pluginManager.addPlugin(new BlendPlugin());
2207
2275
  });
@@ -2238,7 +2306,7 @@
2238
2306
  }
2239
2307
 
2240
2308
  async function loadCircleShape(engine) {
2241
- engine.checkVersion("4.4.0");
2309
+ engine.checkVersion("4.5.1");
2242
2310
  await engine.pluginManager.register(e => {
2243
2311
  e.pluginManager.addShape(["circle"], () => {
2244
2312
  return Promise.resolve(new CircleDrawer());
@@ -2253,7 +2321,7 @@
2253
2321
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2254
2322
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2255
2323
  })(RgbIndexes$1 || (RgbIndexes$1 = {}));
2256
- const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
2324
+ const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, alphaFactor = 0xff;
2257
2325
  class HexColorManager {
2258
2326
  accepts(input) {
2259
2327
  return input.startsWith("#");
@@ -2286,7 +2354,7 @@
2286
2354
  }
2287
2355
 
2288
2356
  async function loadHexColorPlugin(engine) {
2289
- engine.checkVersion("4.4.0");
2357
+ engine.checkVersion("4.5.1");
2290
2358
  await engine.pluginManager.register(e => {
2291
2359
  e.pluginManager.addColorManager("hex", new HexColorManager());
2292
2360
  });
@@ -2326,7 +2394,7 @@
2326
2394
  if (!this.accepts(input)) {
2327
2395
  return;
2328
2396
  }
2329
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2397
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2330
2398
  return result
2331
2399
  ? hslaToRgba({
2332
2400
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2339,12 +2407,20 @@
2339
2407
  }
2340
2408
 
2341
2409
  async function loadHslColorPlugin(engine) {
2342
- engine.checkVersion("4.4.0");
2410
+ engine.checkVersion("4.5.1");
2343
2411
  await engine.pluginManager.register(e => {
2344
2412
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2345
2413
  });
2346
2414
  }
2347
2415
 
2416
+ const ErrorCodes = {
2417
+ baseMoverNotLoaded: "TSP-3203",
2418
+ };
2419
+
2420
+ const ErrorMessages = {
2421
+ [ErrorCodes.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2422
+ };
2423
+
2348
2424
  class MovePlugin {
2349
2425
  id = "move";
2350
2426
  #pluginManager;
@@ -2363,20 +2439,20 @@
2363
2439
  }
2364
2440
 
2365
2441
  async function loadMovePlugin(engine) {
2366
- engine.checkVersion("4.4.0");
2442
+ engine.checkVersion("4.5.1");
2443
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2444
+ addErrorMessages(ErrorMessages);
2445
+ }
2367
2446
  await engine.pluginManager.register(e => {
2368
2447
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2369
2448
  movePluginManager.initializers.pathGenerators ??= new Map();
2370
- movePluginManager.pathGenerators ??= new Map();
2371
2449
  movePluginManager.addPathGenerator = (name, generator) => {
2450
+ if (movePluginManager.initialized) {
2451
+ return;
2452
+ }
2372
2453
  movePluginManager.initializers.pathGenerators ??= new Map();
2373
2454
  movePluginManager.initializers.pathGenerators.set(name, generator);
2374
2455
  };
2375
- movePluginManager.getPathGenerators = async (container, force = false) => {
2376
- movePluginManager.initializers.pathGenerators ??= new Map();
2377
- movePluginManager.pathGenerators ??= new Map();
2378
- return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
2379
- };
2380
2456
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2381
2457
  });
2382
2458
  }
@@ -2579,7 +2655,7 @@
2579
2655
  }
2580
2656
 
2581
2657
  async function loadOpacityUpdater(engine) {
2582
- engine.checkVersion("4.4.0");
2658
+ engine.checkVersion("4.5.1");
2583
2659
  await engine.pluginManager.register(e => {
2584
2660
  e.pluginManager.addParticleUpdater("opacity", container => {
2585
2661
  return Promise.resolve(new OpacityUpdater(container));
@@ -2588,70 +2664,60 @@
2588
2664
  }
2589
2665
 
2590
2666
  const boundsMin = 0;
2591
- function bounceHorizontal(data) {
2592
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2593
- (data.direction !== OutModeDirection.left && data.direction !== OutModeDirection.right)) {
2594
- return;
2595
- }
2596
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2597
- data.particle.position.x = data.size + data.offset.x;
2598
- }
2599
- else if (data.bounds.left > data.canvasSize.width && data.direction === OutModeDirection.right) {
2600
- data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
2601
- }
2602
- const velocity = data.particle.velocity.x;
2603
- let bounced = false;
2604
- if (data.outOfCanvas &&
2605
- ((data.direction === OutModeDirection.right && velocity > minVelocity) ||
2606
- (data.direction === OutModeDirection.left && velocity < minVelocity))) {
2607
- const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
2608
- data.particle.velocity.x *= -newVelocity;
2609
- bounced = true;
2610
- }
2611
- if (!bounced) {
2612
- return;
2613
- }
2614
- const minPos = data.offset.x + data.size;
2615
- if (data.outOfCanvas && data.direction === OutModeDirection.right) {
2616
- data.particle.position.x = data.canvasSize.width - minPos;
2617
- }
2618
- else if (data.outOfCanvas && data.direction === OutModeDirection.left) {
2619
- data.particle.position.x = minPos;
2620
- }
2621
- if (data.outMode === OutMode.split) {
2622
- data.particle.destroy();
2623
- }
2624
- }
2625
- function bounceVertical(data) {
2626
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2627
- (data.direction !== OutModeDirection.bottom && data.direction !== OutModeDirection.top)) {
2667
+ const bounceAxisConfigs = {
2668
+ [OutModeDirection.left]: {
2669
+ axis: "x",
2670
+ canvas: "width",
2671
+ bounce: "horizontal",
2672
+ negativeEdge: "right",
2673
+ positiveEdge: "left",
2674
+ negative: true,
2675
+ },
2676
+ [OutModeDirection.right]: {
2677
+ axis: "x",
2678
+ canvas: "width",
2679
+ bounce: "horizontal",
2680
+ negativeEdge: "right",
2681
+ positiveEdge: "left",
2682
+ negative: false,
2683
+ },
2684
+ [OutModeDirection.top]: {
2685
+ axis: "y",
2686
+ canvas: "height",
2687
+ bounce: "vertical",
2688
+ negativeEdge: "bottom",
2689
+ positiveEdge: "top",
2690
+ negative: true,
2691
+ },
2692
+ [OutModeDirection.bottom]: {
2693
+ axis: "y",
2694
+ canvas: "height",
2695
+ bounce: "vertical",
2696
+ negativeEdge: "bottom",
2697
+ positiveEdge: "top",
2698
+ negative: false,
2699
+ },
2700
+ };
2701
+ function bounce(data) {
2702
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2628
2703
  return;
2629
2704
  }
2630
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2631
- data.particle.position.y = data.size + data.offset.y;
2705
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2706
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2707
+ position[config.axis] = data.size + data.offset[config.axis];
2632
2708
  }
2633
- else if (data.bounds.top > data.canvasSize.height && data.direction === OutModeDirection.bottom) {
2634
- data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
2709
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2710
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2635
2711
  }
2636
- const velocity = data.particle.velocity.y;
2637
- let bounced = false;
2638
2712
  if (data.outOfCanvas &&
2639
- ((data.direction === OutModeDirection.bottom && velocity > minVelocity) ||
2640
- (data.direction === OutModeDirection.top && velocity < minVelocity))) {
2641
- const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
2642
- data.particle.velocity.y *= -newVelocity;
2643
- bounced = true;
2713
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2714
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2644
2715
  }
2645
- if (!bounced) {
2716
+ else {
2646
2717
  return;
2647
2718
  }
2648
- const minPos = data.offset.y + data.size;
2649
- if (data.outOfCanvas && data.direction === OutModeDirection.bottom) {
2650
- data.particle.position.y = data.canvasSize.height - minPos;
2651
- }
2652
- else if (data.outOfCanvas && data.direction === OutModeDirection.top) {
2653
- data.particle.position.y = minPos;
2654
- }
2719
+ const minPos = data.offset[config.axis] + data.size;
2720
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2655
2721
  if (data.outMode === OutMode.split) {
2656
2722
  data.particle.destroy();
2657
2723
  }
@@ -2667,26 +2733,19 @@
2667
2733
  OutMode.bounce,
2668
2734
  OutMode.split,
2669
2735
  ];
2670
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2736
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2671
2737
  }
2672
2738
  update(particle, direction, delta, outMode) {
2673
2739
  if (!this.modes.includes(outMode)) {
2674
2740
  return;
2675
2741
  }
2676
- const container = this.#container;
2677
- let handled = false;
2678
2742
  for (const plugin of this.#particleBouncePlugins) {
2679
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2680
- if (handled) {
2681
- break;
2743
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2744
+ return;
2682
2745
  }
2683
2746
  }
2684
- if (handled) {
2685
- return;
2686
- }
2687
- const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2688
- bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2689
- bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2747
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2748
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2690
2749
  }
2691
2750
  }
2692
2751
 
@@ -2775,107 +2834,108 @@
2775
2834
  return;
2776
2835
  }
2777
2836
  const container = this.#container;
2778
- switch (particle.outType) {
2779
- case ParticleOutType.inside: {
2780
- const { x: vx, y: vy } = particle.velocity;
2781
- updateVector.setTo(originPoint);
2782
- updateVector.length = particle.moveCenter.radius;
2783
- updateVector.angle = particle.velocity.angle + Math.PI;
2784
- updateVector.addTo(particle.moveCenter);
2785
- const { dx, dy } = getDistances(particle.position, updateVector);
2786
- if ((vx <= minVelocity && dx >= minDistance) ||
2787
- (vy <= minVelocity && dy >= minDistance) ||
2788
- (vx >= minVelocity && dx <= minDistance) ||
2789
- (vy >= minVelocity && dy <= minDistance)) {
2790
- return;
2791
- }
2792
- particle.position.x = Math.floor(randomInRangeValue({
2793
- min: 0,
2794
- max: container.canvas.size.width,
2795
- }));
2796
- particle.position.y = Math.floor(randomInRangeValue({
2797
- min: 0,
2798
- max: container.canvas.size.height,
2799
- }));
2800
- const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2801
- particle.direction = Math.atan2(-newDy, -newDx);
2802
- particle.velocity.angle = particle.direction;
2803
- particle.justWarped = true;
2804
- break;
2837
+ if (particle.outType === ParticleOutType.inside) {
2838
+ this.#applyOutModeInside(particle, container);
2839
+ }
2840
+ else {
2841
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2842
+ return;
2805
2843
  }
2806
- default: {
2807
- if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2808
- return;
2809
- }
2810
- switch (particle.outType) {
2811
- case ParticleOutType.outside: {
2812
- particle.position.x =
2813
- Math.floor(randomInRangeValue({
2814
- min: -particle.moveCenter.radius,
2815
- max: particle.moveCenter.radius,
2816
- })) + particle.moveCenter.x;
2817
- particle.position.y =
2818
- Math.floor(randomInRangeValue({
2819
- min: -particle.moveCenter.radius,
2820
- max: particle.moveCenter.radius,
2821
- })) + particle.moveCenter.y;
2822
- const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2823
- if (particle.moveCenter.radius) {
2824
- particle.direction = Math.atan2(dy, dx);
2825
- particle.velocity.angle = particle.direction;
2826
- }
2827
- particle.justWarped = true;
2828
- break;
2829
- }
2830
- case ParticleOutType.normal: {
2831
- const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
2832
- bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
2833
- left: -particle.getRadius() - particle.offset.x,
2834
- right: canvasSize.width + particle.getRadius() + particle.offset.x,
2835
- top: -particle.getRadius() - particle.offset.y,
2836
- }, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
2837
- if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2838
- particle.position.x = newPos.left;
2839
- particle.initialPosition.x = particle.position.x;
2840
- if (!warp) {
2841
- particle.position.y = getRandom() * canvasSize.height;
2842
- particle.initialPosition.y = particle.position.y;
2843
- }
2844
- particle.justWarped = true;
2845
- }
2846
- else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2847
- particle.position.x = newPos.right;
2848
- particle.initialPosition.x = particle.position.x;
2849
- if (!warp) {
2850
- particle.position.y = getRandom() * canvasSize.height;
2851
- particle.initialPosition.y = particle.position.y;
2852
- }
2853
- particle.justWarped = true;
2854
- }
2855
- if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2856
- if (!warp) {
2857
- particle.position.x = getRandom() * canvasSize.width;
2858
- particle.initialPosition.x = particle.position.x;
2859
- }
2860
- particle.position.y = newPos.top;
2861
- particle.initialPosition.y = particle.position.y;
2862
- particle.justWarped = true;
2863
- }
2864
- else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2865
- if (!warp) {
2866
- particle.position.x = getRandom() * canvasSize.width;
2867
- particle.initialPosition.x = particle.position.x;
2868
- }
2869
- particle.position.y = newPos.bottom;
2870
- particle.initialPosition.y = particle.position.y;
2871
- particle.justWarped = true;
2872
- }
2873
- break;
2874
- }
2875
- }
2876
- break;
2844
+ if (particle.outType === ParticleOutType.outside) {
2845
+ this.#applyParticleModeOutside(particle);
2877
2846
  }
2847
+ else {
2848
+ this.#applyOutModeNormal(particle, container, direction);
2849
+ }
2850
+ }
2851
+ }
2852
+ #applyOutModeInside(particle, container) {
2853
+ const { x: vx, y: vy } = particle.velocity;
2854
+ updateVector.setTo(originPoint);
2855
+ updateVector.length = particle.moveCenter.radius;
2856
+ updateVector.angle = particle.velocity.angle + Math.PI;
2857
+ updateVector.addTo(particle.moveCenter);
2858
+ const { dx, dy } = getDistances(particle.position, updateVector);
2859
+ if ((vx <= minVelocity && dx >= minDistance) ||
2860
+ (vy <= minVelocity && dy >= minDistance) ||
2861
+ (vx >= minVelocity && dx <= minDistance) ||
2862
+ (vy >= minVelocity && dy <= minDistance)) {
2863
+ return;
2878
2864
  }
2865
+ particle.position.x = Math.floor(randomInRangeValue({
2866
+ min: 0,
2867
+ max: container.canvas.size.width,
2868
+ }));
2869
+ particle.position.y = Math.floor(randomInRangeValue({
2870
+ min: 0,
2871
+ max: container.canvas.size.height,
2872
+ }));
2873
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2874
+ particle.direction = Math.atan2(-newDy, -newDx);
2875
+ particle.velocity.angle = particle.direction;
2876
+ particle.justWarped = true;
2877
+ }
2878
+ #applyOutModeNormal(particle, container, direction) {
2879
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2880
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2881
+ left: -sizeValue - particle.offset.x,
2882
+ right: canvasSize.width + sizeValue + particle.offset.x,
2883
+ top: -sizeValue - particle.offset.y,
2884
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2885
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2886
+ particle.position.x = newPos.left;
2887
+ particle.initialPosition.x = particle.position.x;
2888
+ if (!warp) {
2889
+ particle.position.y = getRandom() * canvasSize.height;
2890
+ particle.initialPosition.y = particle.position.y;
2891
+ }
2892
+ particle.justWarped = true;
2893
+ }
2894
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2895
+ particle.position.x = newPos.right;
2896
+ particle.initialPosition.x = particle.position.x;
2897
+ if (!warp) {
2898
+ particle.position.y = getRandom() * canvasSize.height;
2899
+ particle.initialPosition.y = particle.position.y;
2900
+ }
2901
+ particle.justWarped = true;
2902
+ }
2903
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2904
+ if (!warp) {
2905
+ particle.position.x = getRandom() * canvasSize.width;
2906
+ particle.initialPosition.x = particle.position.x;
2907
+ }
2908
+ particle.position.y = newPos.top;
2909
+ particle.initialPosition.y = particle.position.y;
2910
+ particle.justWarped = true;
2911
+ }
2912
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2913
+ if (!warp) {
2914
+ particle.position.x = getRandom() * canvasSize.width;
2915
+ particle.initialPosition.x = particle.position.x;
2916
+ }
2917
+ particle.position.y = newPos.bottom;
2918
+ particle.initialPosition.y = particle.position.y;
2919
+ particle.justWarped = true;
2920
+ }
2921
+ }
2922
+ #applyParticleModeOutside(particle) {
2923
+ particle.position.x =
2924
+ Math.floor(randomInRangeValue({
2925
+ min: -particle.moveCenter.radius,
2926
+ max: particle.moveCenter.radius,
2927
+ })) + particle.moveCenter.x;
2928
+ particle.position.y =
2929
+ Math.floor(randomInRangeValue({
2930
+ min: -particle.moveCenter.radius,
2931
+ max: particle.moveCenter.radius,
2932
+ })) + particle.moveCenter.y;
2933
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2934
+ if (particle.moveCenter.radius) {
2935
+ particle.direction = Math.atan2(dy, dx);
2936
+ particle.velocity.angle = particle.direction;
2937
+ }
2938
+ particle.justWarped = true;
2879
2939
  }
2880
2940
  }
2881
2941
 
@@ -2924,7 +2984,7 @@
2924
2984
  }
2925
2985
 
2926
2986
  async function loadOutModesUpdater(engine) {
2927
- engine.checkVersion("4.4.0");
2987
+ engine.checkVersion("4.5.1");
2928
2988
  await engine.pluginManager.register(e => {
2929
2989
  e.pluginManager.addParticleUpdater("outModes", container => {
2930
2990
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2941,39 +3001,9 @@
2941
3001
  this.#pluginManager = pluginManager;
2942
3002
  }
2943
3003
  init(particle) {
2944
- const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates), color = paint?.color, paintColor = color ?? undefined, fill = paint?.fill, stroke = paint?.stroke;
2945
- if (fill) {
2946
- const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
2947
- particle.fillEnabled = fill.enable;
2948
- particle.fillOpacity = getRangeValue(fill.opacity);
2949
- particle.fillAnimation = fillColor.animation;
2950
- const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
2951
- if (fillHslColor) {
2952
- particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
2953
- }
2954
- }
2955
- else {
2956
- particle.fillEnabled = false;
2957
- particle.fillAnimation = undefined;
2958
- particle.fillColor = undefined;
2959
- particle.fillOpacity = defaultOpacity;
2960
- }
2961
- if (stroke) {
2962
- const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
2963
- particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
2964
- particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
2965
- particle.strokeAnimation = strokeColor.animation;
2966
- const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
2967
- if (strokeHslColor) {
2968
- particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
2969
- }
2970
- }
2971
- else {
2972
- particle.strokeAnimation = undefined;
2973
- particle.strokeColor = undefined;
2974
- particle.strokeOpacity = defaultOpacity;
2975
- particle.strokeWidth = 0;
2976
- }
3004
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3005
+ this.#initFill(paint, particle, container);
3006
+ this.#initStroke(paint, particle, container);
2977
3007
  }
2978
3008
  isEnabled(particle) {
2979
3009
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2992,10 +3022,48 @@
2992
3022
  updateColor(particle.fillColor, delta, this.#container.hdr);
2993
3023
  updateColor(particle.strokeColor, delta, this.#container.hdr);
2994
3024
  }
3025
+ #initFill(paint, particle, container) {
3026
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3027
+ if (!fill) {
3028
+ particle.fillEnabled = false;
3029
+ particle.fillAnimation = undefined;
3030
+ particle.fillColor = undefined;
3031
+ particle.fillOpacity = defaultOpacity;
3032
+ return;
3033
+ }
3034
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3035
+ particle.fillEnabled = fill.enable;
3036
+ particle.fillOpacity = getRangeValue(fill.opacity);
3037
+ particle.fillAnimation = fillColor.animation;
3038
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3039
+ if (!fillHslColor) {
3040
+ return;
3041
+ }
3042
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3043
+ }
3044
+ #initStroke(paint, particle, container) {
3045
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3046
+ if (!stroke) {
3047
+ particle.strokeAnimation = undefined;
3048
+ particle.strokeColor = undefined;
3049
+ particle.strokeOpacity = defaultOpacity;
3050
+ particle.strokeWidth = 0;
3051
+ return;
3052
+ }
3053
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3054
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3055
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3056
+ particle.strokeAnimation = strokeColor.animation;
3057
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3058
+ if (!strokeHslColor) {
3059
+ return;
3060
+ }
3061
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3062
+ }
2995
3063
  }
2996
3064
 
2997
3065
  async function loadPaintUpdater(engine) {
2998
- engine.checkVersion("4.4.0");
3066
+ engine.checkVersion("4.5.1");
2999
3067
  await engine.pluginManager.register(e => {
3000
3068
  e.pluginManager.addParticleUpdater("paint", container => {
3001
3069
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3037,7 +3105,7 @@
3037
3105
  if (!this.accepts(input)) {
3038
3106
  return;
3039
3107
  }
3040
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3108
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3041
3109
  return result
3042
3110
  ? {
3043
3111
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3050,7 +3118,7 @@
3050
3118
  }
3051
3119
 
3052
3120
  async function loadRgbColorPlugin(engine) {
3053
- engine.checkVersion("4.4.0");
3121
+ engine.checkVersion("4.5.1");
3054
3122
  await engine.pluginManager.register(e => {
3055
3123
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3056
3124
  });
@@ -3135,7 +3203,7 @@
3135
3203
  }
3136
3204
 
3137
3205
  async function loadSizeUpdater(engine) {
3138
- engine.checkVersion("4.4.0");
3206
+ engine.checkVersion("4.5.1");
3139
3207
  await engine.pluginManager.register(e => {
3140
3208
  e.pluginManager.addParticleUpdater("size", container => {
3141
3209
  return Promise.resolve(new SizeUpdater(container));
@@ -3144,7 +3212,7 @@
3144
3212
  }
3145
3213
 
3146
3214
  async function loadBasic(engine) {
3147
- engine.checkVersion("4.4.0");
3215
+ engine.checkVersion("4.5.1");
3148
3216
  await engine.pluginManager.register(async (e) => {
3149
3217
  await Promise.all([
3150
3218
  loadBlendPlugin(e),
@@ -3237,7 +3305,7 @@
3237
3305
  }
3238
3306
 
3239
3307
  async function loadMotionPlugin(engine) {
3240
- engine.checkVersion("4.4.0");
3308
+ engine.checkVersion("4.5.1");
3241
3309
  await engine.pluginManager.register(e => {
3242
3310
  e.pluginManager.addPlugin(new MotionPlugin());
3243
3311
  });
@@ -3370,7 +3438,7 @@
3370
3438
  }
3371
3439
 
3372
3440
  async function loadRollUpdater(engine) {
3373
- engine.checkVersion("4.4.0");
3441
+ engine.checkVersion("4.5.1");
3374
3442
  await engine.pluginManager.register(e => {
3375
3443
  e.pluginManager.addParticleUpdater("roll", () => {
3376
3444
  return Promise.resolve(new RollUpdater(e.pluginManager));
@@ -3429,7 +3497,7 @@
3429
3497
  particle.pathRotation = rotateOptions.path;
3430
3498
  let rotateDirection = rotateOptions.direction;
3431
3499
  if (rotateDirection === RotateDirection.random) {
3432
- const index = Math.floor(getRandom() * double), minIndex = 0;
3500
+ const index = Math.floor(getRandom() * double);
3433
3501
  rotateDirection = index > minIndex ? RotateDirection.counterClockwise : RotateDirection.clockwise;
3434
3502
  }
3435
3503
  switch (rotateDirection) {
@@ -3476,7 +3544,7 @@
3476
3544
  }
3477
3545
 
3478
3546
  async function loadRotateUpdater(engine) {
3479
- engine.checkVersion("4.4.0");
3547
+ engine.checkVersion("4.5.1");
3480
3548
  await engine.pluginManager.register(e => {
3481
3549
  e.pluginManager.addParticleUpdater("rotate", container => {
3482
3550
  return Promise.resolve(new RotateUpdater(container));
@@ -3500,7 +3568,7 @@
3500
3568
  }
3501
3569
 
3502
3570
  async function loadSquareShape(engine) {
3503
- engine.checkVersion("4.4.0");
3571
+ engine.checkVersion("4.5.1");
3504
3572
  await engine.pluginManager.register(e => {
3505
3573
  e.pluginManager.addShape(["edge", "square"], () => Promise.resolve(new SquareDrawer()));
3506
3574
  });
@@ -3572,7 +3640,7 @@
3572
3640
  };
3573
3641
  let tiltDirection = tiltOptions.direction;
3574
3642
  if (tiltDirection === TiltDirection.random) {
3575
- const index = Math.floor(getRandom() * double), minIndex = 0;
3643
+ const index = Math.floor(getRandom() * double);
3576
3644
  tiltDirection = index > minIndex ? TiltDirection.counterClockwise : TiltDirection.clockwise;
3577
3645
  }
3578
3646
  switch (tiltDirection) {
@@ -3609,7 +3677,7 @@
3609
3677
  }
3610
3678
 
3611
3679
  async function loadTiltUpdater(engine) {
3612
- engine.checkVersion("4.4.0");
3680
+ engine.checkVersion("4.5.1");
3613
3681
  await engine.pluginManager.register(e => {
3614
3682
  e.pluginManager.addParticleUpdater("tilt", container => {
3615
3683
  return Promise.resolve(new TiltUpdater(container));
@@ -3711,7 +3779,7 @@
3711
3779
  }
3712
3780
 
3713
3781
  async function loadWobbleUpdater(engine) {
3714
- engine.checkVersion("4.4.0");
3782
+ engine.checkVersion("4.5.1");
3715
3783
  await engine.pluginManager.register(e => {
3716
3784
  e.pluginManager.addParticleUpdater("wobble", container => {
3717
3785
  return Promise.resolve(new WobbleUpdater(container));
@@ -3893,21 +3961,28 @@
3893
3961
  });
3894
3962
  }
3895
3963
  clear() {
3964
+ const visited = new Set();
3896
3965
  for (const plugin of this.#canvasClearPlugins) {
3966
+ visited.add(plugin);
3897
3967
  if (plugin.canvasClear?.() ?? false) {
3898
- return;
3968
+ return plugin;
3899
3969
  }
3900
3970
  }
3901
3971
  for (const layer of Object.values(DrawLayer)) {
3902
3972
  if (typeof layer === "number") {
3903
3973
  for (const plugin of this.#getLayerPlugins(layer)) {
3974
+ if (visited.has(plugin)) {
3975
+ continue;
3976
+ }
3977
+ visited.add(plugin);
3904
3978
  if (plugin.canvasClear?.() ?? false) {
3905
- return;
3979
+ return plugin;
3906
3980
  }
3907
3981
  }
3908
3982
  }
3909
3983
  }
3910
3984
  this.canvasClear();
3985
+ return undefined;
3911
3986
  }
3912
3987
  destroy() {
3913
3988
  this.stop();
@@ -3985,8 +4060,7 @@
3985
4060
  });
3986
4061
  }
3987
4062
  drawParticles(delta) {
3988
- const { particles, actualOptions } = this.#container;
3989
- this.clear();
4063
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3990
4064
  particles.update(delta);
3991
4065
  this.draw(ctx => {
3992
4066
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -4008,7 +4082,9 @@
4008
4082
  }
4009
4083
  }
4010
4084
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
4011
- plugin.canvasPaint?.();
4085
+ if (plugin !== clearedPlugin) {
4086
+ plugin.canvasPaint?.();
4087
+ }
4012
4088
  }
4013
4089
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
4014
4090
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -4040,35 +4116,7 @@
4040
4116
  this.#layers[layer] = [];
4041
4117
  }
4042
4118
  }
4043
- for (const plugin of this.#container.plugins) {
4044
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
4045
- this.#colorPlugins.push(plugin);
4046
- }
4047
- if (plugin.drawParticle) {
4048
- this.#drawParticlePlugins.push(plugin);
4049
- }
4050
- if (plugin.drawParticleSetup) {
4051
- this.#drawParticlesSetupPlugins.push(plugin);
4052
- }
4053
- if (plugin.drawParticleCleanup) {
4054
- this.#drawParticlesCleanupPlugins.push(plugin);
4055
- }
4056
- if (plugin.canvasClear) {
4057
- this.#canvasClearPlugins.push(plugin);
4058
- }
4059
- if (plugin.canvasPaint) {
4060
- this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
4061
- }
4062
- if (plugin.drawSettingsSetup) {
4063
- this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
4064
- }
4065
- if (plugin.draw) {
4066
- this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
4067
- }
4068
- if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
4069
- this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
4070
- }
4071
- }
4119
+ this.#initPluginsArray();
4072
4120
  }
4073
4121
  initUpdaters() {
4074
4122
  this.#preDrawUpdaters = [];
@@ -4277,6 +4325,45 @@
4277
4325
  this.#reusablePluginColors[sColorIndex] = sColor;
4278
4326
  return this.#reusablePluginColors;
4279
4327
  }
4328
+ #initLayerPlugin(plugin) {
4329
+ const layerMap = [
4330
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
4331
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
4332
+ [DrawLayer.PluginContent, !!plugin.draw],
4333
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
4334
+ ];
4335
+ for (const [layer, active] of layerMap) {
4336
+ if (active) {
4337
+ this.#layers[layer].push(plugin);
4338
+ }
4339
+ }
4340
+ }
4341
+ #initPluginsArray() {
4342
+ for (const plugin of this.#container.plugins) {
4343
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
4344
+ this.#colorPlugins.push(plugin);
4345
+ }
4346
+ if (plugin.drawParticle) {
4347
+ this.#drawParticlePlugins.push(plugin);
4348
+ }
4349
+ if (plugin.drawParticleSetup) {
4350
+ this.#drawParticlesSetupPlugins.push(plugin);
4351
+ }
4352
+ if (plugin.drawParticleCleanup) {
4353
+ this.#drawParticlesCleanupPlugins.push(plugin);
4354
+ }
4355
+ if (plugin.canvasClear) {
4356
+ this.#canvasClearPlugins.push(plugin);
4357
+ }
4358
+ this.#initLayerPlugin(plugin);
4359
+ }
4360
+ }
4361
+ #isSupportedElement(element) {
4362
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
4363
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
4364
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
4365
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
4366
+ }
4280
4367
  #resolveBackgroundElement() {
4281
4368
  const background = this.#container.actualOptions.background;
4282
4369
  this.#backgroundElement = null;
@@ -4286,9 +4373,7 @@
4286
4373
  if (typeof background.element === "string") {
4287
4374
  if (typeof document !== "undefined") {
4288
4375
  const node = document.querySelector(background.element);
4289
- if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
4290
- (typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
4291
- (typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
4376
+ if (node && this.#isSupportedElement(node)) {
4292
4377
  this.#backgroundElement = node;
4293
4378
  }
4294
4379
  else if (node) {
@@ -4299,10 +4384,7 @@
4299
4384
  }
4300
4385
  }
4301
4386
  }
4302
- else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
4303
- (typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
4304
- (typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
4305
- (typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
4387
+ else if (this.#isSupportedElement(background.element)) {
4306
4388
  this.#backgroundElement = background.element;
4307
4389
  }
4308
4390
  }
@@ -4320,7 +4402,7 @@
4320
4402
  return transferredCanvas;
4321
4403
  }
4322
4404
  if (typeof canvas.transferControlToOffscreen !== "function") {
4323
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
4405
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasUnsupported));
4324
4406
  }
4325
4407
  try {
4326
4408
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -4328,10 +4410,16 @@
4328
4410
  return offscreenCanvas;
4329
4411
  }
4330
4412
  catch {
4331
- throw new TypeError("OffscreenCanvas transfer failed");
4413
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasTransferFailed));
4332
4414
  }
4333
4415
  }, isHtmlCanvasElement = (canvas) => {
4334
4416
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
4417
+ }, getEffectiveColorSpace = (context) => {
4418
+ if (!context) {
4419
+ return undefined;
4420
+ }
4421
+ const colorInfo = context;
4422
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
4335
4423
  };
4336
4424
  function setStyle(canvas, style, important = false) {
4337
4425
  if (!style) {
@@ -4456,7 +4544,7 @@
4456
4544
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
4457
4545
  if (color) {
4458
4546
  const hdrOptions = container.actualOptions.hdr;
4459
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
4547
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
4460
4548
  }
4461
4549
  else {
4462
4550
  elementStyle.backgroundColor = "";
@@ -4576,11 +4664,11 @@
4576
4664
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4577
4665
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4578
4666
  safeMatchMedia("(dynamic-range: high)")?.matches;
4579
- container.hdr = canSupportHdr ?? false;
4580
- container.hdrMode = container.actualOptions.hdr.mode;
4667
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4581
4668
  container.peakNits = container.actualOptions.hdr.peakNits;
4582
4669
  const renderCanvas = this.renderCanvas;
4583
4670
  if (!renderCanvas) {
4671
+ container.hdr = canSupportHdr ?? false;
4584
4672
  return;
4585
4673
  }
4586
4674
  this.render.setContextSettings({
@@ -4599,7 +4687,6 @@
4599
4687
  context = null;
4600
4688
  }
4601
4689
  if (canSupportHdr && !context) {
4602
- container.hdr = false;
4603
4690
  const sdrSettings = {
4604
4691
  alpha: true,
4605
4692
  desynchronized: true,
@@ -4615,11 +4702,16 @@
4615
4702
  }
4616
4703
  }
4617
4704
  this.render.setContext(context);
4705
+ if (context) {
4706
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4707
+ if (effectiveColorSpace !== undefined) {
4708
+ container.hdr = effectiveColorSpace === "display-p3";
4709
+ }
4710
+ }
4618
4711
  }
4619
4712
  #initHdrListeners() {
4620
4713
  this.#removeHdrListeners();
4621
4714
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4622
- this.#recreateRenderCanvas();
4623
4715
  this.#initContext();
4624
4716
  this.initBackground();
4625
4717
  }, listeners = [];
@@ -4655,16 +4747,6 @@
4655
4747
  element.style.setProperty(key, value, "important");
4656
4748
  }
4657
4749
  }
4658
- #recreateRenderCanvas() {
4659
- const renderCanvas = this.renderCanvas;
4660
- if (!renderCanvas) {
4661
- return;
4662
- }
4663
- if (this.domElement) {
4664
- return;
4665
- }
4666
- this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
4667
- }
4668
4750
  #removeHdrListeners() {
4669
4751
  if (!this.#hdrMediaListeners) {
4670
4752
  return;
@@ -4736,6 +4818,10 @@
4736
4818
  this.#manageListeners(true);
4737
4819
  }
4738
4820
  removeListeners() {
4821
+ if (this.#resizeTimeout !== undefined) {
4822
+ clearTimeout(this.#resizeTimeout);
4823
+ this.#resizeTimeout = undefined;
4824
+ }
4739
4825
  this.#manageListeners(false);
4740
4826
  }
4741
4827
  #handleVisibilityChange() {
@@ -4763,8 +4849,7 @@
4763
4849
  this.#resizeTimeout = undefined;
4764
4850
  }
4765
4851
  const handleResize = async () => {
4766
- const canvas = this.#container.canvas;
4767
- await canvas.windowResize();
4852
+ await this.#container.canvas.windowResize();
4768
4853
  };
4769
4854
  this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
4770
4855
  }
@@ -4792,8 +4877,8 @@
4792
4877
  }
4793
4878
  else if (!this.#resizeObserver && add && canvasEl) {
4794
4879
  this.#resizeObserver = new ResizeObserver((entries) => {
4795
- const entry = entries.find(e => e.target === canvasEl);
4796
- if (!entry) {
4880
+ const res = entries.some(e => e.target === canvasEl);
4881
+ if (!res) {
4797
4882
  return;
4798
4883
  }
4799
4884
  this.#handleWindowResize();
@@ -5057,10 +5142,10 @@
5057
5142
  return this.#modifiers.find(m => m.id === id);
5058
5143
  }
5059
5144
  getOpacity() {
5060
- const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, baseOpacity = getRangeValue(this.opacity?.value ?? defaultOpacity$1), modifierOpacity = this.#applyModifiers(undefined, m => m.opacity), opacity = modifierOpacity ?? baseOpacity, fillOpacity = this.fillOpacity ?? defaultOpacity$1, strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
5061
- this.#cachedOpacityData.fillOpacity = fillOpacity;
5145
+ const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, baseOpacity = getRangeValue(this.opacity?.value ?? defaultOpacity$1), modifierOpacity = this.#applyModifiers(undefined, m => m.opacity), opacity = modifierOpacity ?? baseOpacity;
5146
+ this.#cachedOpacityData.fillOpacity = this.fillOpacity ?? defaultOpacity$1;
5062
5147
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
5063
- this.#cachedOpacityData.strokeOpacity = strokeOpacity;
5148
+ this.#cachedOpacityData.strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
5064
5149
  return this.#cachedOpacityData;
5065
5150
  }
5066
5151
  getPosition() {
@@ -5307,7 +5392,7 @@
5307
5392
  #initPosition(position) {
5308
5393
  const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
5309
5394
  if (!initialPosition) {
5310
- throw new Error("a valid position cannot be found for particle");
5395
+ throw new Error(getErrorMessage(ErrorCodes$1.particlePositionNotFound));
5311
5396
  }
5312
5397
  this.position = initialPosition;
5313
5398
  this.initialPosition = this.position.copy();
@@ -5356,6 +5441,12 @@
5356
5441
  this.#circlePoolIdx = 0;
5357
5442
  this.#rectanglePoolIdx = 0;
5358
5443
  }
5444
+ get cellSize() {
5445
+ return this.#cellSize;
5446
+ }
5447
+ set cellSize(value) {
5448
+ this.#pendingCellSize = value;
5449
+ }
5359
5450
  clear() {
5360
5451
  this.#cells.clear();
5361
5452
  const pendingCellSize = this.#pendingCellSize;
@@ -5379,40 +5470,46 @@
5379
5470
  const minCellX = Math.floor(bounds.minX / this.#cellSize), maxCellX = Math.floor(bounds.maxX / this.#cellSize), minCellY = Math.floor(bounds.minY / this.#cellSize), maxCellY = Math.floor(bounds.maxY / this.#cellSize);
5380
5471
  for (let cx = minCellX; cx <= maxCellX; cx++) {
5381
5472
  for (let cy = minCellY; cy <= maxCellY; cy++) {
5382
- const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
5383
- if (!cellParticles) {
5384
- continue;
5385
- }
5386
- for (const p of cellParticles) {
5387
- if (check && !check(p)) {
5388
- continue;
5389
- }
5390
- if (range.contains(p.getPosition())) {
5391
- out.push(p);
5392
- }
5393
- }
5473
+ this.#queryCell(cx, cy, range, check, out);
5394
5474
  }
5395
5475
  }
5396
5476
  return out;
5397
5477
  }
5398
5478
  queryCircle(position, radius, check, out = []) {
5399
- const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
5400
- this.#releaseShapes();
5401
- return result;
5479
+ const circle = this.#acquireCircle(position.x, position.y, radius);
5480
+ try {
5481
+ return this.query(circle, check, out);
5482
+ }
5483
+ finally {
5484
+ this.#circlePoolIdx--;
5485
+ }
5402
5486
  }
5403
5487
  queryRectangle(position, size, check, out = []) {
5404
- const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
5405
- this.#releaseShapes();
5406
- return result;
5407
- }
5408
- setCellSize(cellSize) {
5409
- this.#pendingCellSize = cellSize;
5488
+ const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height);
5489
+ try {
5490
+ return this.query(rect, check, out);
5491
+ }
5492
+ finally {
5493
+ this.#rectanglePoolIdx--;
5494
+ }
5410
5495
  }
5411
5496
  #acquireCircle(x, y, r) {
5412
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
5497
+ const idx = this.#circlePoolIdx++;
5498
+ let circle = this.#circlePool[idx];
5499
+ if (!circle) {
5500
+ circle = new Circle(x, y, r);
5501
+ this.#circlePool[idx] = circle;
5502
+ }
5503
+ return circle.reset(x, y, r);
5413
5504
  }
5414
5505
  #acquireRectangle(x, y, w, h) {
5415
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
5506
+ const idx = this.#rectanglePoolIdx++;
5507
+ let rectangle = this.#rectanglePool[idx];
5508
+ if (!rectangle) {
5509
+ rectangle = new Rectangle(x, y, w, h);
5510
+ this.#rectanglePool[idx] = rectangle;
5511
+ }
5512
+ return rectangle.reset(x, y, w, h);
5416
5513
  }
5417
5514
  #cellKeyFromCoords(x, y) {
5418
5515
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -5439,17 +5536,27 @@
5439
5536
  }
5440
5537
  return null;
5441
5538
  }
5442
- #releaseShapes() {
5443
- this.#circlePoolIdx = 0;
5444
- this.#rectanglePoolIdx = 0;
5539
+ #queryCell(cx, cy, range, check, out) {
5540
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5541
+ if (!cellParticles) {
5542
+ return;
5543
+ }
5544
+ for (const p of cellParticles) {
5545
+ if (check && !check(p)) {
5546
+ continue;
5547
+ }
5548
+ if (range.contains(p.getPosition())) {
5549
+ out.push(p);
5550
+ }
5551
+ }
5445
5552
  }
5446
5553
  }
5447
5554
 
5448
5555
  class ParticlesManager {
5449
5556
  checkParticlePositionPlugins;
5450
- grid;
5451
5557
  #array;
5452
5558
  #container;
5559
+ #grid;
5453
5560
  #groupLimits;
5454
5561
  #limit;
5455
5562
  #nextId;
@@ -5473,7 +5580,7 @@
5473
5580
  this.#groupLimits = new Map();
5474
5581
  this.#particleBuckets = new Map();
5475
5582
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
5476
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5583
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
5477
5584
  this.checkParticlePositionPlugins = [];
5478
5585
  this.#particleResetPlugins = [];
5479
5586
  this.#particleUpdatePlugins = [];
@@ -5484,23 +5591,12 @@
5484
5591
  get count() {
5485
5592
  return this.#array.length;
5486
5593
  }
5594
+ get grid() {
5595
+ return this.#grid;
5596
+ }
5487
5597
  addParticle(position, overrideOptions, group, initializer) {
5488
- const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
5489
- if (limit > minLimit) {
5490
- switch (limitMode) {
5491
- case LimitMode.delete: {
5492
- const countToRemove = currentCount + countOffset - limit;
5493
- if (countToRemove > minCount) {
5494
- this.removeQuantity(countToRemove);
5495
- }
5496
- break;
5497
- }
5498
- case LimitMode.wait:
5499
- if (currentCount >= limit) {
5500
- return;
5501
- }
5502
- break;
5503
- }
5598
+ if (this.#checkAndApplyLimits(group, this.count)) {
5599
+ return;
5504
5600
  }
5505
5601
  try {
5506
5602
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5564,7 +5660,7 @@
5564
5660
  return this.#array[index];
5565
5661
  }
5566
5662
  async init() {
5567
- const container = this.#container, options = container.actualOptions;
5663
+ const container = this.#container;
5568
5664
  this.checkParticlePositionPlugins = [];
5569
5665
  this.#updatePlugins = [];
5570
5666
  this.#particleUpdatePlugins = [];
@@ -5573,59 +5669,16 @@
5573
5669
  this.#postParticleUpdatePlugins = [];
5574
5670
  this.#particleBuckets.clear();
5575
5671
  this.#resetBuckets(container.zLayers);
5576
- this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5577
- for (const plugin of container.plugins) {
5578
- if (plugin.redrawInit) {
5579
- await plugin.redrawInit();
5580
- }
5581
- if (plugin.checkParticlePosition) {
5582
- this.checkParticlePositionPlugins.push(plugin);
5583
- }
5584
- if (plugin.update) {
5585
- this.#updatePlugins.push(plugin);
5586
- }
5587
- if (plugin.particleUpdate) {
5588
- this.#particleUpdatePlugins.push(plugin);
5589
- }
5590
- if (plugin.postUpdate) {
5591
- this.#postUpdatePlugins.push(plugin);
5592
- }
5593
- if (plugin.particleReset) {
5594
- this.#particleResetPlugins.push(plugin);
5595
- }
5596
- if (plugin.postParticleUpdate) {
5597
- this.#postParticleUpdatePlugins.push(plugin);
5598
- }
5599
- }
5600
- await this.#container.initDrawersAndUpdaters();
5601
- for (const drawer of this.#container.effectDrawers.values()) {
5602
- await drawer.init?.(container);
5603
- }
5604
- for (const drawer of this.#container.shapeDrawers.values()) {
5605
- await drawer.init?.(container);
5606
- }
5607
- let handled = false;
5608
- for (const plugin of container.plugins) {
5609
- handled = plugin.particlesInitialization?.() ?? handled;
5610
- if (handled) {
5611
- break;
5612
- }
5613
- }
5614
- if (!handled) {
5615
- const particlesOptions = options.particles, groups = particlesOptions.groups;
5616
- for (const group in groups) {
5617
- const groupOptions = groups[group];
5618
- if (!groupOptions) {
5619
- continue;
5620
- }
5621
- for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5622
- this.addParticle(undefined, groupOptions, group);
5623
- }
5624
- }
5625
- for (let i = this.count; i < particlesOptions.number.value; i++) {
5626
- this.addParticle();
5672
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5673
+ this.#applyLimits(container.actualOptions.particles);
5674
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5675
+ for (const group in groups) {
5676
+ const groupData = groups[group];
5677
+ if (groupData) {
5678
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5627
5679
  }
5628
5680
  }
5681
+ await this.#initPlugins();
5629
5682
  }
5630
5683
  push(nb, position, overrideOptions, group) {
5631
5684
  for (let i = 0; i < nb; i++) {
@@ -5640,13 +5693,13 @@
5640
5693
  remove(particle, group, override) {
5641
5694
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5642
5695
  }
5643
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5696
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5644
5697
  if (index < minIndex || index > this.count) {
5645
5698
  return;
5646
5699
  }
5647
5700
  let deleted = 0;
5648
5701
  for (let i = index; deleted < quantity && i < this.count; i++) {
5649
- if (this.#removeParticle(i, group, override)) {
5702
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5650
5703
  i--;
5651
5704
  deleted++;
5652
5705
  }
@@ -5677,7 +5730,7 @@
5677
5730
  this.#resizeFactor = factor;
5678
5731
  }
5679
5732
  update(delta) {
5680
- this.grid.clear();
5733
+ this.#grid.clear();
5681
5734
  for (const plugin of this.#updatePlugins) {
5682
5735
  plugin.update?.(delta);
5683
5736
  }
@@ -5688,7 +5741,7 @@
5688
5741
  this.#updateParticlesPhase2(delta, particlesToDelete);
5689
5742
  if (particlesToDelete.size) {
5690
5743
  for (const particle of particlesToDelete) {
5691
- this.remove(particle);
5744
+ this.remove(particle, particle.group);
5692
5745
  }
5693
5746
  }
5694
5747
  this.#resizeFactor = undefined;
@@ -5699,12 +5752,7 @@
5699
5752
  #applyDensity(options, pluginsCount, group, groupOptions) {
5700
5753
  const numberOptions = options.number;
5701
5754
  if (!numberOptions.density.enable) {
5702
- if (group === undefined) {
5703
- this.#limit = numberOptions.limit.value;
5704
- }
5705
- else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
5706
- this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
5707
- }
5755
+ this.#applyLimits(options, group, groupOptions);
5708
5756
  return;
5709
5757
  }
5710
5758
  const densityFactor = this.#initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit.value > minLimit ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + pluginsCount, particlesCount = Math.min(this.count, this.filter(t => t.group === group).length);
@@ -5712,7 +5760,10 @@
5712
5760
  this.#limit = numberOptions.limit.value * densityFactor;
5713
5761
  }
5714
5762
  else {
5715
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5763
+ this.#groupLimits.set(group, {
5764
+ limit: numberOptions.limit.value * densityFactor,
5765
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5766
+ });
5716
5767
  }
5717
5768
  if (particlesCount < particlesNumber) {
5718
5769
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5721,6 +5772,55 @@
5721
5772
  this.removeQuantity(particlesCount - particlesNumber, group);
5722
5773
  }
5723
5774
  }
5775
+ #applyLimits(options, group, groupOptions) {
5776
+ if (group === undefined) {
5777
+ this.#limit = options.number.limit.value;
5778
+ const groups = this.#container.actualOptions.particles.groups;
5779
+ for (const existingGroup of this.#groupLimits.keys()) {
5780
+ if (!(existingGroup in groups)) {
5781
+ this.#groupLimits.delete(existingGroup);
5782
+ }
5783
+ }
5784
+ }
5785
+ else {
5786
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5787
+ if (limitValue > minLimit) {
5788
+ this.#groupLimits.set(group, {
5789
+ limit: limitValue,
5790
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5791
+ });
5792
+ }
5793
+ else {
5794
+ this.#groupLimits.delete(group);
5795
+ }
5796
+ }
5797
+ }
5798
+ #checkAndApplyLimits(group, currentCount) {
5799
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5800
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5801
+ return true;
5802
+ }
5803
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5804
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5805
+ const groupCount = this.filter(t => t.group === group).length;
5806
+ if (groupCount >= groupLimitEntry.limit) {
5807
+ return true;
5808
+ }
5809
+ }
5810
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5811
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5812
+ if (groupCountToRemove > minCount) {
5813
+ this.removeQuantity(groupCountToRemove, group);
5814
+ }
5815
+ }
5816
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5817
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5818
+ if (globalCountToRemove > minCount) {
5819
+ this.#removeAny(globalCountToRemove);
5820
+ }
5821
+ }
5822
+ return false;
5823
+ }
5724
5824
  #createBuckets(zLayers) {
5725
5825
  const bucketCount = Math.max(Math.floor(zLayers), one);
5726
5826
  return Array.from({ length: bucketCount }, () => []);
@@ -5743,6 +5843,70 @@
5743
5843
  }
5744
5844
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5745
5845
  }
5846
+ async #initDrawersAndUpdaters() {
5847
+ const container = this.#container;
5848
+ await container.initDrawersAndUpdaters();
5849
+ for (const drawer of container.effectDrawers.values()) {
5850
+ await drawer.init?.(container);
5851
+ }
5852
+ for (const drawer of container.shapeDrawers.values()) {
5853
+ await drawer.init?.(container);
5854
+ }
5855
+ }
5856
+ async #initPlugins() {
5857
+ await this.#initPluginsArrays();
5858
+ await this.#initDrawersAndUpdaters();
5859
+ let handled = false;
5860
+ const container = this.#container;
5861
+ for (const plugin of container.plugins) {
5862
+ handled = plugin.particlesInitialization?.() ?? handled;
5863
+ if (handled) {
5864
+ break;
5865
+ }
5866
+ }
5867
+ if (handled) {
5868
+ return;
5869
+ }
5870
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5871
+ for (const group in groups) {
5872
+ const groupOptions = groups[group];
5873
+ if (!groupOptions) {
5874
+ continue;
5875
+ }
5876
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5877
+ this.addParticle(undefined, groupOptions, group);
5878
+ }
5879
+ }
5880
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5881
+ this.addParticle();
5882
+ }
5883
+ }
5884
+ async #initPluginsArrays() {
5885
+ const container = this.#container;
5886
+ for (const plugin of container.plugins) {
5887
+ if (plugin.redrawInit) {
5888
+ await plugin.redrawInit();
5889
+ }
5890
+ if (plugin.checkParticlePosition) {
5891
+ this.checkParticlePositionPlugins.push(plugin);
5892
+ }
5893
+ if (plugin.update) {
5894
+ this.#updatePlugins.push(plugin);
5895
+ }
5896
+ if (plugin.particleUpdate) {
5897
+ this.#particleUpdatePlugins.push(plugin);
5898
+ }
5899
+ if (plugin.postUpdate) {
5900
+ this.#postUpdatePlugins.push(plugin);
5901
+ }
5902
+ if (plugin.particleReset) {
5903
+ this.#particleResetPlugins.push(plugin);
5904
+ }
5905
+ if (plugin.postParticleUpdate) {
5906
+ this.#postParticleUpdatePlugins.push(plugin);
5907
+ }
5908
+ }
5909
+ }
5746
5910
  #insertParticleIntoBucket(particle) {
5747
5911
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5748
5912
  if (!bucket) {
@@ -5751,12 +5915,15 @@
5751
5915
  bucket.push(particle);
5752
5916
  this.#particleBuckets.set(particle.id, bucketIndex);
5753
5917
  }
5754
- #removeParticle(index, group, override) {
5918
+ #removeAny(quantity) {
5919
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5920
+ }
5921
+ #removeParticle(index, group, override, matchGroup = true) {
5755
5922
  const particle = this.#array[index];
5756
5923
  if (!particle) {
5757
5924
  return false;
5758
5925
  }
5759
- if (particle.group !== group) {
5926
+ if (matchGroup && particle.group !== group) {
5760
5927
  return false;
5761
5928
  }
5762
5929
  this.#array.splice(index, deleteCount);
@@ -5843,7 +6010,7 @@
5843
6010
  particlesToDelete.add(particle);
5844
6011
  continue;
5845
6012
  }
5846
- this.grid.insert(particle);
6013
+ this.#grid.insert(particle);
5847
6014
  }
5848
6015
  return particlesToDelete;
5849
6016
  }
@@ -5930,6 +6097,7 @@
5930
6097
  started;
5931
6098
  zLayers;
5932
6099
  #delay;
6100
+ #delayResolve;
5933
6101
  #delayTimeout;
5934
6102
  #delta = { value: 0, factor: 0 };
5935
6103
  #dispatchCallback;
@@ -6019,7 +6187,6 @@
6019
6187
  this.shapeDrawers = new Map();
6020
6188
  this.particleUpdaters = [];
6021
6189
  this.plugins.length = 0;
6022
- this.#pluginManager.clearPlugins(this);
6023
6190
  this.destroyed = true;
6024
6191
  this.#onDestroy(remove);
6025
6192
  this.dispatchEvent(EventType.containerDestroyed);
@@ -6072,31 +6239,14 @@
6072
6239
  await this.initDrawersAndUpdaters();
6073
6240
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
6074
6241
  this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
6075
- this.plugins.length = 0;
6076
- this.particleDestroyedPlugins.length = 0;
6077
- this.particleCreatedPlugins.length = 0;
6078
- this.particlePositionPlugins.length = 0;
6079
- for (const [plugin, containerPlugin] of allContainerPlugins) {
6080
- if (plugin.needsPlugin(this.actualOptions)) {
6081
- this.plugins.push(containerPlugin);
6082
- if (containerPlugin.particleCreated) {
6083
- this.particleCreatedPlugins.push(containerPlugin);
6084
- }
6085
- if (containerPlugin.particleDestroyed) {
6086
- this.particleDestroyedPlugins.push(containerPlugin);
6087
- }
6088
- if (containerPlugin.particlePosition) {
6089
- this.particlePositionPlugins.push(containerPlugin);
6090
- }
6091
- }
6092
- }
6242
+ this.#initPlugins(allContainerPlugins);
6093
6243
  this.retina.init();
6094
6244
  this.canvas.init();
6095
6245
  this.updateActualOptions();
6096
6246
  this.canvas.initBackground();
6097
6247
  this.canvas.resize();
6098
6248
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
6099
- this.hdrMode = hdrOptions.mode;
6249
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
6100
6250
  this.peakNits = hdrOptions.peakNits;
6101
6251
  this.zLayers = zLayers;
6102
6252
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -6117,9 +6267,9 @@
6117
6267
  }
6118
6268
  async initDrawersAndUpdaters() {
6119
6269
  const pluginManager = this.#pluginManager;
6120
- this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
6121
- this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
6122
- this.particleUpdaters = await pluginManager.getUpdaters(this, true);
6270
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
6271
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
6272
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
6123
6273
  }
6124
6274
  pause() {
6125
6275
  if (!guardCheck(this)) {
@@ -6186,7 +6336,10 @@
6186
6336
  await this.init();
6187
6337
  this.started = true;
6188
6338
  await new Promise(resolve => {
6339
+ this.#delayResolve = resolve;
6189
6340
  const start = async () => {
6341
+ this.#delayTimeout = undefined;
6342
+ this.#delayResolve = undefined;
6190
6343
  this.#eventListeners.addListeners();
6191
6344
  for (const plugin of this.plugins) {
6192
6345
  await plugin.start?.();
@@ -6202,9 +6355,11 @@
6202
6355
  if (!guardCheck(this) || !this.started) {
6203
6356
  return;
6204
6357
  }
6205
- if (this.#delayTimeout) {
6358
+ if (this.#delayTimeout !== undefined) {
6206
6359
  clearTimeout(this.#delayTimeout);
6207
6360
  this.#delayTimeout = undefined;
6361
+ this.#delayResolve?.();
6362
+ this.#delayResolve = undefined;
6208
6363
  }
6209
6364
  this.#firstStart = true;
6210
6365
  this.started = false;
@@ -6230,6 +6385,27 @@
6230
6385
  }
6231
6386
  return refresh;
6232
6387
  }
6388
+ #initPlugins(allContainerPlugins) {
6389
+ this.plugins.length = 0;
6390
+ this.particleDestroyedPlugins.length = 0;
6391
+ this.particleCreatedPlugins.length = 0;
6392
+ this.particlePositionPlugins.length = 0;
6393
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
6394
+ if (!plugin.needsPlugin(this.actualOptions)) {
6395
+ continue;
6396
+ }
6397
+ this.plugins.push(containerPlugin);
6398
+ if (containerPlugin.particleCreated) {
6399
+ this.particleCreatedPlugins.push(containerPlugin);
6400
+ }
6401
+ if (containerPlugin.particleDestroyed) {
6402
+ this.particleDestroyedPlugins.push(containerPlugin);
6403
+ }
6404
+ if (containerPlugin.particlePosition) {
6405
+ this.particlePositionPlugins.push(containerPlugin);
6406
+ }
6407
+ }
6408
+ }
6233
6409
  #nextFrame(timestamp) {
6234
6410
  try {
6235
6411
  if (!this.#smooth &&
@@ -6506,11 +6682,11 @@
6506
6682
  }
6507
6683
  }
6508
6684
  async #init() {
6509
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6510
- if (!availablePathGenerators) {
6685
+ const pluginManager = this.#pluginManager;
6686
+ if (!pluginManager.initializers.pathGenerators) {
6511
6687
  return;
6512
6688
  }
6513
- this.availablePathGenerators = availablePathGenerators;
6689
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6514
6690
  this.pathGenerators = new Map();
6515
6691
  for (const pathGenerator of this.pathGenerators.values()) {
6516
6692
  pathGenerator.init();
@@ -6581,6 +6757,7 @@
6581
6757
  exports.loadConfettiFallingPreset = loadConfettiFallingPreset;
6582
6758
  exports.tsParticles = tsParticles;
6583
6759
 
6584
- }));
6760
+ })(g.__tsParticlesInternals.presets.confettiFalling = g.__tsParticlesInternals.presets.confettiFalling || {});
6761
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6762
+
6585
6763
  Object.assign(globalThis.window || globalThis, { loadConfettiFallingPreset: (globalThis.__tsParticlesInternals.presets.confettiFalling || {}).loadConfettiFallingPreset });
6586
- delete (globalThis.window || globalThis).tsparticlesInternalExports;