@tsparticles/preset-confetti-falling 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.0 */
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 {
@@ -573,7 +715,6 @@
573
715
  class PluginManager {
574
716
  colorManagers = new Map();
575
717
  easingFunctions = new Map();
576
- effectDrawers = new Map();
577
718
  initializers = {
578
719
  effects: new Map(),
579
720
  shapes: new Map(),
@@ -582,8 +723,6 @@
582
723
  palettes = new Map();
583
724
  plugins = [];
584
725
  presets = new Map();
585
- shapeDrawers = new Map();
586
- updaters = new Map();
587
726
  #allLoadersSet = new Set();
588
727
  #configs = new Map();
589
728
  #engine;
@@ -601,57 +740,79 @@
601
740
  }
602
741
  return res;
603
742
  }
743
+ get initialized() {
744
+ return this.#initialized;
745
+ }
604
746
  addColorManager(name, manager) {
747
+ if (this.#initialized) {
748
+ return;
749
+ }
605
750
  this.colorManagers.set(name, manager);
606
751
  }
607
752
  addConfig(config) {
753
+ if (this.#initialized) {
754
+ return;
755
+ }
608
756
  const key = config.key ?? config.name ?? "default";
609
757
  this.#configs.set(key, config);
610
758
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
611
759
  }
612
760
  addEasing(name, easing) {
761
+ if (this.#initialized) {
762
+ return;
763
+ }
613
764
  if (this.easingFunctions.get(name)) {
614
765
  return;
615
766
  }
616
767
  this.easingFunctions.set(name, easing);
617
768
  }
618
769
  addEffect(effect, drawer) {
770
+ if (this.#initialized) {
771
+ return;
772
+ }
619
773
  this.initializers.effects.set(effect, drawer);
620
774
  }
621
775
  addPalette(name, palette) {
776
+ if (this.#initialized) {
777
+ return;
778
+ }
622
779
  this.palettes.set(name, palette);
623
780
  }
624
781
  addParticleUpdater(name, updaterInitializer) {
782
+ if (this.#initialized) {
783
+ return;
784
+ }
625
785
  this.initializers.updaters.set(name, updaterInitializer);
626
786
  }
627
787
  addPlugin(plugin) {
788
+ if (this.#initialized) {
789
+ return;
790
+ }
628
791
  if (this.getPlugin(plugin.id)) {
629
792
  return;
630
793
  }
631
794
  this.plugins.push(plugin);
632
795
  }
633
796
  addPreset(preset, options, override = false) {
797
+ if (this.#initialized) {
798
+ return;
799
+ }
634
800
  if (!(override || !this.getPreset(preset))) {
635
801
  return;
636
802
  }
637
803
  this.presets.set(preset, options);
638
804
  }
639
805
  addShape(shapes, drawer) {
806
+ if (this.#initialized) {
807
+ return;
808
+ }
640
809
  for (const shape of shapes) {
641
810
  this.initializers.shapes.set(shape, drawer);
642
811
  }
643
812
  }
644
- clearPlugins(container) {
645
- this.effectDrawers.delete(container);
646
- this.shapeDrawers.delete(container);
647
- this.updaters.delete(container);
648
- }
649
813
  getEasing(name) {
650
814
  return this.easingFunctions.get(name) ?? ((value) => value);
651
815
  }
652
- getEffectDrawers(container, force = false) {
653
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
654
- }
655
816
  getPalette(name) {
656
817
  return this.palettes.get(name);
657
818
  }
@@ -661,12 +822,6 @@
661
822
  getPreset(preset) {
662
823
  return this.presets.get(preset);
663
824
  }
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
825
  async init() {
671
826
  if (this.#initialized || this.#isRunningLoaders) {
672
827
  return;
@@ -685,16 +840,9 @@
685
840
  this.#initialized = true;
686
841
  }
687
842
  }
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
843
  async register(...loaders) {
696
844
  if (this.#initialized) {
697
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
845
+ throw new Error(getErrorMessage(ErrorCodes$1.pluginRegisterAfterLoad));
698
846
  }
699
847
  for (const loader of loaders) {
700
848
  if (this.#isRunningLoaders) {
@@ -706,88 +854,15 @@
706
854
  }
707
855
  }
708
856
  async #runLoader(loader, executed, allLoaders) {
709
- if (executed.has(loader))
857
+ if (executed.has(loader)) {
710
858
  return;
859
+ }
711
860
  executed.add(loader);
712
861
  allLoaders.add(loader);
713
862
  await loader(this.#engine);
714
863
  }
715
864
  }
716
865
 
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
866
  class Engine {
792
867
  pluginManager = new PluginManager(this);
793
868
  #domArray = [];
@@ -797,7 +872,7 @@
797
872
  return this.#domArray;
798
873
  }
799
874
  get version() {
800
- return "4.4.0";
875
+ return "4.5.0";
801
876
  }
802
877
  addEventListener(type, listener) {
803
878
  this.#eventDispatcher.addEventListener(type, listener);
@@ -806,7 +881,7 @@
806
881
  if (this.version === pluginVersion) {
807
882
  return;
808
883
  }
809
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
884
+ throw new Error(getErrorMessage(ErrorCodes$1.engineVersionMismatch, this.version, pluginVersion));
810
885
  }
811
886
  dispatchEvent(type, args) {
812
887
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1055,20 +1130,6 @@
1055
1130
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1056
1131
  })(DrawLayer || (DrawLayer = {}));
1057
1132
 
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
1133
  function loadProperty(obj, key, value) {
1073
1134
  if (value !== undefined) {
1074
1135
  obj[key] = value;
@@ -1100,6 +1161,20 @@
1100
1161
  }
1101
1162
  }
1102
1163
 
1164
+ class OptionLoader {
1165
+ load(data) {
1166
+ if (isNull(data)) {
1167
+ return;
1168
+ }
1169
+ this.doLoad(data);
1170
+ }
1171
+ }
1172
+ function loadOptions(options, ...sourceOptionsArr) {
1173
+ for (const sourceOptions of sourceOptionsArr) {
1174
+ options.load(sourceOptions);
1175
+ }
1176
+ }
1177
+
1103
1178
  class AnimationOptions extends OptionLoader {
1104
1179
  count = 0;
1105
1180
  decay = 0;
@@ -1250,15 +1325,14 @@
1250
1325
  peakNits = defaultPeakNits;
1251
1326
  doLoad(data) {
1252
1327
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1253
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1328
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrEnableInvalid, String(data.enable)));
1254
1329
  }
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)}"`);
1330
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1331
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1258
1332
  }
1259
1333
  if (data.peakNits !== undefined &&
1260
1334
  (!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)}"`);
1335
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrPeakNitsInvalid, String(data.peakNits)));
1262
1336
  }
1263
1337
  loadProperty(this, "enable", data.enable);
1264
1338
  loadProperty(this, "mode", data.mode);
@@ -1636,12 +1710,10 @@
1636
1710
  plugin.loadParticlesOptions(this.#container, this, data);
1637
1711
  }
1638
1712
  }
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
- }
1713
+ const updaters = this.#container.particleUpdaters;
1714
+ for (const updater of updaters) {
1715
+ if (updater.loadOptions) {
1716
+ updater.loadOptions(this, data);
1645
1717
  }
1646
1718
  }
1647
1719
  }
@@ -1974,9 +2046,9 @@
1974
2046
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1975
2047
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1976
2048
  }
1977
- const temp1 = lNormalized < half
2049
+ const temp2 = lNormalized < half
1978
2050
  ? 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));
2051
+ : 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
2052
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1981
2053
  }
1982
2054
  function hslToRgbFloat(hsl) {
@@ -1985,9 +2057,9 @@
1985
2057
  const grayscaleValue = lNormalized * rgbMax;
1986
2058
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1987
2059
  }
1988
- const temp1 = lNormalized < half
2060
+ const temp2 = lNormalized < half
1989
2061
  ? 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));
2062
+ : 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
2063
  return { r: red, g: green, b: blue };
1992
2064
  }
1993
2065
  function hslaToRgba(hsla) {
@@ -2201,7 +2273,7 @@
2201
2273
  }
2202
2274
 
2203
2275
  async function loadBlendPlugin(engine) {
2204
- engine.checkVersion("4.4.0");
2276
+ engine.checkVersion("4.5.0");
2205
2277
  await engine.pluginManager.register(e => {
2206
2278
  e.pluginManager.addPlugin(new BlendPlugin());
2207
2279
  });
@@ -2238,7 +2310,7 @@
2238
2310
  }
2239
2311
 
2240
2312
  async function loadCircleShape(engine) {
2241
- engine.checkVersion("4.4.0");
2313
+ engine.checkVersion("4.5.0");
2242
2314
  await engine.pluginManager.register(e => {
2243
2315
  e.pluginManager.addShape(["circle"], () => {
2244
2316
  return Promise.resolve(new CircleDrawer());
@@ -2253,7 +2325,7 @@
2253
2325
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2254
2326
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2255
2327
  })(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;
2328
+ 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
2329
  class HexColorManager {
2258
2330
  accepts(input) {
2259
2331
  return input.startsWith("#");
@@ -2286,7 +2358,7 @@
2286
2358
  }
2287
2359
 
2288
2360
  async function loadHexColorPlugin(engine) {
2289
- engine.checkVersion("4.4.0");
2361
+ engine.checkVersion("4.5.0");
2290
2362
  await engine.pluginManager.register(e => {
2291
2363
  e.pluginManager.addColorManager("hex", new HexColorManager());
2292
2364
  });
@@ -2326,7 +2398,7 @@
2326
2398
  if (!this.accepts(input)) {
2327
2399
  return;
2328
2400
  }
2329
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2401
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2330
2402
  return result
2331
2403
  ? hslaToRgba({
2332
2404
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2339,12 +2411,20 @@
2339
2411
  }
2340
2412
 
2341
2413
  async function loadHslColorPlugin(engine) {
2342
- engine.checkVersion("4.4.0");
2414
+ engine.checkVersion("4.5.0");
2343
2415
  await engine.pluginManager.register(e => {
2344
2416
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2345
2417
  });
2346
2418
  }
2347
2419
 
2420
+ const ErrorCodes = {
2421
+ baseMoverNotLoaded: "TSP-3203",
2422
+ };
2423
+
2424
+ const ErrorMessages = {
2425
+ [ErrorCodes.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2426
+ };
2427
+
2348
2428
  class MovePlugin {
2349
2429
  id = "move";
2350
2430
  #pluginManager;
@@ -2363,20 +2443,20 @@
2363
2443
  }
2364
2444
 
2365
2445
  async function loadMovePlugin(engine) {
2366
- engine.checkVersion("4.4.0");
2446
+ engine.checkVersion("4.5.0");
2447
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2448
+ addErrorMessages(ErrorMessages);
2449
+ }
2367
2450
  await engine.pluginManager.register(e => {
2368
2451
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2369
2452
  movePluginManager.initializers.pathGenerators ??= new Map();
2370
- movePluginManager.pathGenerators ??= new Map();
2371
2453
  movePluginManager.addPathGenerator = (name, generator) => {
2454
+ if (movePluginManager.initialized) {
2455
+ return;
2456
+ }
2372
2457
  movePluginManager.initializers.pathGenerators ??= new Map();
2373
2458
  movePluginManager.initializers.pathGenerators.set(name, generator);
2374
2459
  };
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
2460
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2381
2461
  });
2382
2462
  }
@@ -2579,7 +2659,7 @@
2579
2659
  }
2580
2660
 
2581
2661
  async function loadOpacityUpdater(engine) {
2582
- engine.checkVersion("4.4.0");
2662
+ engine.checkVersion("4.5.0");
2583
2663
  await engine.pluginManager.register(e => {
2584
2664
  e.pluginManager.addParticleUpdater("opacity", container => {
2585
2665
  return Promise.resolve(new OpacityUpdater(container));
@@ -2588,70 +2668,60 @@
2588
2668
  }
2589
2669
 
2590
2670
  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)) {
2671
+ const bounceAxisConfigs = {
2672
+ [OutModeDirection.left]: {
2673
+ axis: "x",
2674
+ canvas: "width",
2675
+ bounce: "horizontal",
2676
+ negativeEdge: "right",
2677
+ positiveEdge: "left",
2678
+ negative: true,
2679
+ },
2680
+ [OutModeDirection.right]: {
2681
+ axis: "x",
2682
+ canvas: "width",
2683
+ bounce: "horizontal",
2684
+ negativeEdge: "right",
2685
+ positiveEdge: "left",
2686
+ negative: false,
2687
+ },
2688
+ [OutModeDirection.top]: {
2689
+ axis: "y",
2690
+ canvas: "height",
2691
+ bounce: "vertical",
2692
+ negativeEdge: "bottom",
2693
+ positiveEdge: "top",
2694
+ negative: true,
2695
+ },
2696
+ [OutModeDirection.bottom]: {
2697
+ axis: "y",
2698
+ canvas: "height",
2699
+ bounce: "vertical",
2700
+ negativeEdge: "bottom",
2701
+ positiveEdge: "top",
2702
+ negative: false,
2703
+ },
2704
+ };
2705
+ function bounce(data) {
2706
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2594
2707
  return;
2595
2708
  }
2596
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2597
- data.particle.position.x = data.size + data.offset.x;
2709
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2710
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2711
+ position[config.axis] = data.size + data.offset[config.axis];
2598
2712
  }
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;
2713
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2714
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2601
2715
  }
2602
- const velocity = data.particle.velocity.x;
2603
- let bounced = false;
2604
2716
  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;
2717
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2718
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2610
2719
  }
2611
- if (!bounced) {
2720
+ else {
2612
2721
  return;
2613
2722
  }
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)) {
2628
- return;
2629
- }
2630
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2631
- data.particle.position.y = data.size + data.offset.y;
2632
- }
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;
2635
- }
2636
- const velocity = data.particle.velocity.y;
2637
- let bounced = false;
2638
- 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;
2644
- }
2645
- if (!bounced) {
2646
- return;
2647
- }
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
- }
2723
+ const minPos = data.offset[config.axis] + data.size;
2724
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2655
2725
  if (data.outMode === OutMode.split) {
2656
2726
  data.particle.destroy();
2657
2727
  }
@@ -2667,26 +2737,19 @@
2667
2737
  OutMode.bounce,
2668
2738
  OutMode.split,
2669
2739
  ];
2670
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2740
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2671
2741
  }
2672
2742
  update(particle, direction, delta, outMode) {
2673
2743
  if (!this.modes.includes(outMode)) {
2674
2744
  return;
2675
2745
  }
2676
- const container = this.#container;
2677
- let handled = false;
2678
2746
  for (const plugin of this.#particleBouncePlugins) {
2679
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2680
- if (handled) {
2681
- break;
2747
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2748
+ return;
2682
2749
  }
2683
2750
  }
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 });
2751
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2752
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2690
2753
  }
2691
2754
  }
2692
2755
 
@@ -2775,108 +2838,109 @@
2775
2838
  return;
2776
2839
  }
2777
2840
  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;
2841
+ if (particle.outType === ParticleOutType.inside) {
2842
+ this.#applyOutModeInside(particle, container);
2843
+ }
2844
+ else {
2845
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2846
+ return;
2805
2847
  }
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;
2848
+ if (particle.outType === ParticleOutType.outside) {
2849
+ this.#applyParticleModeOutside(particle);
2850
+ }
2851
+ else {
2852
+ this.#applyOutModeNormal(particle, container, direction);
2877
2853
  }
2878
2854
  }
2879
2855
  }
2856
+ #applyOutModeInside(particle, container) {
2857
+ const { x: vx, y: vy } = particle.velocity;
2858
+ updateVector.setTo(originPoint);
2859
+ updateVector.length = particle.moveCenter.radius;
2860
+ updateVector.angle = particle.velocity.angle + Math.PI;
2861
+ updateVector.addTo(particle.moveCenter);
2862
+ const { dx, dy } = getDistances(particle.position, updateVector);
2863
+ if ((vx <= minVelocity && dx >= minDistance) ||
2864
+ (vy <= minVelocity && dy >= minDistance) ||
2865
+ (vx >= minVelocity && dx <= minDistance) ||
2866
+ (vy >= minVelocity && dy <= minDistance)) {
2867
+ return;
2868
+ }
2869
+ particle.position.x = Math.floor(randomInRangeValue({
2870
+ min: 0,
2871
+ max: container.canvas.size.width,
2872
+ }));
2873
+ particle.position.y = Math.floor(randomInRangeValue({
2874
+ min: 0,
2875
+ max: container.canvas.size.height,
2876
+ }));
2877
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2878
+ particle.direction = Math.atan2(-newDy, -newDx);
2879
+ particle.velocity.angle = particle.direction;
2880
+ particle.justWarped = true;
2881
+ }
2882
+ #applyOutModeNormal(particle, container, direction) {
2883
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2884
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2885
+ left: -sizeValue - particle.offset.x,
2886
+ right: canvasSize.width + sizeValue + particle.offset.x,
2887
+ top: -sizeValue - particle.offset.y,
2888
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2889
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2890
+ particle.position.x = newPos.left;
2891
+ particle.initialPosition.x = particle.position.x;
2892
+ if (!warp) {
2893
+ particle.position.y = getRandom() * canvasSize.height;
2894
+ particle.initialPosition.y = particle.position.y;
2895
+ }
2896
+ particle.justWarped = true;
2897
+ }
2898
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2899
+ particle.position.x = newPos.right;
2900
+ particle.initialPosition.x = particle.position.x;
2901
+ if (!warp) {
2902
+ particle.position.y = getRandom() * canvasSize.height;
2903
+ particle.initialPosition.y = particle.position.y;
2904
+ }
2905
+ particle.justWarped = true;
2906
+ }
2907
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2908
+ if (!warp) {
2909
+ particle.position.x = getRandom() * canvasSize.width;
2910
+ particle.initialPosition.x = particle.position.x;
2911
+ }
2912
+ particle.position.y = newPos.top;
2913
+ particle.initialPosition.y = particle.position.y;
2914
+ particle.justWarped = true;
2915
+ }
2916
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2917
+ if (!warp) {
2918
+ particle.position.x = getRandom() * canvasSize.width;
2919
+ particle.initialPosition.x = particle.position.x;
2920
+ }
2921
+ particle.position.y = newPos.bottom;
2922
+ particle.initialPosition.y = particle.position.y;
2923
+ particle.justWarped = true;
2924
+ }
2925
+ }
2926
+ #applyParticleModeOutside(particle) {
2927
+ particle.position.x =
2928
+ Math.floor(randomInRangeValue({
2929
+ min: -particle.moveCenter.radius,
2930
+ max: particle.moveCenter.radius,
2931
+ })) + particle.moveCenter.x;
2932
+ particle.position.y =
2933
+ Math.floor(randomInRangeValue({
2934
+ min: -particle.moveCenter.radius,
2935
+ max: particle.moveCenter.radius,
2936
+ })) + particle.moveCenter.y;
2937
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2938
+ if (particle.moveCenter.radius) {
2939
+ particle.direction = Math.atan2(dy, dx);
2940
+ particle.velocity.angle = particle.direction;
2941
+ }
2942
+ particle.justWarped = true;
2943
+ }
2880
2944
  }
2881
2945
 
2882
2946
  const checkOutMode = (outModes, outMode) => {
@@ -2924,56 +2988,26 @@
2924
2988
  }
2925
2989
 
2926
2990
  async function loadOutModesUpdater(engine) {
2927
- engine.checkVersion("4.4.0");
2991
+ engine.checkVersion("4.5.0");
2928
2992
  await engine.pluginManager.register(e => {
2929
2993
  e.pluginManager.addParticleUpdater("outModes", container => {
2930
2994
  return Promise.resolve(new OutOfCanvasUpdater(container));
2931
2995
  });
2932
- });
2933
- }
2934
-
2935
- const defaultOpacity = 1;
2936
- class PaintUpdater {
2937
- #container;
2938
- #pluginManager;
2939
- constructor(pluginManager, container) {
2940
- this.#container = container;
2941
- this.#pluginManager = pluginManager;
2942
- }
2943
- 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
- }
2996
+ });
2997
+ }
2998
+
2999
+ const defaultOpacity = 1;
3000
+ class PaintUpdater {
3001
+ #container;
3002
+ #pluginManager;
3003
+ constructor(pluginManager, container) {
3004
+ this.#container = container;
3005
+ this.#pluginManager = pluginManager;
3006
+ }
3007
+ init(particle) {
3008
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3009
+ this.#initFill(paint, particle, container);
3010
+ this.#initStroke(paint, particle, container);
2977
3011
  }
2978
3012
  isEnabled(particle) {
2979
3013
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2992,10 +3026,48 @@
2992
3026
  updateColor(particle.fillColor, delta, this.#container.hdr);
2993
3027
  updateColor(particle.strokeColor, delta, this.#container.hdr);
2994
3028
  }
3029
+ #initFill(paint, particle, container) {
3030
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3031
+ if (!fill) {
3032
+ particle.fillEnabled = false;
3033
+ particle.fillAnimation = undefined;
3034
+ particle.fillColor = undefined;
3035
+ particle.fillOpacity = defaultOpacity;
3036
+ return;
3037
+ }
3038
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3039
+ particle.fillEnabled = fill.enable;
3040
+ particle.fillOpacity = getRangeValue(fill.opacity);
3041
+ particle.fillAnimation = fillColor.animation;
3042
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3043
+ if (!fillHslColor) {
3044
+ return;
3045
+ }
3046
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3047
+ }
3048
+ #initStroke(paint, particle, container) {
3049
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3050
+ if (!stroke) {
3051
+ particle.strokeAnimation = undefined;
3052
+ particle.strokeColor = undefined;
3053
+ particle.strokeOpacity = defaultOpacity;
3054
+ particle.strokeWidth = 0;
3055
+ return;
3056
+ }
3057
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3058
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3059
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3060
+ particle.strokeAnimation = strokeColor.animation;
3061
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3062
+ if (!strokeHslColor) {
3063
+ return;
3064
+ }
3065
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3066
+ }
2995
3067
  }
2996
3068
 
2997
3069
  async function loadPaintUpdater(engine) {
2998
- engine.checkVersion("4.4.0");
3070
+ engine.checkVersion("4.5.0");
2999
3071
  await engine.pluginManager.register(e => {
3000
3072
  e.pluginManager.addParticleUpdater("paint", container => {
3001
3073
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3037,7 +3109,7 @@
3037
3109
  if (!this.accepts(input)) {
3038
3110
  return;
3039
3111
  }
3040
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3112
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3041
3113
  return result
3042
3114
  ? {
3043
3115
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3050,7 +3122,7 @@
3050
3122
  }
3051
3123
 
3052
3124
  async function loadRgbColorPlugin(engine) {
3053
- engine.checkVersion("4.4.0");
3125
+ engine.checkVersion("4.5.0");
3054
3126
  await engine.pluginManager.register(e => {
3055
3127
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3056
3128
  });
@@ -3135,7 +3207,7 @@
3135
3207
  }
3136
3208
 
3137
3209
  async function loadSizeUpdater(engine) {
3138
- engine.checkVersion("4.4.0");
3210
+ engine.checkVersion("4.5.0");
3139
3211
  await engine.pluginManager.register(e => {
3140
3212
  e.pluginManager.addParticleUpdater("size", container => {
3141
3213
  return Promise.resolve(new SizeUpdater(container));
@@ -3144,7 +3216,7 @@
3144
3216
  }
3145
3217
 
3146
3218
  async function loadBasic(engine) {
3147
- engine.checkVersion("4.4.0");
3219
+ engine.checkVersion("4.5.0");
3148
3220
  await engine.pluginManager.register(async (e) => {
3149
3221
  await Promise.all([
3150
3222
  loadBlendPlugin(e),
@@ -3237,7 +3309,7 @@
3237
3309
  }
3238
3310
 
3239
3311
  async function loadMotionPlugin(engine) {
3240
- engine.checkVersion("4.4.0");
3312
+ engine.checkVersion("4.5.0");
3241
3313
  await engine.pluginManager.register(e => {
3242
3314
  e.pluginManager.addPlugin(new MotionPlugin());
3243
3315
  });
@@ -3370,7 +3442,7 @@
3370
3442
  }
3371
3443
 
3372
3444
  async function loadRollUpdater(engine) {
3373
- engine.checkVersion("4.4.0");
3445
+ engine.checkVersion("4.5.0");
3374
3446
  await engine.pluginManager.register(e => {
3375
3447
  e.pluginManager.addParticleUpdater("roll", () => {
3376
3448
  return Promise.resolve(new RollUpdater(e.pluginManager));
@@ -3429,7 +3501,7 @@
3429
3501
  particle.pathRotation = rotateOptions.path;
3430
3502
  let rotateDirection = rotateOptions.direction;
3431
3503
  if (rotateDirection === RotateDirection.random) {
3432
- const index = Math.floor(getRandom() * double), minIndex = 0;
3504
+ const index = Math.floor(getRandom() * double);
3433
3505
  rotateDirection = index > minIndex ? RotateDirection.counterClockwise : RotateDirection.clockwise;
3434
3506
  }
3435
3507
  switch (rotateDirection) {
@@ -3476,7 +3548,7 @@
3476
3548
  }
3477
3549
 
3478
3550
  async function loadRotateUpdater(engine) {
3479
- engine.checkVersion("4.4.0");
3551
+ engine.checkVersion("4.5.0");
3480
3552
  await engine.pluginManager.register(e => {
3481
3553
  e.pluginManager.addParticleUpdater("rotate", container => {
3482
3554
  return Promise.resolve(new RotateUpdater(container));
@@ -3500,7 +3572,7 @@
3500
3572
  }
3501
3573
 
3502
3574
  async function loadSquareShape(engine) {
3503
- engine.checkVersion("4.4.0");
3575
+ engine.checkVersion("4.5.0");
3504
3576
  await engine.pluginManager.register(e => {
3505
3577
  e.pluginManager.addShape(["edge", "square"], () => Promise.resolve(new SquareDrawer()));
3506
3578
  });
@@ -3572,7 +3644,7 @@
3572
3644
  };
3573
3645
  let tiltDirection = tiltOptions.direction;
3574
3646
  if (tiltDirection === TiltDirection.random) {
3575
- const index = Math.floor(getRandom() * double), minIndex = 0;
3647
+ const index = Math.floor(getRandom() * double);
3576
3648
  tiltDirection = index > minIndex ? TiltDirection.counterClockwise : TiltDirection.clockwise;
3577
3649
  }
3578
3650
  switch (tiltDirection) {
@@ -3609,7 +3681,7 @@
3609
3681
  }
3610
3682
 
3611
3683
  async function loadTiltUpdater(engine) {
3612
- engine.checkVersion("4.4.0");
3684
+ engine.checkVersion("4.5.0");
3613
3685
  await engine.pluginManager.register(e => {
3614
3686
  e.pluginManager.addParticleUpdater("tilt", container => {
3615
3687
  return Promise.resolve(new TiltUpdater(container));
@@ -3711,7 +3783,7 @@
3711
3783
  }
3712
3784
 
3713
3785
  async function loadWobbleUpdater(engine) {
3714
- engine.checkVersion("4.4.0");
3786
+ engine.checkVersion("4.5.0");
3715
3787
  await engine.pluginManager.register(e => {
3716
3788
  e.pluginManager.addParticleUpdater("wobble", container => {
3717
3789
  return Promise.resolve(new WobbleUpdater(container));
@@ -3893,21 +3965,28 @@
3893
3965
  });
3894
3966
  }
3895
3967
  clear() {
3968
+ const visited = new Set();
3896
3969
  for (const plugin of this.#canvasClearPlugins) {
3970
+ visited.add(plugin);
3897
3971
  if (plugin.canvasClear?.() ?? false) {
3898
- return;
3972
+ return plugin;
3899
3973
  }
3900
3974
  }
3901
3975
  for (const layer of Object.values(DrawLayer)) {
3902
3976
  if (typeof layer === "number") {
3903
3977
  for (const plugin of this.#getLayerPlugins(layer)) {
3978
+ if (visited.has(plugin)) {
3979
+ continue;
3980
+ }
3981
+ visited.add(plugin);
3904
3982
  if (plugin.canvasClear?.() ?? false) {
3905
- return;
3983
+ return plugin;
3906
3984
  }
3907
3985
  }
3908
3986
  }
3909
3987
  }
3910
3988
  this.canvasClear();
3989
+ return undefined;
3911
3990
  }
3912
3991
  destroy() {
3913
3992
  this.stop();
@@ -3985,8 +4064,7 @@
3985
4064
  });
3986
4065
  }
3987
4066
  drawParticles(delta) {
3988
- const { particles, actualOptions } = this.#container;
3989
- this.clear();
4067
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3990
4068
  particles.update(delta);
3991
4069
  this.draw(ctx => {
3992
4070
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -4008,7 +4086,9 @@
4008
4086
  }
4009
4087
  }
4010
4088
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
4011
- plugin.canvasPaint?.();
4089
+ if (plugin !== clearedPlugin) {
4090
+ plugin.canvasPaint?.();
4091
+ }
4012
4092
  }
4013
4093
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
4014
4094
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -4040,35 +4120,7 @@
4040
4120
  this.#layers[layer] = [];
4041
4121
  }
4042
4122
  }
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
- }
4123
+ this.#initPluginsArray();
4072
4124
  }
4073
4125
  initUpdaters() {
4074
4126
  this.#preDrawUpdaters = [];
@@ -4277,6 +4329,45 @@
4277
4329
  this.#reusablePluginColors[sColorIndex] = sColor;
4278
4330
  return this.#reusablePluginColors;
4279
4331
  }
4332
+ #initLayerPlugin(plugin) {
4333
+ const layerMap = [
4334
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
4335
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
4336
+ [DrawLayer.PluginContent, !!plugin.draw],
4337
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
4338
+ ];
4339
+ for (const [layer, active] of layerMap) {
4340
+ if (active) {
4341
+ this.#layers[layer].push(plugin);
4342
+ }
4343
+ }
4344
+ }
4345
+ #initPluginsArray() {
4346
+ for (const plugin of this.#container.plugins) {
4347
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
4348
+ this.#colorPlugins.push(plugin);
4349
+ }
4350
+ if (plugin.drawParticle) {
4351
+ this.#drawParticlePlugins.push(plugin);
4352
+ }
4353
+ if (plugin.drawParticleSetup) {
4354
+ this.#drawParticlesSetupPlugins.push(plugin);
4355
+ }
4356
+ if (plugin.drawParticleCleanup) {
4357
+ this.#drawParticlesCleanupPlugins.push(plugin);
4358
+ }
4359
+ if (plugin.canvasClear) {
4360
+ this.#canvasClearPlugins.push(plugin);
4361
+ }
4362
+ this.#initLayerPlugin(plugin);
4363
+ }
4364
+ }
4365
+ #isSupportedElement(element) {
4366
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
4367
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
4368
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
4369
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
4370
+ }
4280
4371
  #resolveBackgroundElement() {
4281
4372
  const background = this.#container.actualOptions.background;
4282
4373
  this.#backgroundElement = null;
@@ -4286,9 +4377,7 @@
4286
4377
  if (typeof background.element === "string") {
4287
4378
  if (typeof document !== "undefined") {
4288
4379
  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)) {
4380
+ if (node && this.#isSupportedElement(node)) {
4292
4381
  this.#backgroundElement = node;
4293
4382
  }
4294
4383
  else if (node) {
@@ -4299,10 +4388,7 @@
4299
4388
  }
4300
4389
  }
4301
4390
  }
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)) {
4391
+ else if (this.#isSupportedElement(background.element)) {
4306
4392
  this.#backgroundElement = background.element;
4307
4393
  }
4308
4394
  }
@@ -4320,7 +4406,7 @@
4320
4406
  return transferredCanvas;
4321
4407
  }
4322
4408
  if (typeof canvas.transferControlToOffscreen !== "function") {
4323
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
4409
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasUnsupported));
4324
4410
  }
4325
4411
  try {
4326
4412
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -4328,10 +4414,16 @@
4328
4414
  return offscreenCanvas;
4329
4415
  }
4330
4416
  catch {
4331
- throw new TypeError("OffscreenCanvas transfer failed");
4417
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasTransferFailed));
4332
4418
  }
4333
4419
  }, isHtmlCanvasElement = (canvas) => {
4334
4420
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
4421
+ }, getEffectiveColorSpace = (context) => {
4422
+ if (!context) {
4423
+ return undefined;
4424
+ }
4425
+ const colorInfo = context;
4426
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
4335
4427
  };
4336
4428
  function setStyle(canvas, style, important = false) {
4337
4429
  if (!style) {
@@ -4456,7 +4548,7 @@
4456
4548
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
4457
4549
  if (color) {
4458
4550
  const hdrOptions = container.actualOptions.hdr;
4459
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
4551
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
4460
4552
  }
4461
4553
  else {
4462
4554
  elementStyle.backgroundColor = "";
@@ -4576,11 +4668,11 @@
4576
4668
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4577
4669
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4578
4670
  safeMatchMedia("(dynamic-range: high)")?.matches;
4579
- container.hdr = canSupportHdr ?? false;
4580
- container.hdrMode = container.actualOptions.hdr.mode;
4671
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4581
4672
  container.peakNits = container.actualOptions.hdr.peakNits;
4582
4673
  const renderCanvas = this.renderCanvas;
4583
4674
  if (!renderCanvas) {
4675
+ container.hdr = canSupportHdr ?? false;
4584
4676
  return;
4585
4677
  }
4586
4678
  this.render.setContextSettings({
@@ -4599,7 +4691,6 @@
4599
4691
  context = null;
4600
4692
  }
4601
4693
  if (canSupportHdr && !context) {
4602
- container.hdr = false;
4603
4694
  const sdrSettings = {
4604
4695
  alpha: true,
4605
4696
  desynchronized: true,
@@ -4615,11 +4706,16 @@
4615
4706
  }
4616
4707
  }
4617
4708
  this.render.setContext(context);
4709
+ if (context) {
4710
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4711
+ if (effectiveColorSpace !== undefined) {
4712
+ container.hdr = effectiveColorSpace === "display-p3";
4713
+ }
4714
+ }
4618
4715
  }
4619
4716
  #initHdrListeners() {
4620
4717
  this.#removeHdrListeners();
4621
4718
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4622
- this.#recreateRenderCanvas();
4623
4719
  this.#initContext();
4624
4720
  this.initBackground();
4625
4721
  }, listeners = [];
@@ -4655,16 +4751,6 @@
4655
4751
  element.style.setProperty(key, value, "important");
4656
4752
  }
4657
4753
  }
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
4754
  #removeHdrListeners() {
4669
4755
  if (!this.#hdrMediaListeners) {
4670
4756
  return;
@@ -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,18 +5470,7 @@
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;
@@ -5405,14 +5485,23 @@
5405
5485
  this.#releaseShapes();
5406
5486
  return result;
5407
5487
  }
5408
- setCellSize(cellSize) {
5409
- this.#pendingCellSize = cellSize;
5410
- }
5411
5488
  #acquireCircle(x, y, r) {
5412
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
5489
+ const idx = this.#circlePoolIdx++;
5490
+ let circle = this.#circlePool[idx];
5491
+ if (!circle) {
5492
+ circle = new Circle(x, y, r);
5493
+ this.#circlePool[idx] = circle;
5494
+ }
5495
+ return circle.reset(x, y, r);
5413
5496
  }
5414
5497
  #acquireRectangle(x, y, w, h) {
5415
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
5498
+ const idx = this.#rectanglePoolIdx++;
5499
+ let rectangle = this.#rectanglePool[idx];
5500
+ if (!rectangle) {
5501
+ rectangle = new Rectangle(x, y, w, h);
5502
+ this.#rectanglePool[idx] = rectangle;
5503
+ }
5504
+ return rectangle.reset(x, y, w, h);
5416
5505
  }
5417
5506
  #cellKeyFromCoords(x, y) {
5418
5507
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -5439,6 +5528,20 @@
5439
5528
  }
5440
5529
  return null;
5441
5530
  }
5531
+ #queryCell(cx, cy, range, check, out) {
5532
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5533
+ if (!cellParticles) {
5534
+ return;
5535
+ }
5536
+ for (const p of cellParticles) {
5537
+ if (check && !check(p)) {
5538
+ continue;
5539
+ }
5540
+ if (range.contains(p.getPosition())) {
5541
+ out.push(p);
5542
+ }
5543
+ }
5544
+ }
5442
5545
  #releaseShapes() {
5443
5546
  this.#circlePoolIdx = 0;
5444
5547
  this.#rectanglePoolIdx = 0;
@@ -5447,9 +5550,9 @@
5447
5550
 
5448
5551
  class ParticlesManager {
5449
5552
  checkParticlePositionPlugins;
5450
- grid;
5451
5553
  #array;
5452
5554
  #container;
5555
+ #grid;
5453
5556
  #groupLimits;
5454
5557
  #limit;
5455
5558
  #nextId;
@@ -5473,7 +5576,7 @@
5473
5576
  this.#groupLimits = new Map();
5474
5577
  this.#particleBuckets = new Map();
5475
5578
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
5476
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5579
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
5477
5580
  this.checkParticlePositionPlugins = [];
5478
5581
  this.#particleResetPlugins = [];
5479
5582
  this.#particleUpdatePlugins = [];
@@ -5484,23 +5587,12 @@
5484
5587
  get count() {
5485
5588
  return this.#array.length;
5486
5589
  }
5590
+ get grid() {
5591
+ return this.#grid;
5592
+ }
5487
5593
  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
- }
5594
+ if (this.#checkAndApplyLimits(group, this.count)) {
5595
+ return;
5504
5596
  }
5505
5597
  try {
5506
5598
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5564,7 +5656,7 @@
5564
5656
  return this.#array[index];
5565
5657
  }
5566
5658
  async init() {
5567
- const container = this.#container, options = container.actualOptions;
5659
+ const container = this.#container;
5568
5660
  this.checkParticlePositionPlugins = [];
5569
5661
  this.#updatePlugins = [];
5570
5662
  this.#particleUpdatePlugins = [];
@@ -5573,59 +5665,16 @@
5573
5665
  this.#postParticleUpdatePlugins = [];
5574
5666
  this.#particleBuckets.clear();
5575
5667
  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();
5668
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5669
+ this.#applyLimits(container.actualOptions.particles);
5670
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5671
+ for (const group in groups) {
5672
+ const groupData = groups[group];
5673
+ if (groupData) {
5674
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5627
5675
  }
5628
5676
  }
5677
+ await this.#initPlugins();
5629
5678
  }
5630
5679
  push(nb, position, overrideOptions, group) {
5631
5680
  for (let i = 0; i < nb; i++) {
@@ -5640,13 +5689,13 @@
5640
5689
  remove(particle, group, override) {
5641
5690
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5642
5691
  }
5643
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5692
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5644
5693
  if (index < minIndex || index > this.count) {
5645
5694
  return;
5646
5695
  }
5647
5696
  let deleted = 0;
5648
5697
  for (let i = index; deleted < quantity && i < this.count; i++) {
5649
- if (this.#removeParticle(i, group, override)) {
5698
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5650
5699
  i--;
5651
5700
  deleted++;
5652
5701
  }
@@ -5677,7 +5726,7 @@
5677
5726
  this.#resizeFactor = factor;
5678
5727
  }
5679
5728
  update(delta) {
5680
- this.grid.clear();
5729
+ this.#grid.clear();
5681
5730
  for (const plugin of this.#updatePlugins) {
5682
5731
  plugin.update?.(delta);
5683
5732
  }
@@ -5688,7 +5737,7 @@
5688
5737
  this.#updateParticlesPhase2(delta, particlesToDelete);
5689
5738
  if (particlesToDelete.size) {
5690
5739
  for (const particle of particlesToDelete) {
5691
- this.remove(particle);
5740
+ this.remove(particle, particle.group);
5692
5741
  }
5693
5742
  }
5694
5743
  this.#resizeFactor = undefined;
@@ -5699,12 +5748,7 @@
5699
5748
  #applyDensity(options, pluginsCount, group, groupOptions) {
5700
5749
  const numberOptions = options.number;
5701
5750
  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
- }
5751
+ this.#applyLimits(options, group, groupOptions);
5708
5752
  return;
5709
5753
  }
5710
5754
  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 +5756,10 @@
5712
5756
  this.#limit = numberOptions.limit.value * densityFactor;
5713
5757
  }
5714
5758
  else {
5715
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5759
+ this.#groupLimits.set(group, {
5760
+ limit: numberOptions.limit.value * densityFactor,
5761
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5762
+ });
5716
5763
  }
5717
5764
  if (particlesCount < particlesNumber) {
5718
5765
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5721,6 +5768,55 @@
5721
5768
  this.removeQuantity(particlesCount - particlesNumber, group);
5722
5769
  }
5723
5770
  }
5771
+ #applyLimits(options, group, groupOptions) {
5772
+ if (group === undefined) {
5773
+ this.#limit = options.number.limit.value;
5774
+ const groups = this.#container.actualOptions.particles.groups;
5775
+ for (const existingGroup of this.#groupLimits.keys()) {
5776
+ if (!(existingGroup in groups)) {
5777
+ this.#groupLimits.delete(existingGroup);
5778
+ }
5779
+ }
5780
+ }
5781
+ else {
5782
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5783
+ if (limitValue > minLimit) {
5784
+ this.#groupLimits.set(group, {
5785
+ limit: limitValue,
5786
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5787
+ });
5788
+ }
5789
+ else {
5790
+ this.#groupLimits.delete(group);
5791
+ }
5792
+ }
5793
+ }
5794
+ #checkAndApplyLimits(group, currentCount) {
5795
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5796
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5797
+ return true;
5798
+ }
5799
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5800
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5801
+ const groupCount = this.filter(t => t.group === group).length;
5802
+ if (groupCount >= groupLimitEntry.limit) {
5803
+ return true;
5804
+ }
5805
+ }
5806
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5807
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5808
+ if (groupCountToRemove > minCount) {
5809
+ this.removeQuantity(groupCountToRemove, group);
5810
+ }
5811
+ }
5812
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5813
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5814
+ if (globalCountToRemove > minCount) {
5815
+ this.#removeAny(globalCountToRemove);
5816
+ }
5817
+ }
5818
+ return false;
5819
+ }
5724
5820
  #createBuckets(zLayers) {
5725
5821
  const bucketCount = Math.max(Math.floor(zLayers), one);
5726
5822
  return Array.from({ length: bucketCount }, () => []);
@@ -5743,6 +5839,70 @@
5743
5839
  }
5744
5840
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5745
5841
  }
5842
+ async #initDrawersAndUpdaters() {
5843
+ const container = this.#container;
5844
+ await container.initDrawersAndUpdaters();
5845
+ for (const drawer of container.effectDrawers.values()) {
5846
+ await drawer.init?.(container);
5847
+ }
5848
+ for (const drawer of container.shapeDrawers.values()) {
5849
+ await drawer.init?.(container);
5850
+ }
5851
+ }
5852
+ async #initPlugins() {
5853
+ await this.#initPluginsArrays();
5854
+ await this.#initDrawersAndUpdaters();
5855
+ let handled = false;
5856
+ const container = this.#container;
5857
+ for (const plugin of container.plugins) {
5858
+ handled = plugin.particlesInitialization?.() ?? handled;
5859
+ if (handled) {
5860
+ break;
5861
+ }
5862
+ }
5863
+ if (handled) {
5864
+ return;
5865
+ }
5866
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5867
+ for (const group in groups) {
5868
+ const groupOptions = groups[group];
5869
+ if (!groupOptions) {
5870
+ continue;
5871
+ }
5872
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5873
+ this.addParticle(undefined, groupOptions, group);
5874
+ }
5875
+ }
5876
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5877
+ this.addParticle();
5878
+ }
5879
+ }
5880
+ async #initPluginsArrays() {
5881
+ const container = this.#container;
5882
+ for (const plugin of container.plugins) {
5883
+ if (plugin.redrawInit) {
5884
+ await plugin.redrawInit();
5885
+ }
5886
+ if (plugin.checkParticlePosition) {
5887
+ this.checkParticlePositionPlugins.push(plugin);
5888
+ }
5889
+ if (plugin.update) {
5890
+ this.#updatePlugins.push(plugin);
5891
+ }
5892
+ if (plugin.particleUpdate) {
5893
+ this.#particleUpdatePlugins.push(plugin);
5894
+ }
5895
+ if (plugin.postUpdate) {
5896
+ this.#postUpdatePlugins.push(plugin);
5897
+ }
5898
+ if (plugin.particleReset) {
5899
+ this.#particleResetPlugins.push(plugin);
5900
+ }
5901
+ if (plugin.postParticleUpdate) {
5902
+ this.#postParticleUpdatePlugins.push(plugin);
5903
+ }
5904
+ }
5905
+ }
5746
5906
  #insertParticleIntoBucket(particle) {
5747
5907
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5748
5908
  if (!bucket) {
@@ -5751,12 +5911,15 @@
5751
5911
  bucket.push(particle);
5752
5912
  this.#particleBuckets.set(particle.id, bucketIndex);
5753
5913
  }
5754
- #removeParticle(index, group, override) {
5914
+ #removeAny(quantity) {
5915
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5916
+ }
5917
+ #removeParticle(index, group, override, matchGroup = true) {
5755
5918
  const particle = this.#array[index];
5756
5919
  if (!particle) {
5757
5920
  return false;
5758
5921
  }
5759
- if (particle.group !== group) {
5922
+ if (matchGroup && particle.group !== group) {
5760
5923
  return false;
5761
5924
  }
5762
5925
  this.#array.splice(index, deleteCount);
@@ -5843,7 +6006,7 @@
5843
6006
  particlesToDelete.add(particle);
5844
6007
  continue;
5845
6008
  }
5846
- this.grid.insert(particle);
6009
+ this.#grid.insert(particle);
5847
6010
  }
5848
6011
  return particlesToDelete;
5849
6012
  }
@@ -6019,7 +6182,6 @@
6019
6182
  this.shapeDrawers = new Map();
6020
6183
  this.particleUpdaters = [];
6021
6184
  this.plugins.length = 0;
6022
- this.#pluginManager.clearPlugins(this);
6023
6185
  this.destroyed = true;
6024
6186
  this.#onDestroy(remove);
6025
6187
  this.dispatchEvent(EventType.containerDestroyed);
@@ -6072,31 +6234,14 @@
6072
6234
  await this.initDrawersAndUpdaters();
6073
6235
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
6074
6236
  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
- }
6237
+ this.#initPlugins(allContainerPlugins);
6093
6238
  this.retina.init();
6094
6239
  this.canvas.init();
6095
6240
  this.updateActualOptions();
6096
6241
  this.canvas.initBackground();
6097
6242
  this.canvas.resize();
6098
6243
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
6099
- this.hdrMode = hdrOptions.mode;
6244
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
6100
6245
  this.peakNits = hdrOptions.peakNits;
6101
6246
  this.zLayers = zLayers;
6102
6247
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -6117,9 +6262,9 @@
6117
6262
  }
6118
6263
  async initDrawersAndUpdaters() {
6119
6264
  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);
6265
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
6266
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
6267
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
6123
6268
  }
6124
6269
  pause() {
6125
6270
  if (!guardCheck(this)) {
@@ -6230,6 +6375,27 @@
6230
6375
  }
6231
6376
  return refresh;
6232
6377
  }
6378
+ #initPlugins(allContainerPlugins) {
6379
+ this.plugins.length = 0;
6380
+ this.particleDestroyedPlugins.length = 0;
6381
+ this.particleCreatedPlugins.length = 0;
6382
+ this.particlePositionPlugins.length = 0;
6383
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
6384
+ if (!plugin.needsPlugin(this.actualOptions)) {
6385
+ continue;
6386
+ }
6387
+ this.plugins.push(containerPlugin);
6388
+ if (containerPlugin.particleCreated) {
6389
+ this.particleCreatedPlugins.push(containerPlugin);
6390
+ }
6391
+ if (containerPlugin.particleDestroyed) {
6392
+ this.particleDestroyedPlugins.push(containerPlugin);
6393
+ }
6394
+ if (containerPlugin.particlePosition) {
6395
+ this.particlePositionPlugins.push(containerPlugin);
6396
+ }
6397
+ }
6398
+ }
6233
6399
  #nextFrame(timestamp) {
6234
6400
  try {
6235
6401
  if (!this.#smooth &&
@@ -6506,11 +6672,11 @@
6506
6672
  }
6507
6673
  }
6508
6674
  async #init() {
6509
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6510
- if (!availablePathGenerators) {
6675
+ const pluginManager = this.#pluginManager;
6676
+ if (!pluginManager.initializers.pathGenerators) {
6511
6677
  return;
6512
6678
  }
6513
- this.availablePathGenerators = availablePathGenerators;
6679
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6514
6680
  this.pathGenerators = new Map();
6515
6681
  for (const pathGenerator of this.pathGenerators.values()) {
6516
6682
  pathGenerator.init();
@@ -6581,6 +6747,7 @@
6581
6747
  exports.loadConfettiFallingPreset = loadConfettiFallingPreset;
6582
6748
  exports.tsParticles = tsParticles;
6583
6749
 
6584
- }));
6750
+ })(g.__tsParticlesInternals.presets.confettiFalling = g.__tsParticlesInternals.presets.confettiFalling || {});
6751
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6752
+
6585
6753
  Object.assign(globalThis.window || globalThis, { loadConfettiFallingPreset: (globalThis.__tsParticlesInternals.presets.confettiFalling || {}).loadConfettiFallingPreset });
6586
- delete (globalThis.window || globalThis).tsparticlesInternalExports;