@tsparticles/preset-bubbles 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.bubbles = global.__tsParticlesInternals.presets.bubbles || {})));
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, 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, 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
  }
@@ -283,11 +314,61 @@
283
314
  };
284
315
  }
285
316
  function parseAlpha(input) {
286
- const defaultAlpha = 1;
287
317
  if (!input) {
288
318
  return defaultAlpha;
289
319
  }
290
- return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
320
+ return input.endsWith("%") ? Number.parseFloat(input) / percentDenominator : Number.parseFloat(input);
321
+ }
322
+
323
+ const ErrorCodes$2 = {
324
+ engineVersionMismatch: "TSP-1001",
325
+ particlePositionNotFound: "TSP-1002",
326
+ pluginRegisterAfterLoad: "TSP-1003",
327
+ offscreenCanvasUnsupported: "TSP-1101",
328
+ offscreenCanvasTransferFailed: "TSP-1102",
329
+ hdrEnableInvalid: "TSP-1201",
330
+ hdrModeInvalid: "TSP-1202",
331
+ hdrPeakNitsInvalid: "TSP-1203",
332
+ };
333
+
334
+ const messages = {
335
+ [ErrorCodes$2.engineVersionMismatch]: "The tsParticles version is different from the loaded plugins version. Engine version: {0}. Plugin version: {1}",
336
+ [ErrorCodes$2.particlePositionNotFound]: "a valid position cannot be found for particle",
337
+ [ErrorCodes$2.pluginRegisterAfterLoad]: "Register plugins can only be done before calling tsParticles.load()",
338
+ [ErrorCodes$2.offscreenCanvasUnsupported]: "OffscreenCanvas is required but not supported by this browser",
339
+ [ErrorCodes$2.offscreenCanvasTransferFailed]: "OffscreenCanvas transfer failed",
340
+ [ErrorCodes$2.hdrEnableInvalid]: 'Invalid HDR "enable" value: expected a boolean, got "{0}"',
341
+ [ErrorCodes$2.hdrModeInvalid]: 'Invalid HDR "mode" value: expected one of {0}, got "{1}"',
342
+ [ErrorCodes$2.hdrPeakNitsInvalid]: 'Invalid HDR "peakNits" value: expected a positive number, got "{0}"',
343
+ };
344
+
345
+ let registeredMessages;
346
+ const ensureRegisteredMessages = () => {
347
+ registeredMessages ??= new Map(Object.entries(messages));
348
+ return registeredMessages;
349
+ }, toStringValue$1 = (value) => String(value), format = (template, args) => {
350
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
351
+ const arg = args[Number(index)];
352
+ return arg === undefined ? match : toStringValue$1(arg);
353
+ });
354
+ };
355
+ function addErrorMessages(extraMessages) {
356
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
357
+ return;
358
+ }
359
+ const catalog = ensureRegisteredMessages();
360
+ for (const [code, message] of Object.entries(extraMessages)) {
361
+ if (message !== undefined) {
362
+ catalog.set(code, message);
363
+ }
364
+ }
365
+ }
366
+ function getErrorMessage(code, ...args) {
367
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
368
+ return `[tsParticles Error ${code}] https://particles.js.org/errors/#${code}`;
369
+ }
370
+ const template = ensureRegisteredMessages().get(code) ?? `Unknown error code ${code}`;
371
+ return format(template, args);
291
372
  }
292
373
 
293
374
  var OutModeDirection;
@@ -304,7 +385,6 @@
304
385
  PixelMode["percent"] = "percent";
305
386
  })(PixelMode || (PixelMode = {}));
306
387
 
307
- const minRadius = 0;
308
388
  function isForbiddenKey(key) {
309
389
  return key === "__proto__" || key === "constructor" || key === "prototype";
310
390
  }
@@ -500,22 +580,84 @@
500
580
  element.removeEventListener(event, handler, removeOptions);
501
581
  }
502
582
  }
503
- async function getItemsFromInitializer(container, map, initializers, force = false) {
504
- let res = map.get(container);
505
- if (!res || force) {
506
- res = await Promise.all([...initializers.values()].map(t => t(container)));
507
- map.set(container, res);
583
+ async function getItemsFromInitializer(container, initializers) {
584
+ return await Promise.all([...initializers.values()].map(t => t(container)));
585
+ }
586
+ async function getItemMapFromInitializer(container, initializers) {
587
+ const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
588
+ return new Map(entries);
589
+ }
590
+ async function getDataFromUrl(data) {
591
+ const url = itemFromSingleOrMultiple(data.url, data.index);
592
+ if (!url) {
593
+ return data.fallback;
508
594
  }
509
- return res;
595
+ try {
596
+ const response = await fetch(url);
597
+ if (response.ok) {
598
+ const current = (await response.json());
599
+ if (isSourceOptionsValue(current)) {
600
+ return current;
601
+ }
602
+ getLogger().error("invalid configuration data while retrieving config file");
603
+ }
604
+ else {
605
+ getLogger().error(`${response.status.toString()} while retrieving config file`);
606
+ }
607
+ }
608
+ catch (error) {
609
+ getLogger().error(`error while retrieving config file: ${error}`);
610
+ }
611
+ return data.fallback;
510
612
  }
511
- async function getItemMapFromInitializer(container, map, initializers, force = false) {
512
- let res = map.get(container);
513
- if (!res || force) {
514
- const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
515
- res = new Map(entries);
516
- map.set(container, res);
613
+ function isSourceOptionsValue(value) {
614
+ if (isArray(value)) {
615
+ return value.every((entry) => isObject(entry) && !isArray(entry));
517
616
  }
518
- return res;
617
+ return isObject(value) && !isArray(value);
618
+ }
619
+ function getCanvasFromContainer(domContainer) {
620
+ let canvasEl;
621
+ const documentSafe = safeDocument(), isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
622
+ if (isCanvas) {
623
+ canvasEl = domContainer;
624
+ canvasEl.dataset[generatedAttribute] ??= generatedFalse;
625
+ if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
626
+ canvasEl.style.width ||= fullPercent;
627
+ canvasEl.style.height ||= fullPercent;
628
+ canvasEl.style.pointerEvents = "none";
629
+ canvasEl.style.setProperty("pointer-events", "none");
630
+ }
631
+ }
632
+ else {
633
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
634
+ if (foundCanvas) {
635
+ canvasEl = foundCanvas;
636
+ canvasEl.dataset[generatedAttribute] = generatedFalse;
637
+ }
638
+ else {
639
+ canvasEl = documentSafe.createElement(canvasTag);
640
+ canvasEl.dataset[generatedAttribute] = generatedTrue;
641
+ domContainer.appendChild(canvasEl);
642
+ }
643
+ canvasEl.style.width ||= fullPercent;
644
+ canvasEl.style.height ||= fullPercent;
645
+ canvasEl.style.pointerEvents = "none";
646
+ canvasEl.style.setProperty("pointer-events", "none");
647
+ }
648
+ return canvasEl;
649
+ }
650
+ function getDomContainer(id, source) {
651
+ const documentSafe = safeDocument();
652
+ let domContainer = source ?? documentSafe.getElementById(id);
653
+ if (domContainer) {
654
+ return domContainer;
655
+ }
656
+ domContainer = documentSafe.createElement("canvas");
657
+ domContainer.id = id;
658
+ domContainer.dataset[generatedAttribute] = generatedTrue;
659
+ documentSafe.body.append(domContainer);
660
+ return domContainer;
519
661
  }
520
662
 
521
663
  class EventDispatcher {
@@ -586,7 +728,6 @@
586
728
  class PluginManager {
587
729
  colorManagers = new Map();
588
730
  easingFunctions = new Map();
589
- effectDrawers = new Map();
590
731
  initializers = {
591
732
  effects: new Map(),
592
733
  shapes: new Map(),
@@ -595,8 +736,6 @@
595
736
  palettes = new Map();
596
737
  plugins = [];
597
738
  presets = new Map();
598
- shapeDrawers = new Map();
599
- updaters = new Map();
600
739
  #allLoadersSet = new Set();
601
740
  #configs = new Map();
602
741
  #engine;
@@ -614,57 +753,79 @@
614
753
  }
615
754
  return res;
616
755
  }
756
+ get initialized() {
757
+ return this.#initialized;
758
+ }
617
759
  addColorManager(name, manager) {
760
+ if (this.#initialized) {
761
+ return;
762
+ }
618
763
  this.colorManagers.set(name, manager);
619
764
  }
620
765
  addConfig(config) {
766
+ if (this.#initialized) {
767
+ return;
768
+ }
621
769
  const key = config.key ?? config.name ?? "default";
622
770
  this.#configs.set(key, config);
623
771
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
624
772
  }
625
773
  addEasing(name, easing) {
774
+ if (this.#initialized) {
775
+ return;
776
+ }
626
777
  if (this.easingFunctions.get(name)) {
627
778
  return;
628
779
  }
629
780
  this.easingFunctions.set(name, easing);
630
781
  }
631
782
  addEffect(effect, drawer) {
783
+ if (this.#initialized) {
784
+ return;
785
+ }
632
786
  this.initializers.effects.set(effect, drawer);
633
787
  }
634
788
  addPalette(name, palette) {
789
+ if (this.#initialized) {
790
+ return;
791
+ }
635
792
  this.palettes.set(name, palette);
636
793
  }
637
794
  addParticleUpdater(name, updaterInitializer) {
795
+ if (this.#initialized) {
796
+ return;
797
+ }
638
798
  this.initializers.updaters.set(name, updaterInitializer);
639
799
  }
640
800
  addPlugin(plugin) {
801
+ if (this.#initialized) {
802
+ return;
803
+ }
641
804
  if (this.getPlugin(plugin.id)) {
642
805
  return;
643
806
  }
644
807
  this.plugins.push(plugin);
645
808
  }
646
809
  addPreset(preset, options, override = false) {
810
+ if (this.#initialized) {
811
+ return;
812
+ }
647
813
  if (!(override || !this.getPreset(preset))) {
648
814
  return;
649
815
  }
650
816
  this.presets.set(preset, options);
651
817
  }
652
818
  addShape(shapes, drawer) {
819
+ if (this.#initialized) {
820
+ return;
821
+ }
653
822
  for (const shape of shapes) {
654
823
  this.initializers.shapes.set(shape, drawer);
655
824
  }
656
825
  }
657
- clearPlugins(container) {
658
- this.effectDrawers.delete(container);
659
- this.shapeDrawers.delete(container);
660
- this.updaters.delete(container);
661
- }
662
826
  getEasing(name) {
663
827
  return this.easingFunctions.get(name) ?? ((value) => value);
664
828
  }
665
- getEffectDrawers(container, force = false) {
666
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
667
- }
668
829
  getPalette(name) {
669
830
  return this.palettes.get(name);
670
831
  }
@@ -674,12 +835,6 @@
674
835
  getPreset(preset) {
675
836
  return this.presets.get(preset);
676
837
  }
677
- async getShapeDrawers(container, force = false) {
678
- return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
679
- }
680
- async getUpdaters(container, force = false) {
681
- return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
682
- }
683
838
  async init() {
684
839
  if (this.#initialized || this.#isRunningLoaders) {
685
840
  return;
@@ -698,16 +853,9 @@
698
853
  this.#initialized = true;
699
854
  }
700
855
  }
701
- loadParticlesOptions(container, options, ...sourceOptions) {
702
- const updaters = this.updaters.get(container);
703
- if (!updaters) {
704
- return;
705
- }
706
- updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
707
- }
708
856
  async register(...loaders) {
709
857
  if (this.#initialized) {
710
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
858
+ throw new Error(getErrorMessage(ErrorCodes$2.pluginRegisterAfterLoad));
711
859
  }
712
860
  for (const loader of loaders) {
713
861
  if (this.#isRunningLoaders) {
@@ -719,88 +867,15 @@
719
867
  }
720
868
  }
721
869
  async #runLoader(loader, executed, allLoaders) {
722
- if (executed.has(loader))
870
+ if (executed.has(loader)) {
723
871
  return;
872
+ }
724
873
  executed.add(loader);
725
874
  allLoaders.add(loader);
726
875
  await loader(this.#engine);
727
876
  }
728
877
  }
729
878
 
730
- const errorPrefix = "tsParticles - Error";
731
- const wrap = (fn) => (...args) => {
732
- fn(...args);
733
- }, _logger = {
734
- debug: wrap(console.debug),
735
- error: (message, ...optionalParams) => {
736
- console.error(`${errorPrefix} - ${message}`, ...optionalParams);
737
- },
738
- info: wrap(console.info),
739
- log: wrap(console.log),
740
- trace: wrap(console.trace),
741
- verbose: wrap(console.log),
742
- warning: wrap(console.warn),
743
- };
744
- function getLogger() {
745
- return _logger;
746
- }
747
-
748
- const fullPercent = "100%";
749
- async function getDataFromUrl(data) {
750
- const url = itemFromSingleOrMultiple(data.url, data.index);
751
- if (!url) {
752
- return data.fallback;
753
- }
754
- const response = await fetch(url);
755
- if (response.ok) {
756
- return (await response.json());
757
- }
758
- getLogger().error(`${response.status.toString()} while retrieving config file`);
759
- return data.fallback;
760
- }
761
- const getCanvasFromContainer = (domContainer) => {
762
- const documentSafe = safeDocument();
763
- let canvasEl;
764
- const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
765
- if (isCanvas) {
766
- canvasEl = domContainer;
767
- canvasEl.dataset[generatedAttribute] ??= generatedFalse;
768
- if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
769
- canvasEl.style.width ||= fullPercent;
770
- canvasEl.style.height ||= fullPercent;
771
- canvasEl.style.pointerEvents = "none";
772
- canvasEl.style.setProperty("pointer-events", "none");
773
- }
774
- }
775
- else {
776
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
777
- if (foundCanvas) {
778
- canvasEl = foundCanvas;
779
- canvasEl.dataset[generatedAttribute] = generatedFalse;
780
- }
781
- else {
782
- canvasEl = documentSafe.createElement(canvasTag);
783
- canvasEl.dataset[generatedAttribute] = generatedTrue;
784
- domContainer.appendChild(canvasEl);
785
- }
786
- canvasEl.style.width ||= fullPercent;
787
- canvasEl.style.height ||= fullPercent;
788
- canvasEl.style.pointerEvents = "none";
789
- canvasEl.style.setProperty("pointer-events", "none");
790
- }
791
- return canvasEl;
792
- }, getDomContainer = (id, source) => {
793
- const documentSafe = safeDocument();
794
- let domContainer = source ?? documentSafe.getElementById(id);
795
- if (domContainer) {
796
- return domContainer;
797
- }
798
- domContainer = documentSafe.createElement("canvas");
799
- domContainer.id = id;
800
- domContainer.dataset[generatedAttribute] = generatedTrue;
801
- documentSafe.body.append(domContainer);
802
- return domContainer;
803
- };
804
879
  class Engine {
805
880
  pluginManager = new PluginManager(this);
806
881
  #domArray = [];
@@ -810,7 +885,7 @@
810
885
  return this.#domArray;
811
886
  }
812
887
  get version() {
813
- return "4.4.0";
888
+ return "4.5.0";
814
889
  }
815
890
  addEventListener(type, listener) {
816
891
  this.#eventDispatcher.addEventListener(type, listener);
@@ -819,7 +894,7 @@
819
894
  if (this.version === pluginVersion) {
820
895
  return;
821
896
  }
822
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
897
+ throw new Error(getErrorMessage(ErrorCodes$2.engineVersionMismatch, this.version, pluginVersion));
823
898
  }
824
899
  dispatchEvent(type, args) {
825
900
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1068,20 +1143,6 @@
1068
1143
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1069
1144
  })(DrawLayer || (DrawLayer = {}));
1070
1145
 
1071
- class OptionLoader {
1072
- load(data) {
1073
- if (isNull(data)) {
1074
- return;
1075
- }
1076
- this.doLoad(data);
1077
- }
1078
- }
1079
- function loadOptions(options, ...sourceOptionsArr) {
1080
- for (const sourceOptions of sourceOptionsArr) {
1081
- options.load(sourceOptions);
1082
- }
1083
- }
1084
-
1085
1146
  function loadProperty(obj, key, value) {
1086
1147
  if (value !== undefined) {
1087
1148
  obj[key] = value;
@@ -1113,6 +1174,20 @@
1113
1174
  }
1114
1175
  }
1115
1176
 
1177
+ class OptionLoader {
1178
+ load(data) {
1179
+ if (isNull(data)) {
1180
+ return;
1181
+ }
1182
+ this.doLoad(data);
1183
+ }
1184
+ }
1185
+ function loadOptions(options, ...sourceOptionsArr) {
1186
+ for (const sourceOptions of sourceOptionsArr) {
1187
+ options.load(sourceOptions);
1188
+ }
1189
+ }
1190
+
1116
1191
  class AnimationOptions extends OptionLoader {
1117
1192
  count = 0;
1118
1193
  decay = 0;
@@ -1263,15 +1338,14 @@
1263
1338
  peakNits = defaultPeakNits;
1264
1339
  doLoad(data) {
1265
1340
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1266
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1341
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrEnableInvalid, String(data.enable)));
1267
1342
  }
1268
- if (data.mode !== undefined &&
1269
- (typeof data.mode !== "string" || !Object.values(HdrMode).includes(data.mode))) {
1270
- throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
1343
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1344
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1271
1345
  }
1272
1346
  if (data.peakNits !== undefined &&
1273
1347
  (!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
1274
- throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
1348
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrPeakNitsInvalid, String(data.peakNits)));
1275
1349
  }
1276
1350
  loadProperty(this, "enable", data.enable);
1277
1351
  loadProperty(this, "mode", data.mode);
@@ -1649,12 +1723,10 @@
1649
1723
  plugin.loadParticlesOptions(this.#container, this, data);
1650
1724
  }
1651
1725
  }
1652
- const updaters = this.#pluginManager.updaters.get(this.#container);
1653
- if (updaters) {
1654
- for (const updater of updaters) {
1655
- if (updater.loadOptions) {
1656
- updater.loadOptions(this, data);
1657
- }
1726
+ const updaters = this.#container.particleUpdaters;
1727
+ for (const updater of updaters) {
1728
+ if (updater.loadOptions) {
1729
+ updater.loadOptions(this, data);
1658
1730
  }
1659
1731
  }
1660
1732
  }
@@ -1987,9 +2059,9 @@
1987
2059
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1988
2060
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1989
2061
  }
1990
- const temp1 = lNormalized < half
2062
+ const temp2 = lNormalized < half
1991
2063
  ? lNormalized * (sNormalizedOffset + sNormalized)
1992
- : 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));
2064
+ : 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));
1993
2065
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1994
2066
  }
1995
2067
  function hslToRgbFloat(hsl) {
@@ -1998,9 +2070,9 @@
1998
2070
  const grayscaleValue = lNormalized * rgbMax;
1999
2071
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
2000
2072
  }
2001
- const temp1 = lNormalized < half
2073
+ const temp2 = lNormalized < half
2002
2074
  ? lNormalized * (sNormalizedOffset + sNormalized)
2003
- : 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));
2075
+ : 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));
2004
2076
  return { r: red, g: green, b: blue };
2005
2077
  }
2006
2078
  function hslaToRgba(hsla) {
@@ -2214,7 +2286,7 @@
2214
2286
  }
2215
2287
 
2216
2288
  async function loadBlendPlugin(engine) {
2217
- engine.checkVersion("4.4.0");
2289
+ engine.checkVersion("4.5.0");
2218
2290
  await engine.pluginManager.register(e => {
2219
2291
  e.pluginManager.addPlugin(new BlendPlugin());
2220
2292
  });
@@ -2251,7 +2323,7 @@
2251
2323
  }
2252
2324
 
2253
2325
  async function loadCircleShape(engine) {
2254
- engine.checkVersion("4.4.0");
2326
+ engine.checkVersion("4.5.0");
2255
2327
  await engine.pluginManager.register(e => {
2256
2328
  e.pluginManager.addShape(["circle"], () => {
2257
2329
  return Promise.resolve(new CircleDrawer());
@@ -2266,7 +2338,7 @@
2266
2338
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2267
2339
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2268
2340
  })(RgbIndexes$1 || (RgbIndexes$1 = {}));
2269
- 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;
2341
+ 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;
2270
2342
  class HexColorManager {
2271
2343
  accepts(input) {
2272
2344
  return input.startsWith("#");
@@ -2299,7 +2371,7 @@
2299
2371
  }
2300
2372
 
2301
2373
  async function loadHexColorPlugin(engine) {
2302
- engine.checkVersion("4.4.0");
2374
+ engine.checkVersion("4.5.0");
2303
2375
  await engine.pluginManager.register(e => {
2304
2376
  e.pluginManager.addColorManager("hex", new HexColorManager());
2305
2377
  });
@@ -2339,7 +2411,7 @@
2339
2411
  if (!this.accepts(input)) {
2340
2412
  return;
2341
2413
  }
2342
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2414
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2343
2415
  return result
2344
2416
  ? hslaToRgba({
2345
2417
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2352,12 +2424,20 @@
2352
2424
  }
2353
2425
 
2354
2426
  async function loadHslColorPlugin(engine) {
2355
- engine.checkVersion("4.4.0");
2427
+ engine.checkVersion("4.5.0");
2356
2428
  await engine.pluginManager.register(e => {
2357
2429
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2358
2430
  });
2359
2431
  }
2360
2432
 
2433
+ const ErrorCodes$1 = {
2434
+ baseMoverNotLoaded: "TSP-3203",
2435
+ };
2436
+
2437
+ const ErrorMessages$1 = {
2438
+ [ErrorCodes$1.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2439
+ };
2440
+
2361
2441
  class MovePlugin {
2362
2442
  id = "move";
2363
2443
  #pluginManager;
@@ -2376,20 +2456,20 @@
2376
2456
  }
2377
2457
 
2378
2458
  async function loadMovePlugin(engine) {
2379
- engine.checkVersion("4.4.0");
2459
+ engine.checkVersion("4.5.0");
2460
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2461
+ addErrorMessages(ErrorMessages$1);
2462
+ }
2380
2463
  await engine.pluginManager.register(e => {
2381
2464
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2382
2465
  movePluginManager.initializers.pathGenerators ??= new Map();
2383
- movePluginManager.pathGenerators ??= new Map();
2384
2466
  movePluginManager.addPathGenerator = (name, generator) => {
2467
+ if (movePluginManager.initialized) {
2468
+ return;
2469
+ }
2385
2470
  movePluginManager.initializers.pathGenerators ??= new Map();
2386
2471
  movePluginManager.initializers.pathGenerators.set(name, generator);
2387
2472
  };
2388
- movePluginManager.getPathGenerators = async (container, force = false) => {
2389
- movePluginManager.initializers.pathGenerators ??= new Map();
2390
- movePluginManager.pathGenerators ??= new Map();
2391
- return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
2392
- };
2393
2473
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2394
2474
  });
2395
2475
  }
@@ -2586,7 +2666,7 @@
2586
2666
  }
2587
2667
 
2588
2668
  async function loadOpacityUpdater(engine) {
2589
- engine.checkVersion("4.4.0");
2669
+ engine.checkVersion("4.5.0");
2590
2670
  await engine.pluginManager.register(e => {
2591
2671
  e.pluginManager.addParticleUpdater("opacity", container => {
2592
2672
  return Promise.resolve(new OpacityUpdater(container));
@@ -2595,70 +2675,60 @@
2595
2675
  }
2596
2676
 
2597
2677
  const boundsMin = 0;
2598
- function bounceHorizontal(data) {
2599
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2600
- (data.direction !== OutModeDirection.left && data.direction !== OutModeDirection.right)) {
2601
- return;
2602
- }
2603
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2604
- data.particle.position.x = data.size + data.offset.x;
2605
- }
2606
- else if (data.bounds.left > data.canvasSize.width && data.direction === OutModeDirection.right) {
2607
- data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
2608
- }
2609
- const velocity = data.particle.velocity.x;
2610
- let bounced = false;
2611
- if (data.outOfCanvas &&
2612
- ((data.direction === OutModeDirection.right && velocity > minVelocity) ||
2613
- (data.direction === OutModeDirection.left && velocity < minVelocity))) {
2614
- const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
2615
- data.particle.velocity.x *= -newVelocity;
2616
- bounced = true;
2617
- }
2618
- if (!bounced) {
2619
- return;
2620
- }
2621
- const minPos = data.offset.x + data.size;
2622
- if (data.outOfCanvas && data.direction === OutModeDirection.right) {
2623
- data.particle.position.x = data.canvasSize.width - minPos;
2624
- }
2625
- else if (data.outOfCanvas && data.direction === OutModeDirection.left) {
2626
- data.particle.position.x = minPos;
2627
- }
2628
- if (data.outMode === OutMode.split) {
2629
- data.particle.destroy();
2630
- }
2631
- }
2632
- function bounceVertical(data) {
2633
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2634
- (data.direction !== OutModeDirection.bottom && data.direction !== OutModeDirection.top)) {
2678
+ const bounceAxisConfigs = {
2679
+ [OutModeDirection.left]: {
2680
+ axis: "x",
2681
+ canvas: "width",
2682
+ bounce: "horizontal",
2683
+ negativeEdge: "right",
2684
+ positiveEdge: "left",
2685
+ negative: true,
2686
+ },
2687
+ [OutModeDirection.right]: {
2688
+ axis: "x",
2689
+ canvas: "width",
2690
+ bounce: "horizontal",
2691
+ negativeEdge: "right",
2692
+ positiveEdge: "left",
2693
+ negative: false,
2694
+ },
2695
+ [OutModeDirection.top]: {
2696
+ axis: "y",
2697
+ canvas: "height",
2698
+ bounce: "vertical",
2699
+ negativeEdge: "bottom",
2700
+ positiveEdge: "top",
2701
+ negative: true,
2702
+ },
2703
+ [OutModeDirection.bottom]: {
2704
+ axis: "y",
2705
+ canvas: "height",
2706
+ bounce: "vertical",
2707
+ negativeEdge: "bottom",
2708
+ positiveEdge: "top",
2709
+ negative: false,
2710
+ },
2711
+ };
2712
+ function bounce(data) {
2713
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2635
2714
  return;
2636
2715
  }
2637
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2638
- data.particle.position.y = data.size + data.offset.y;
2716
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2717
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2718
+ position[config.axis] = data.size + data.offset[config.axis];
2639
2719
  }
2640
- else if (data.bounds.top > data.canvasSize.height && data.direction === OutModeDirection.bottom) {
2641
- data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
2720
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2721
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2642
2722
  }
2643
- const velocity = data.particle.velocity.y;
2644
- let bounced = false;
2645
2723
  if (data.outOfCanvas &&
2646
- ((data.direction === OutModeDirection.bottom && velocity > minVelocity) ||
2647
- (data.direction === OutModeDirection.top && velocity < minVelocity))) {
2648
- const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
2649
- data.particle.velocity.y *= -newVelocity;
2650
- bounced = true;
2724
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2725
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2651
2726
  }
2652
- if (!bounced) {
2727
+ else {
2653
2728
  return;
2654
2729
  }
2655
- const minPos = data.offset.y + data.size;
2656
- if (data.outOfCanvas && data.direction === OutModeDirection.bottom) {
2657
- data.particle.position.y = data.canvasSize.height - minPos;
2658
- }
2659
- else if (data.outOfCanvas && data.direction === OutModeDirection.top) {
2660
- data.particle.position.y = minPos;
2661
- }
2730
+ const minPos = data.offset[config.axis] + data.size;
2731
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2662
2732
  if (data.outMode === OutMode.split) {
2663
2733
  data.particle.destroy();
2664
2734
  }
@@ -2674,26 +2744,19 @@
2674
2744
  OutMode.bounce,
2675
2745
  OutMode.split,
2676
2746
  ];
2677
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2747
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2678
2748
  }
2679
2749
  update(particle, direction, delta, outMode) {
2680
2750
  if (!this.modes.includes(outMode)) {
2681
2751
  return;
2682
2752
  }
2683
- const container = this.#container;
2684
- let handled = false;
2685
2753
  for (const plugin of this.#particleBouncePlugins) {
2686
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2687
- if (handled) {
2688
- break;
2754
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2755
+ return;
2689
2756
  }
2690
2757
  }
2691
- if (handled) {
2692
- return;
2693
- }
2694
- const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2695
- bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2696
- bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2758
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2759
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2697
2760
  }
2698
2761
  }
2699
2762
 
@@ -2782,107 +2845,108 @@
2782
2845
  return;
2783
2846
  }
2784
2847
  const container = this.#container;
2785
- switch (particle.outType) {
2786
- case ParticleOutType.inside: {
2787
- const { x: vx, y: vy } = particle.velocity;
2788
- updateVector.setTo(originPoint);
2789
- updateVector.length = particle.moveCenter.radius;
2790
- updateVector.angle = particle.velocity.angle + Math.PI;
2791
- updateVector.addTo(particle.moveCenter);
2792
- const { dx, dy } = getDistances(particle.position, updateVector);
2793
- if ((vx <= minVelocity && dx >= minDistance) ||
2794
- (vy <= minVelocity && dy >= minDistance) ||
2795
- (vx >= minVelocity && dx <= minDistance) ||
2796
- (vy >= minVelocity && dy <= minDistance)) {
2797
- return;
2798
- }
2799
- particle.position.x = Math.floor(randomInRangeValue({
2800
- min: 0,
2801
- max: container.canvas.size.width,
2802
- }));
2803
- particle.position.y = Math.floor(randomInRangeValue({
2804
- min: 0,
2805
- max: container.canvas.size.height,
2806
- }));
2807
- const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2808
- particle.direction = Math.atan2(-newDy, -newDx);
2809
- particle.velocity.angle = particle.direction;
2810
- particle.justWarped = true;
2811
- break;
2848
+ if (particle.outType === ParticleOutType.inside) {
2849
+ this.#applyOutModeInside(particle, container);
2850
+ }
2851
+ else {
2852
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2853
+ return;
2812
2854
  }
2813
- default: {
2814
- if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2815
- return;
2816
- }
2817
- switch (particle.outType) {
2818
- case ParticleOutType.outside: {
2819
- particle.position.x =
2820
- Math.floor(randomInRangeValue({
2821
- min: -particle.moveCenter.radius,
2822
- max: particle.moveCenter.radius,
2823
- })) + particle.moveCenter.x;
2824
- particle.position.y =
2825
- Math.floor(randomInRangeValue({
2826
- min: -particle.moveCenter.radius,
2827
- max: particle.moveCenter.radius,
2828
- })) + particle.moveCenter.y;
2829
- const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2830
- if (particle.moveCenter.radius) {
2831
- particle.direction = Math.atan2(dy, dx);
2832
- particle.velocity.angle = particle.direction;
2833
- }
2834
- particle.justWarped = true;
2835
- break;
2836
- }
2837
- case ParticleOutType.normal: {
2838
- const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
2839
- bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
2840
- left: -particle.getRadius() - particle.offset.x,
2841
- right: canvasSize.width + particle.getRadius() + particle.offset.x,
2842
- top: -particle.getRadius() - particle.offset.y,
2843
- }, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
2844
- if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2845
- particle.position.x = newPos.left;
2846
- particle.initialPosition.x = particle.position.x;
2847
- if (!warp) {
2848
- particle.position.y = getRandom() * canvasSize.height;
2849
- particle.initialPosition.y = particle.position.y;
2850
- }
2851
- particle.justWarped = true;
2852
- }
2853
- else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2854
- particle.position.x = newPos.right;
2855
- particle.initialPosition.x = particle.position.x;
2856
- if (!warp) {
2857
- particle.position.y = getRandom() * canvasSize.height;
2858
- particle.initialPosition.y = particle.position.y;
2859
- }
2860
- particle.justWarped = true;
2861
- }
2862
- if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2863
- if (!warp) {
2864
- particle.position.x = getRandom() * canvasSize.width;
2865
- particle.initialPosition.x = particle.position.x;
2866
- }
2867
- particle.position.y = newPos.top;
2868
- particle.initialPosition.y = particle.position.y;
2869
- particle.justWarped = true;
2870
- }
2871
- else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2872
- if (!warp) {
2873
- particle.position.x = getRandom() * canvasSize.width;
2874
- particle.initialPosition.x = particle.position.x;
2875
- }
2876
- particle.position.y = newPos.bottom;
2877
- particle.initialPosition.y = particle.position.y;
2878
- particle.justWarped = true;
2879
- }
2880
- break;
2881
- }
2882
- }
2883
- break;
2855
+ if (particle.outType === ParticleOutType.outside) {
2856
+ this.#applyParticleModeOutside(particle);
2884
2857
  }
2858
+ else {
2859
+ this.#applyOutModeNormal(particle, container, direction);
2860
+ }
2861
+ }
2862
+ }
2863
+ #applyOutModeInside(particle, container) {
2864
+ const { x: vx, y: vy } = particle.velocity;
2865
+ updateVector.setTo(originPoint);
2866
+ updateVector.length = particle.moveCenter.radius;
2867
+ updateVector.angle = particle.velocity.angle + Math.PI;
2868
+ updateVector.addTo(particle.moveCenter);
2869
+ const { dx, dy } = getDistances(particle.position, updateVector);
2870
+ if ((vx <= minVelocity && dx >= minDistance) ||
2871
+ (vy <= minVelocity && dy >= minDistance) ||
2872
+ (vx >= minVelocity && dx <= minDistance) ||
2873
+ (vy >= minVelocity && dy <= minDistance)) {
2874
+ return;
2885
2875
  }
2876
+ particle.position.x = Math.floor(randomInRangeValue({
2877
+ min: 0,
2878
+ max: container.canvas.size.width,
2879
+ }));
2880
+ particle.position.y = Math.floor(randomInRangeValue({
2881
+ min: 0,
2882
+ max: container.canvas.size.height,
2883
+ }));
2884
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2885
+ particle.direction = Math.atan2(-newDy, -newDx);
2886
+ particle.velocity.angle = particle.direction;
2887
+ particle.justWarped = true;
2888
+ }
2889
+ #applyOutModeNormal(particle, container, direction) {
2890
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2891
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2892
+ left: -sizeValue - particle.offset.x,
2893
+ right: canvasSize.width + sizeValue + particle.offset.x,
2894
+ top: -sizeValue - particle.offset.y,
2895
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2896
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2897
+ particle.position.x = newPos.left;
2898
+ particle.initialPosition.x = particle.position.x;
2899
+ if (!warp) {
2900
+ particle.position.y = getRandom() * canvasSize.height;
2901
+ particle.initialPosition.y = particle.position.y;
2902
+ }
2903
+ particle.justWarped = true;
2904
+ }
2905
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2906
+ particle.position.x = newPos.right;
2907
+ particle.initialPosition.x = particle.position.x;
2908
+ if (!warp) {
2909
+ particle.position.y = getRandom() * canvasSize.height;
2910
+ particle.initialPosition.y = particle.position.y;
2911
+ }
2912
+ particle.justWarped = true;
2913
+ }
2914
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2915
+ if (!warp) {
2916
+ particle.position.x = getRandom() * canvasSize.width;
2917
+ particle.initialPosition.x = particle.position.x;
2918
+ }
2919
+ particle.position.y = newPos.top;
2920
+ particle.initialPosition.y = particle.position.y;
2921
+ particle.justWarped = true;
2922
+ }
2923
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2924
+ if (!warp) {
2925
+ particle.position.x = getRandom() * canvasSize.width;
2926
+ particle.initialPosition.x = particle.position.x;
2927
+ }
2928
+ particle.position.y = newPos.bottom;
2929
+ particle.initialPosition.y = particle.position.y;
2930
+ particle.justWarped = true;
2931
+ }
2932
+ }
2933
+ #applyParticleModeOutside(particle) {
2934
+ particle.position.x =
2935
+ Math.floor(randomInRangeValue({
2936
+ min: -particle.moveCenter.radius,
2937
+ max: particle.moveCenter.radius,
2938
+ })) + particle.moveCenter.x;
2939
+ particle.position.y =
2940
+ Math.floor(randomInRangeValue({
2941
+ min: -particle.moveCenter.radius,
2942
+ max: particle.moveCenter.radius,
2943
+ })) + particle.moveCenter.y;
2944
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2945
+ if (particle.moveCenter.radius) {
2946
+ particle.direction = Math.atan2(dy, dx);
2947
+ particle.velocity.angle = particle.direction;
2948
+ }
2949
+ particle.justWarped = true;
2886
2950
  }
2887
2951
  }
2888
2952
 
@@ -2931,7 +2995,7 @@
2931
2995
  }
2932
2996
 
2933
2997
  async function loadOutModesUpdater(engine) {
2934
- engine.checkVersion("4.4.0");
2998
+ engine.checkVersion("4.5.0");
2935
2999
  await engine.pluginManager.register(e => {
2936
3000
  e.pluginManager.addParticleUpdater("outModes", container => {
2937
3001
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2948,39 +3012,9 @@
2948
3012
  this.#pluginManager = pluginManager;
2949
3013
  }
2950
3014
  init(particle) {
2951
- 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;
2952
- if (fill) {
2953
- const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
2954
- particle.fillEnabled = fill.enable;
2955
- particle.fillOpacity = getRangeValue(fill.opacity);
2956
- particle.fillAnimation = fillColor.animation;
2957
- const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
2958
- if (fillHslColor) {
2959
- particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
2960
- }
2961
- }
2962
- else {
2963
- particle.fillEnabled = false;
2964
- particle.fillAnimation = undefined;
2965
- particle.fillColor = undefined;
2966
- particle.fillOpacity = defaultOpacity;
2967
- }
2968
- if (stroke) {
2969
- const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
2970
- particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
2971
- particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
2972
- particle.strokeAnimation = strokeColor.animation;
2973
- const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
2974
- if (strokeHslColor) {
2975
- particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
2976
- }
2977
- }
2978
- else {
2979
- particle.strokeAnimation = undefined;
2980
- particle.strokeColor = undefined;
2981
- particle.strokeOpacity = defaultOpacity;
2982
- particle.strokeWidth = 0;
2983
- }
3015
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3016
+ this.#initFill(paint, particle, container);
3017
+ this.#initStroke(paint, particle, container);
2984
3018
  }
2985
3019
  isEnabled(particle) {
2986
3020
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2996,13 +3030,51 @@
2996
3030
  if (!this.isEnabled(particle)) {
2997
3031
  return;
2998
3032
  }
2999
- updateColor(particle.fillColor, delta, this.#container.hdr);
3000
- updateColor(particle.strokeColor, delta, this.#container.hdr);
3033
+ updateColor(particle.fillColor, delta, this.#container.hdr);
3034
+ updateColor(particle.strokeColor, delta, this.#container.hdr);
3035
+ }
3036
+ #initFill(paint, particle, container) {
3037
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3038
+ if (!fill) {
3039
+ particle.fillEnabled = false;
3040
+ particle.fillAnimation = undefined;
3041
+ particle.fillColor = undefined;
3042
+ particle.fillOpacity = defaultOpacity;
3043
+ return;
3044
+ }
3045
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3046
+ particle.fillEnabled = fill.enable;
3047
+ particle.fillOpacity = getRangeValue(fill.opacity);
3048
+ particle.fillAnimation = fillColor.animation;
3049
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3050
+ if (!fillHslColor) {
3051
+ return;
3052
+ }
3053
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3054
+ }
3055
+ #initStroke(paint, particle, container) {
3056
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3057
+ if (!stroke) {
3058
+ particle.strokeAnimation = undefined;
3059
+ particle.strokeColor = undefined;
3060
+ particle.strokeOpacity = defaultOpacity;
3061
+ particle.strokeWidth = 0;
3062
+ return;
3063
+ }
3064
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3065
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3066
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3067
+ particle.strokeAnimation = strokeColor.animation;
3068
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3069
+ if (!strokeHslColor) {
3070
+ return;
3071
+ }
3072
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3001
3073
  }
3002
3074
  }
3003
3075
 
3004
3076
  async function loadPaintUpdater(engine) {
3005
- engine.checkVersion("4.4.0");
3077
+ engine.checkVersion("4.5.0");
3006
3078
  await engine.pluginManager.register(e => {
3007
3079
  e.pluginManager.addParticleUpdater("paint", container => {
3008
3080
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3044,7 +3116,7 @@
3044
3116
  if (!this.accepts(input)) {
3045
3117
  return;
3046
3118
  }
3047
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3119
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3048
3120
  return result
3049
3121
  ? {
3050
3122
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3057,7 +3129,7 @@
3057
3129
  }
3058
3130
 
3059
3131
  async function loadRgbColorPlugin(engine) {
3060
- engine.checkVersion("4.4.0");
3132
+ engine.checkVersion("4.5.0");
3061
3133
  await engine.pluginManager.register(e => {
3062
3134
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3063
3135
  });
@@ -3142,7 +3214,7 @@
3142
3214
  }
3143
3215
 
3144
3216
  async function loadSizeUpdater(engine) {
3145
- engine.checkVersion("4.4.0");
3217
+ engine.checkVersion("4.5.0");
3146
3218
  await engine.pluginManager.register(e => {
3147
3219
  e.pluginManager.addParticleUpdater("size", container => {
3148
3220
  return Promise.resolve(new SizeUpdater(container));
@@ -3151,7 +3223,7 @@
3151
3223
  }
3152
3224
 
3153
3225
  async function loadBasic(engine) {
3154
- engine.checkVersion("4.4.0");
3226
+ engine.checkVersion("4.5.0");
3155
3227
  await engine.pluginManager.register(async (e) => {
3156
3228
  await Promise.all([
3157
3229
  loadBlendPlugin(e),
@@ -3260,6 +3332,8 @@
3260
3332
  autoPlay = true;
3261
3333
  direction;
3262
3334
  domId;
3335
+ draggable = false;
3336
+ draw = true;
3263
3337
  fill = true;
3264
3338
  life = new EmitterLife();
3265
3339
  name;
@@ -3283,6 +3357,8 @@
3283
3357
  }
3284
3358
  loadProperty(this, "direction", data.direction);
3285
3359
  loadProperty(this, "domId", data.domId);
3360
+ loadProperty(this, "draggable", data.draggable);
3361
+ loadProperty(this, "draw", data.draw);
3286
3362
  loadProperty(this, "fill", data.fill);
3287
3363
  this.life.load(data.life);
3288
3364
  loadProperty(this, "name", data.name);
@@ -3344,6 +3420,14 @@
3344
3420
  }
3345
3421
  }
3346
3422
 
3423
+ const ErrorCodes = {
3424
+ emittersPluginNotLoaded: "TSP-3101",
3425
+ };
3426
+
3427
+ const ErrorMessages = {
3428
+ [ErrorCodes.emittersPluginNotLoaded]: "tsParticles Emitters Plugin is not loaded",
3429
+ };
3430
+
3347
3431
  async function addEmittersShapesManager(e) {
3348
3432
  const { ShapeManager } = await Promise.resolve().then(function () { return ShapeManager$1; }), pluginManager = e.pluginManager;
3349
3433
  pluginManager.emitterShapeManager ??= new ShapeManager();
@@ -3374,7 +3458,10 @@
3374
3458
  })(EmitterClickMode || (EmitterClickMode = {}));
3375
3459
 
3376
3460
  async function loadEmittersPluginSimple(engine) {
3377
- engine.checkVersion("4.4.0");
3461
+ engine.checkVersion("4.5.0");
3462
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
3463
+ addErrorMessages(ErrorMessages);
3464
+ }
3378
3465
  await engine.pluginManager.register(async (e) => {
3379
3466
  const instancesManager = await getEmittersInstancesManager(e);
3380
3467
  await addEmittersShapesManager(e);
@@ -3514,21 +3601,28 @@
3514
3601
  });
3515
3602
  }
3516
3603
  clear() {
3604
+ const visited = new Set();
3517
3605
  for (const plugin of this.#canvasClearPlugins) {
3606
+ visited.add(plugin);
3518
3607
  if (plugin.canvasClear?.() ?? false) {
3519
- return;
3608
+ return plugin;
3520
3609
  }
3521
3610
  }
3522
3611
  for (const layer of Object.values(DrawLayer)) {
3523
3612
  if (typeof layer === "number") {
3524
3613
  for (const plugin of this.#getLayerPlugins(layer)) {
3614
+ if (visited.has(plugin)) {
3615
+ continue;
3616
+ }
3617
+ visited.add(plugin);
3525
3618
  if (plugin.canvasClear?.() ?? false) {
3526
- return;
3619
+ return plugin;
3527
3620
  }
3528
3621
  }
3529
3622
  }
3530
3623
  }
3531
3624
  this.canvasClear();
3625
+ return undefined;
3532
3626
  }
3533
3627
  destroy() {
3534
3628
  this.stop();
@@ -3606,8 +3700,7 @@
3606
3700
  });
3607
3701
  }
3608
3702
  drawParticles(delta) {
3609
- const { particles, actualOptions } = this.#container;
3610
- this.clear();
3703
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3611
3704
  particles.update(delta);
3612
3705
  this.draw(ctx => {
3613
3706
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -3629,7 +3722,9 @@
3629
3722
  }
3630
3723
  }
3631
3724
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3632
- plugin.canvasPaint?.();
3725
+ if (plugin !== clearedPlugin) {
3726
+ plugin.canvasPaint?.();
3727
+ }
3633
3728
  }
3634
3729
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3635
3730
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -3661,35 +3756,7 @@
3661
3756
  this.#layers[layer] = [];
3662
3757
  }
3663
3758
  }
3664
- for (const plugin of this.#container.plugins) {
3665
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3666
- this.#colorPlugins.push(plugin);
3667
- }
3668
- if (plugin.drawParticle) {
3669
- this.#drawParticlePlugins.push(plugin);
3670
- }
3671
- if (plugin.drawParticleSetup) {
3672
- this.#drawParticlesSetupPlugins.push(plugin);
3673
- }
3674
- if (plugin.drawParticleCleanup) {
3675
- this.#drawParticlesCleanupPlugins.push(plugin);
3676
- }
3677
- if (plugin.canvasClear) {
3678
- this.#canvasClearPlugins.push(plugin);
3679
- }
3680
- if (plugin.canvasPaint) {
3681
- this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
3682
- }
3683
- if (plugin.drawSettingsSetup) {
3684
- this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
3685
- }
3686
- if (plugin.draw) {
3687
- this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
3688
- }
3689
- if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
3690
- this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
3691
- }
3692
- }
3759
+ this.#initPluginsArray();
3693
3760
  }
3694
3761
  initUpdaters() {
3695
3762
  this.#preDrawUpdaters = [];
@@ -3898,6 +3965,45 @@
3898
3965
  this.#reusablePluginColors[sColorIndex] = sColor;
3899
3966
  return this.#reusablePluginColors;
3900
3967
  }
3968
+ #initLayerPlugin(plugin) {
3969
+ const layerMap = [
3970
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
3971
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
3972
+ [DrawLayer.PluginContent, !!plugin.draw],
3973
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
3974
+ ];
3975
+ for (const [layer, active] of layerMap) {
3976
+ if (active) {
3977
+ this.#layers[layer].push(plugin);
3978
+ }
3979
+ }
3980
+ }
3981
+ #initPluginsArray() {
3982
+ for (const plugin of this.#container.plugins) {
3983
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3984
+ this.#colorPlugins.push(plugin);
3985
+ }
3986
+ if (plugin.drawParticle) {
3987
+ this.#drawParticlePlugins.push(plugin);
3988
+ }
3989
+ if (plugin.drawParticleSetup) {
3990
+ this.#drawParticlesSetupPlugins.push(plugin);
3991
+ }
3992
+ if (plugin.drawParticleCleanup) {
3993
+ this.#drawParticlesCleanupPlugins.push(plugin);
3994
+ }
3995
+ if (plugin.canvasClear) {
3996
+ this.#canvasClearPlugins.push(plugin);
3997
+ }
3998
+ this.#initLayerPlugin(plugin);
3999
+ }
4000
+ }
4001
+ #isSupportedElement(element) {
4002
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
4003
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
4004
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
4005
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
4006
+ }
3901
4007
  #resolveBackgroundElement() {
3902
4008
  const background = this.#container.actualOptions.background;
3903
4009
  this.#backgroundElement = null;
@@ -3907,9 +4013,7 @@
3907
4013
  if (typeof background.element === "string") {
3908
4014
  if (typeof document !== "undefined") {
3909
4015
  const node = document.querySelector(background.element);
3910
- if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
3911
- (typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
3912
- (typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
4016
+ if (node && this.#isSupportedElement(node)) {
3913
4017
  this.#backgroundElement = node;
3914
4018
  }
3915
4019
  else if (node) {
@@ -3920,10 +4024,7 @@
3920
4024
  }
3921
4025
  }
3922
4026
  }
3923
- else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
3924
- (typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
3925
- (typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
3926
- (typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
4027
+ else if (this.#isSupportedElement(background.element)) {
3927
4028
  this.#backgroundElement = background.element;
3928
4029
  }
3929
4030
  }
@@ -3941,7 +4042,7 @@
3941
4042
  return transferredCanvas;
3942
4043
  }
3943
4044
  if (typeof canvas.transferControlToOffscreen !== "function") {
3944
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
4045
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasUnsupported));
3945
4046
  }
3946
4047
  try {
3947
4048
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -3949,10 +4050,16 @@
3949
4050
  return offscreenCanvas;
3950
4051
  }
3951
4052
  catch {
3952
- throw new TypeError("OffscreenCanvas transfer failed");
4053
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasTransferFailed));
3953
4054
  }
3954
4055
  }, isHtmlCanvasElement = (canvas) => {
3955
4056
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
4057
+ }, getEffectiveColorSpace = (context) => {
4058
+ if (!context) {
4059
+ return undefined;
4060
+ }
4061
+ const colorInfo = context;
4062
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
3956
4063
  };
3957
4064
  function setStyle(canvas, style, important = false) {
3958
4065
  if (!style) {
@@ -4077,7 +4184,7 @@
4077
4184
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
4078
4185
  if (color) {
4079
4186
  const hdrOptions = container.actualOptions.hdr;
4080
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
4187
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
4081
4188
  }
4082
4189
  else {
4083
4190
  elementStyle.backgroundColor = "";
@@ -4197,11 +4304,11 @@
4197
4304
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4198
4305
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4199
4306
  safeMatchMedia("(dynamic-range: high)")?.matches;
4200
- container.hdr = canSupportHdr ?? false;
4201
- container.hdrMode = container.actualOptions.hdr.mode;
4307
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4202
4308
  container.peakNits = container.actualOptions.hdr.peakNits;
4203
4309
  const renderCanvas = this.renderCanvas;
4204
4310
  if (!renderCanvas) {
4311
+ container.hdr = canSupportHdr ?? false;
4205
4312
  return;
4206
4313
  }
4207
4314
  this.render.setContextSettings({
@@ -4220,7 +4327,6 @@
4220
4327
  context = null;
4221
4328
  }
4222
4329
  if (canSupportHdr && !context) {
4223
- container.hdr = false;
4224
4330
  const sdrSettings = {
4225
4331
  alpha: true,
4226
4332
  desynchronized: true,
@@ -4236,11 +4342,16 @@
4236
4342
  }
4237
4343
  }
4238
4344
  this.render.setContext(context);
4345
+ if (context) {
4346
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4347
+ if (effectiveColorSpace !== undefined) {
4348
+ container.hdr = effectiveColorSpace === "display-p3";
4349
+ }
4350
+ }
4239
4351
  }
4240
4352
  #initHdrListeners() {
4241
4353
  this.#removeHdrListeners();
4242
4354
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4243
- this.#recreateRenderCanvas();
4244
4355
  this.#initContext();
4245
4356
  this.initBackground();
4246
4357
  }, listeners = [];
@@ -4276,16 +4387,6 @@
4276
4387
  element.style.setProperty(key, value, "important");
4277
4388
  }
4278
4389
  }
4279
- #recreateRenderCanvas() {
4280
- const renderCanvas = this.renderCanvas;
4281
- if (!renderCanvas) {
4282
- return;
4283
- }
4284
- if (this.domElement) {
4285
- return;
4286
- }
4287
- this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
4288
- }
4289
4390
  #removeHdrListeners() {
4290
4391
  if (!this.#hdrMediaListeners) {
4291
4392
  return;
@@ -4384,8 +4485,7 @@
4384
4485
  this.#resizeTimeout = undefined;
4385
4486
  }
4386
4487
  const handleResize = async () => {
4387
- const canvas = this.#container.canvas;
4388
- await canvas.windowResize();
4488
+ await this.#container.canvas.windowResize();
4389
4489
  };
4390
4490
  this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
4391
4491
  }
@@ -4413,8 +4513,8 @@
4413
4513
  }
4414
4514
  else if (!this.#resizeObserver && add && canvasEl) {
4415
4515
  this.#resizeObserver = new ResizeObserver((entries) => {
4416
- const entry = entries.find(e => e.target === canvasEl);
4417
- if (!entry) {
4516
+ const res = entries.some(e => e.target === canvasEl);
4517
+ if (!res) {
4418
4518
  return;
4419
4519
  }
4420
4520
  this.#handleWindowResize();
@@ -4678,10 +4778,10 @@
4678
4778
  return this.#modifiers.find(m => m.id === id);
4679
4779
  }
4680
4780
  getOpacity() {
4681
- 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;
4682
- this.#cachedOpacityData.fillOpacity = fillOpacity;
4781
+ 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;
4782
+ this.#cachedOpacityData.fillOpacity = this.fillOpacity ?? defaultOpacity$1;
4683
4783
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
4684
- this.#cachedOpacityData.strokeOpacity = strokeOpacity;
4784
+ this.#cachedOpacityData.strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
4685
4785
  return this.#cachedOpacityData;
4686
4786
  }
4687
4787
  getPosition() {
@@ -4928,7 +5028,7 @@
4928
5028
  #initPosition(position) {
4929
5029
  const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
4930
5030
  if (!initialPosition) {
4931
- throw new Error("a valid position cannot be found for particle");
5031
+ throw new Error(getErrorMessage(ErrorCodes$2.particlePositionNotFound));
4932
5032
  }
4933
5033
  this.position = initialPosition;
4934
5034
  this.initialPosition = this.position.copy();
@@ -4977,6 +5077,12 @@
4977
5077
  this.#circlePoolIdx = 0;
4978
5078
  this.#rectanglePoolIdx = 0;
4979
5079
  }
5080
+ get cellSize() {
5081
+ return this.#cellSize;
5082
+ }
5083
+ set cellSize(value) {
5084
+ this.#pendingCellSize = value;
5085
+ }
4980
5086
  clear() {
4981
5087
  this.#cells.clear();
4982
5088
  const pendingCellSize = this.#pendingCellSize;
@@ -5000,18 +5106,7 @@
5000
5106
  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);
5001
5107
  for (let cx = minCellX; cx <= maxCellX; cx++) {
5002
5108
  for (let cy = minCellY; cy <= maxCellY; cy++) {
5003
- const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
5004
- if (!cellParticles) {
5005
- continue;
5006
- }
5007
- for (const p of cellParticles) {
5008
- if (check && !check(p)) {
5009
- continue;
5010
- }
5011
- if (range.contains(p.getPosition())) {
5012
- out.push(p);
5013
- }
5014
- }
5109
+ this.#queryCell(cx, cy, range, check, out);
5015
5110
  }
5016
5111
  }
5017
5112
  return out;
@@ -5026,14 +5121,23 @@
5026
5121
  this.#releaseShapes();
5027
5122
  return result;
5028
5123
  }
5029
- setCellSize(cellSize) {
5030
- this.#pendingCellSize = cellSize;
5031
- }
5032
5124
  #acquireCircle(x, y, r) {
5033
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
5125
+ const idx = this.#circlePoolIdx++;
5126
+ let circle = this.#circlePool[idx];
5127
+ if (!circle) {
5128
+ circle = new Circle(x, y, r);
5129
+ this.#circlePool[idx] = circle;
5130
+ }
5131
+ return circle.reset(x, y, r);
5034
5132
  }
5035
5133
  #acquireRectangle(x, y, w, h) {
5036
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
5134
+ const idx = this.#rectanglePoolIdx++;
5135
+ let rectangle = this.#rectanglePool[idx];
5136
+ if (!rectangle) {
5137
+ rectangle = new Rectangle(x, y, w, h);
5138
+ this.#rectanglePool[idx] = rectangle;
5139
+ }
5140
+ return rectangle.reset(x, y, w, h);
5037
5141
  }
5038
5142
  #cellKeyFromCoords(x, y) {
5039
5143
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -5060,6 +5164,20 @@
5060
5164
  }
5061
5165
  return null;
5062
5166
  }
5167
+ #queryCell(cx, cy, range, check, out) {
5168
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5169
+ if (!cellParticles) {
5170
+ return;
5171
+ }
5172
+ for (const p of cellParticles) {
5173
+ if (check && !check(p)) {
5174
+ continue;
5175
+ }
5176
+ if (range.contains(p.getPosition())) {
5177
+ out.push(p);
5178
+ }
5179
+ }
5180
+ }
5063
5181
  #releaseShapes() {
5064
5182
  this.#circlePoolIdx = 0;
5065
5183
  this.#rectanglePoolIdx = 0;
@@ -5068,9 +5186,9 @@
5068
5186
 
5069
5187
  class ParticlesManager {
5070
5188
  checkParticlePositionPlugins;
5071
- grid;
5072
5189
  #array;
5073
5190
  #container;
5191
+ #grid;
5074
5192
  #groupLimits;
5075
5193
  #limit;
5076
5194
  #nextId;
@@ -5094,7 +5212,7 @@
5094
5212
  this.#groupLimits = new Map();
5095
5213
  this.#particleBuckets = new Map();
5096
5214
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
5097
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5215
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
5098
5216
  this.checkParticlePositionPlugins = [];
5099
5217
  this.#particleResetPlugins = [];
5100
5218
  this.#particleUpdatePlugins = [];
@@ -5105,23 +5223,12 @@
5105
5223
  get count() {
5106
5224
  return this.#array.length;
5107
5225
  }
5226
+ get grid() {
5227
+ return this.#grid;
5228
+ }
5108
5229
  addParticle(position, overrideOptions, group, initializer) {
5109
- const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
5110
- if (limit > minLimit) {
5111
- switch (limitMode) {
5112
- case LimitMode.delete: {
5113
- const countToRemove = currentCount + countOffset - limit;
5114
- if (countToRemove > minCount) {
5115
- this.removeQuantity(countToRemove);
5116
- }
5117
- break;
5118
- }
5119
- case LimitMode.wait:
5120
- if (currentCount >= limit) {
5121
- return;
5122
- }
5123
- break;
5124
- }
5230
+ if (this.#checkAndApplyLimits(group, this.count)) {
5231
+ return;
5125
5232
  }
5126
5233
  try {
5127
5234
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5185,7 +5292,7 @@
5185
5292
  return this.#array[index];
5186
5293
  }
5187
5294
  async init() {
5188
- const container = this.#container, options = container.actualOptions;
5295
+ const container = this.#container;
5189
5296
  this.checkParticlePositionPlugins = [];
5190
5297
  this.#updatePlugins = [];
5191
5298
  this.#particleUpdatePlugins = [];
@@ -5194,59 +5301,16 @@
5194
5301
  this.#postParticleUpdatePlugins = [];
5195
5302
  this.#particleBuckets.clear();
5196
5303
  this.#resetBuckets(container.zLayers);
5197
- this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5198
- for (const plugin of container.plugins) {
5199
- if (plugin.redrawInit) {
5200
- await plugin.redrawInit();
5201
- }
5202
- if (plugin.checkParticlePosition) {
5203
- this.checkParticlePositionPlugins.push(plugin);
5204
- }
5205
- if (plugin.update) {
5206
- this.#updatePlugins.push(plugin);
5207
- }
5208
- if (plugin.particleUpdate) {
5209
- this.#particleUpdatePlugins.push(plugin);
5210
- }
5211
- if (plugin.postUpdate) {
5212
- this.#postUpdatePlugins.push(plugin);
5213
- }
5214
- if (plugin.particleReset) {
5215
- this.#particleResetPlugins.push(plugin);
5216
- }
5217
- if (plugin.postParticleUpdate) {
5218
- this.#postParticleUpdatePlugins.push(plugin);
5219
- }
5220
- }
5221
- await this.#container.initDrawersAndUpdaters();
5222
- for (const drawer of this.#container.effectDrawers.values()) {
5223
- await drawer.init?.(container);
5224
- }
5225
- for (const drawer of this.#container.shapeDrawers.values()) {
5226
- await drawer.init?.(container);
5227
- }
5228
- let handled = false;
5229
- for (const plugin of container.plugins) {
5230
- handled = plugin.particlesInitialization?.() ?? handled;
5231
- if (handled) {
5232
- break;
5233
- }
5234
- }
5235
- if (!handled) {
5236
- const particlesOptions = options.particles, groups = particlesOptions.groups;
5237
- for (const group in groups) {
5238
- const groupOptions = groups[group];
5239
- if (!groupOptions) {
5240
- continue;
5241
- }
5242
- for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5243
- this.addParticle(undefined, groupOptions, group);
5244
- }
5245
- }
5246
- for (let i = this.count; i < particlesOptions.number.value; i++) {
5247
- this.addParticle();
5304
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5305
+ this.#applyLimits(container.actualOptions.particles);
5306
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5307
+ for (const group in groups) {
5308
+ const groupData = groups[group];
5309
+ if (groupData) {
5310
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5248
5311
  }
5249
5312
  }
5313
+ await this.#initPlugins();
5250
5314
  }
5251
5315
  push(nb, position, overrideOptions, group) {
5252
5316
  for (let i = 0; i < nb; i++) {
@@ -5261,13 +5325,13 @@
5261
5325
  remove(particle, group, override) {
5262
5326
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5263
5327
  }
5264
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5328
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5265
5329
  if (index < minIndex || index > this.count) {
5266
5330
  return;
5267
5331
  }
5268
5332
  let deleted = 0;
5269
5333
  for (let i = index; deleted < quantity && i < this.count; i++) {
5270
- if (this.#removeParticle(i, group, override)) {
5334
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5271
5335
  i--;
5272
5336
  deleted++;
5273
5337
  }
@@ -5298,7 +5362,7 @@
5298
5362
  this.#resizeFactor = factor;
5299
5363
  }
5300
5364
  update(delta) {
5301
- this.grid.clear();
5365
+ this.#grid.clear();
5302
5366
  for (const plugin of this.#updatePlugins) {
5303
5367
  plugin.update?.(delta);
5304
5368
  }
@@ -5309,7 +5373,7 @@
5309
5373
  this.#updateParticlesPhase2(delta, particlesToDelete);
5310
5374
  if (particlesToDelete.size) {
5311
5375
  for (const particle of particlesToDelete) {
5312
- this.remove(particle);
5376
+ this.remove(particle, particle.group);
5313
5377
  }
5314
5378
  }
5315
5379
  this.#resizeFactor = undefined;
@@ -5320,12 +5384,7 @@
5320
5384
  #applyDensity(options, pluginsCount, group, groupOptions) {
5321
5385
  const numberOptions = options.number;
5322
5386
  if (!numberOptions.density.enable) {
5323
- if (group === undefined) {
5324
- this.#limit = numberOptions.limit.value;
5325
- }
5326
- else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
5327
- this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
5328
- }
5387
+ this.#applyLimits(options, group, groupOptions);
5329
5388
  return;
5330
5389
  }
5331
5390
  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);
@@ -5333,7 +5392,10 @@
5333
5392
  this.#limit = numberOptions.limit.value * densityFactor;
5334
5393
  }
5335
5394
  else {
5336
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5395
+ this.#groupLimits.set(group, {
5396
+ limit: numberOptions.limit.value * densityFactor,
5397
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5398
+ });
5337
5399
  }
5338
5400
  if (particlesCount < particlesNumber) {
5339
5401
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5342,6 +5404,55 @@
5342
5404
  this.removeQuantity(particlesCount - particlesNumber, group);
5343
5405
  }
5344
5406
  }
5407
+ #applyLimits(options, group, groupOptions) {
5408
+ if (group === undefined) {
5409
+ this.#limit = options.number.limit.value;
5410
+ const groups = this.#container.actualOptions.particles.groups;
5411
+ for (const existingGroup of this.#groupLimits.keys()) {
5412
+ if (!(existingGroup in groups)) {
5413
+ this.#groupLimits.delete(existingGroup);
5414
+ }
5415
+ }
5416
+ }
5417
+ else {
5418
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5419
+ if (limitValue > minLimit) {
5420
+ this.#groupLimits.set(group, {
5421
+ limit: limitValue,
5422
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5423
+ });
5424
+ }
5425
+ else {
5426
+ this.#groupLimits.delete(group);
5427
+ }
5428
+ }
5429
+ }
5430
+ #checkAndApplyLimits(group, currentCount) {
5431
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5432
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5433
+ return true;
5434
+ }
5435
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5436
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5437
+ const groupCount = this.filter(t => t.group === group).length;
5438
+ if (groupCount >= groupLimitEntry.limit) {
5439
+ return true;
5440
+ }
5441
+ }
5442
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5443
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5444
+ if (groupCountToRemove > minCount) {
5445
+ this.removeQuantity(groupCountToRemove, group);
5446
+ }
5447
+ }
5448
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5449
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5450
+ if (globalCountToRemove > minCount) {
5451
+ this.#removeAny(globalCountToRemove);
5452
+ }
5453
+ }
5454
+ return false;
5455
+ }
5345
5456
  #createBuckets(zLayers) {
5346
5457
  const bucketCount = Math.max(Math.floor(zLayers), one);
5347
5458
  return Array.from({ length: bucketCount }, () => []);
@@ -5364,6 +5475,70 @@
5364
5475
  }
5365
5476
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5366
5477
  }
5478
+ async #initDrawersAndUpdaters() {
5479
+ const container = this.#container;
5480
+ await container.initDrawersAndUpdaters();
5481
+ for (const drawer of container.effectDrawers.values()) {
5482
+ await drawer.init?.(container);
5483
+ }
5484
+ for (const drawer of container.shapeDrawers.values()) {
5485
+ await drawer.init?.(container);
5486
+ }
5487
+ }
5488
+ async #initPlugins() {
5489
+ await this.#initPluginsArrays();
5490
+ await this.#initDrawersAndUpdaters();
5491
+ let handled = false;
5492
+ const container = this.#container;
5493
+ for (const plugin of container.plugins) {
5494
+ handled = plugin.particlesInitialization?.() ?? handled;
5495
+ if (handled) {
5496
+ break;
5497
+ }
5498
+ }
5499
+ if (handled) {
5500
+ return;
5501
+ }
5502
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5503
+ for (const group in groups) {
5504
+ const groupOptions = groups[group];
5505
+ if (!groupOptions) {
5506
+ continue;
5507
+ }
5508
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5509
+ this.addParticle(undefined, groupOptions, group);
5510
+ }
5511
+ }
5512
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5513
+ this.addParticle();
5514
+ }
5515
+ }
5516
+ async #initPluginsArrays() {
5517
+ const container = this.#container;
5518
+ for (const plugin of container.plugins) {
5519
+ if (plugin.redrawInit) {
5520
+ await plugin.redrawInit();
5521
+ }
5522
+ if (plugin.checkParticlePosition) {
5523
+ this.checkParticlePositionPlugins.push(plugin);
5524
+ }
5525
+ if (plugin.update) {
5526
+ this.#updatePlugins.push(plugin);
5527
+ }
5528
+ if (plugin.particleUpdate) {
5529
+ this.#particleUpdatePlugins.push(plugin);
5530
+ }
5531
+ if (plugin.postUpdate) {
5532
+ this.#postUpdatePlugins.push(plugin);
5533
+ }
5534
+ if (plugin.particleReset) {
5535
+ this.#particleResetPlugins.push(plugin);
5536
+ }
5537
+ if (plugin.postParticleUpdate) {
5538
+ this.#postParticleUpdatePlugins.push(plugin);
5539
+ }
5540
+ }
5541
+ }
5367
5542
  #insertParticleIntoBucket(particle) {
5368
5543
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5369
5544
  if (!bucket) {
@@ -5372,12 +5547,15 @@
5372
5547
  bucket.push(particle);
5373
5548
  this.#particleBuckets.set(particle.id, bucketIndex);
5374
5549
  }
5375
- #removeParticle(index, group, override) {
5550
+ #removeAny(quantity) {
5551
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5552
+ }
5553
+ #removeParticle(index, group, override, matchGroup = true) {
5376
5554
  const particle = this.#array[index];
5377
5555
  if (!particle) {
5378
5556
  return false;
5379
5557
  }
5380
- if (particle.group !== group) {
5558
+ if (matchGroup && particle.group !== group) {
5381
5559
  return false;
5382
5560
  }
5383
5561
  this.#array.splice(index, deleteCount);
@@ -5464,7 +5642,7 @@
5464
5642
  particlesToDelete.add(particle);
5465
5643
  continue;
5466
5644
  }
5467
- this.grid.insert(particle);
5645
+ this.#grid.insert(particle);
5468
5646
  }
5469
5647
  return particlesToDelete;
5470
5648
  }
@@ -5640,7 +5818,6 @@
5640
5818
  this.shapeDrawers = new Map();
5641
5819
  this.particleUpdaters = [];
5642
5820
  this.plugins.length = 0;
5643
- this.#pluginManager.clearPlugins(this);
5644
5821
  this.destroyed = true;
5645
5822
  this.#onDestroy(remove);
5646
5823
  this.dispatchEvent(EventType.containerDestroyed);
@@ -5693,31 +5870,14 @@
5693
5870
  await this.initDrawersAndUpdaters();
5694
5871
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
5695
5872
  this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
5696
- this.plugins.length = 0;
5697
- this.particleDestroyedPlugins.length = 0;
5698
- this.particleCreatedPlugins.length = 0;
5699
- this.particlePositionPlugins.length = 0;
5700
- for (const [plugin, containerPlugin] of allContainerPlugins) {
5701
- if (plugin.needsPlugin(this.actualOptions)) {
5702
- this.plugins.push(containerPlugin);
5703
- if (containerPlugin.particleCreated) {
5704
- this.particleCreatedPlugins.push(containerPlugin);
5705
- }
5706
- if (containerPlugin.particleDestroyed) {
5707
- this.particleDestroyedPlugins.push(containerPlugin);
5708
- }
5709
- if (containerPlugin.particlePosition) {
5710
- this.particlePositionPlugins.push(containerPlugin);
5711
- }
5712
- }
5713
- }
5873
+ this.#initPlugins(allContainerPlugins);
5714
5874
  this.retina.init();
5715
5875
  this.canvas.init();
5716
5876
  this.updateActualOptions();
5717
5877
  this.canvas.initBackground();
5718
5878
  this.canvas.resize();
5719
5879
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
5720
- this.hdrMode = hdrOptions.mode;
5880
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
5721
5881
  this.peakNits = hdrOptions.peakNits;
5722
5882
  this.zLayers = zLayers;
5723
5883
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -5738,9 +5898,9 @@
5738
5898
  }
5739
5899
  async initDrawersAndUpdaters() {
5740
5900
  const pluginManager = this.#pluginManager;
5741
- this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
5742
- this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
5743
- this.particleUpdaters = await pluginManager.getUpdaters(this, true);
5901
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
5902
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
5903
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
5744
5904
  }
5745
5905
  pause() {
5746
5906
  if (!guardCheck(this)) {
@@ -5851,6 +6011,27 @@
5851
6011
  }
5852
6012
  return refresh;
5853
6013
  }
6014
+ #initPlugins(allContainerPlugins) {
6015
+ this.plugins.length = 0;
6016
+ this.particleDestroyedPlugins.length = 0;
6017
+ this.particleCreatedPlugins.length = 0;
6018
+ this.particlePositionPlugins.length = 0;
6019
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
6020
+ if (!plugin.needsPlugin(this.actualOptions)) {
6021
+ continue;
6022
+ }
6023
+ this.plugins.push(containerPlugin);
6024
+ if (containerPlugin.particleCreated) {
6025
+ this.particleCreatedPlugins.push(containerPlugin);
6026
+ }
6027
+ if (containerPlugin.particleDestroyed) {
6028
+ this.particleDestroyedPlugins.push(containerPlugin);
6029
+ }
6030
+ if (containerPlugin.particlePosition) {
6031
+ this.particlePositionPlugins.push(containerPlugin);
6032
+ }
6033
+ }
6034
+ }
5854
6035
  #nextFrame(timestamp) {
5855
6036
  try {
5856
6037
  if (!this.#smooth &&
@@ -6127,11 +6308,11 @@
6127
6308
  }
6128
6309
  }
6129
6310
  async #init() {
6130
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6131
- if (!availablePathGenerators) {
6311
+ const pluginManager = this.#pluginManager;
6312
+ if (!pluginManager.initializers.pathGenerators) {
6132
6313
  return;
6133
6314
  }
6134
- this.availablePathGenerators = availablePathGenerators;
6315
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6135
6316
  this.pathGenerators = new Map();
6136
6317
  for (const pathGenerator of this.pathGenerators.values()) {
6137
6318
  pathGenerator.init();
@@ -6152,6 +6333,11 @@
6152
6333
  this.#container = container;
6153
6334
  this.#instancesManager.initContainer(container);
6154
6335
  }
6336
+ draw(context, _delta) {
6337
+ for (const emitter of this.#instancesManager.getArray(this.#container)) {
6338
+ emitter.draw(context);
6339
+ }
6340
+ }
6155
6341
  async init() {
6156
6342
  const emittersOptions = this.#container.actualOptions.emitters;
6157
6343
  if (isArray(emittersOptions)) {
@@ -6222,14 +6408,21 @@
6222
6408
  this.#pluginManager = pluginManager;
6223
6409
  }
6224
6410
  async addEmitter(container, options, position) {
6225
- const emitterOptions = new Emitter();
6226
- emitterOptions.load(options);
6227
- const { EmitterInstance } = await Promise.resolve().then(function () { return EmitterInstance$1; }), emitter = new EmitterInstance(this.#pluginManager, container, (emitter) => {
6228
- this.removeEmitter(container, emitter);
6229
- }, emitterOptions, position);
6230
- await emitter.init();
6231
- this.getArray(container).push(emitter);
6232
- return emitter;
6411
+ return this.addEmitters(container, [{ options, position }]).then(t => t[defaultIndex]);
6412
+ }
6413
+ async addEmitters(container, emitters) {
6414
+ const { EmitterInstance } = await Promise.resolve().then(function () { return EmitterInstance$1; }), res = [];
6415
+ for (const { options, position } of emitters) {
6416
+ const emitterOptions = new Emitter();
6417
+ emitterOptions.load(options);
6418
+ const emitter = new EmitterInstance(this.#pluginManager, container, (emitter) => {
6419
+ this.removeEmitter(container, emitter);
6420
+ }, emitterOptions, position);
6421
+ await emitter.init();
6422
+ this.getArray(container).push(emitter);
6423
+ res.push(emitter);
6424
+ }
6425
+ return res;
6233
6426
  }
6234
6427
  clear(container) {
6235
6428
  this.initContainer(container);
@@ -6256,6 +6449,7 @@
6256
6449
  : array.find(t => t.name === idxOrName);
6257
6450
  };
6258
6451
  container.addEmitter = async (options, position) => this.addEmitter(container, options, position);
6452
+ container.addEmitters = async (emitters) => this.addEmitters(container, emitters);
6259
6453
  container.removeEmitter = (idxOrName) => {
6260
6454
  const emitter = container.getEmitter?.(idxOrName);
6261
6455
  if (emitter) {
@@ -6276,7 +6470,7 @@
6276
6470
  };
6277
6471
  }
6278
6472
  removeEmitter(container, emitter) {
6279
- const index = this.getArray(container).indexOf(emitter), minIndex = 0, deleteCount = 1;
6473
+ const index = this.getArray(container).indexOf(emitter), deleteCount = 1;
6280
6474
  if (index >= minIndex) {
6281
6475
  this.getArray(container).splice(index, deleteCount);
6282
6476
  }
@@ -6331,6 +6525,7 @@
6331
6525
  #particlesOptions;
6332
6526
  #paused;
6333
6527
  #pluginManager;
6528
+ #positionOverride = false;
6334
6529
  #removeCallback;
6335
6530
  #resizeObserver;
6336
6531
  #shape;
@@ -6407,6 +6602,31 @@
6407
6602
  });
6408
6603
  this.play();
6409
6604
  }
6605
+ draw(context) {
6606
+ if (!this.options.draw || !this.#shape) {
6607
+ return;
6608
+ }
6609
+ const container = this.#container, fillEnabled = this.options.spawn.fill?.enable ?? true;
6610
+ context.save();
6611
+ context.beginPath();
6612
+ if (fillEnabled) {
6613
+ context.fillStyle = this.spawnFillColor
6614
+ ? getStyleFromHsl(this.spawnFillColor, container.hdr, this.spawnFillOpacity ?? defaultOpacity$1, container.peakNits, container.hdrMode)
6615
+ : getStyleFromRgb({ r: 0, g: 0, b: 0 }, container.hdr, defaultOpacity$1, container.peakNits, container.hdrMode);
6616
+ }
6617
+ if (this.spawnStrokeColor) {
6618
+ context.strokeStyle = getStyleFromHsl(this.spawnStrokeColor, container.hdr, this.spawnStrokeOpacity ?? defaultOpacity$1, container.peakNits, container.hdrMode);
6619
+ context.lineWidth = this.spawnStrokeWidth ?? defaultStrokeWidth;
6620
+ }
6621
+ this.#shape.draw?.(context);
6622
+ if (fillEnabled) {
6623
+ context.fill();
6624
+ }
6625
+ if (this.spawnStrokeColor) {
6626
+ context.stroke();
6627
+ }
6628
+ context.restore();
6629
+ }
6410
6630
  externalPause() {
6411
6631
  this.#paused = true;
6412
6632
  this.pause();
@@ -6444,11 +6664,14 @@
6444
6664
  }
6445
6665
  }
6446
6666
  resize() {
6447
- const initialPosition = this.#initialPosition, container = this.#container;
6448
- this.position =
6449
- initialPosition && isPointInside(initialPosition, container.canvas.size, Vector.origin)
6450
- ? initialPosition
6451
- : this.#calcPosition();
6667
+ const container = this.#container;
6668
+ if (!this.#positionOverride) {
6669
+ const initialPosition = this.#initialPosition;
6670
+ this.position =
6671
+ initialPosition && isPointInside(initialPosition, container.canvas.size, Vector.origin)
6672
+ ? initialPosition
6673
+ : this.#calcPosition();
6674
+ }
6452
6675
  this.#size = this.#calcSize();
6453
6676
  this.size =
6454
6677
  this.#size.mode === PixelMode.percent
@@ -6459,6 +6682,11 @@
6459
6682
  : { width: this.#size.width, height: this.#size.height };
6460
6683
  this.#shape?.resize(this.position, this.size);
6461
6684
  }
6685
+ setPosition(position) {
6686
+ this.#positionOverride = true;
6687
+ this.position = { ...position };
6688
+ this.#shape?.resize(this.position, this.size);
6689
+ }
6462
6690
  update(delta) {
6463
6691
  if (this.#paused) {
6464
6692
  return;
@@ -6654,6 +6882,7 @@
6654
6882
  exports.loadBubblesPreset = loadBubblesPreset;
6655
6883
  exports.tsParticles = tsParticles;
6656
6884
 
6657
- }));
6885
+ })(g.__tsParticlesInternals.presets.bubbles = g.__tsParticlesInternals.presets.bubbles || {});
6886
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6887
+
6658
6888
  Object.assign(globalThis.window || globalThis, { loadBubblesPreset: (globalThis.__tsParticlesInternals.presets.bubbles || {}).loadBubblesPreset });
6659
- delete (globalThis.window || globalThis).tsparticlesInternalExports;