@tsparticles/preset-bubbles 4.4.0 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,35 @@
1
- (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.4.0 */
3
- (function (global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.presets = global.__tsParticlesInternals.presets || {}, global.__tsParticlesInternals.presets.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.1 */
4
+ g.__tsParticlesInternals = g.__tsParticlesInternals || {};
5
+ g.__tsParticlesInternals.presets = g.__tsParticlesInternals.presets || {};
6
+ (function (exports) {
7
+ 'use strict';
8
+
9
+ const errorPrefix = "tsParticles - Error", codedErrorPattern = /^\[tsParticles Error (TSP-\d+)\]\s*/, fullMatchIndex = 0, codeIndex = 1;
10
+ const wrap = (fn) => (...args) => {
11
+ fn(...args);
12
+ }, _logger = {
13
+ debug: wrap(console.debug),
14
+ error: (message, ...optionalParams) => {
15
+ if (typeof message === "string") {
16
+ const match = codedErrorPattern.exec(message);
17
+ if (match?.[codeIndex]) {
18
+ console.error(`tsParticles - ${match[codeIndex]} - ${message.slice(match[fullMatchIndex].length)}`, ...optionalParams);
19
+ return;
20
+ }
21
+ }
22
+ console.error(`${errorPrefix} - ${message}`, ...optionalParams);
23
+ },
24
+ info: wrap(console.info),
25
+ log: wrap(console.log),
26
+ trace: wrap(console.trace),
27
+ verbose: wrap(console.log),
28
+ warning: wrap(console.warn),
29
+ };
30
+ function getLogger() {
31
+ return _logger;
32
+ }
19
33
 
20
34
  var MoveDirection;
21
35
  (function (MoveDirection) {
@@ -32,6 +46,17 @@
32
46
  MoveDirection["inside"] = "inside";
33
47
  })(MoveDirection || (MoveDirection = {}));
34
48
 
49
+ const generatedAttribute = "generated", fullPercent = "100%", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
50
+ x: 0,
51
+ y: 0,
52
+ z: 0,
53
+ }, defaultTransform = {
54
+ a: 1,
55
+ b: 0,
56
+ c: 0,
57
+ d: 1,
58
+ }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, minRadius = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity = 1, 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 {
@@ -525,12 +667,8 @@
525
667
  }
526
668
  addEventListener(type, listener) {
527
669
  this.removeEventListener(type, listener);
528
- let arr = this.#listeners.get(type);
529
- if (!arr) {
530
- arr = [];
531
- this.#listeners.set(type, arr);
532
- }
533
- arr.push(listener);
670
+ const arr = this.#listeners.get(type);
671
+ this.#listeners.set(type, arr ? [...arr, listener] : [listener]);
534
672
  }
535
673
  dispatchEvent(type, args) {
536
674
  const listeners = this.#listeners.get(type);
@@ -562,7 +700,7 @@
562
700
  this.#listeners.delete(type);
563
701
  }
564
702
  else {
565
- arr.splice(idx, deleteCount);
703
+ this.#listeners.set(type, [...arr.slice(minIndex, idx), ...arr.slice(idx + deleteCount)]);
566
704
  }
567
705
  }
568
706
  }
@@ -586,7 +724,6 @@
586
724
  class PluginManager {
587
725
  colorManagers = new Map();
588
726
  easingFunctions = new Map();
589
- effectDrawers = new Map();
590
727
  initializers = {
591
728
  effects: new Map(),
592
729
  shapes: new Map(),
@@ -595,8 +732,6 @@
595
732
  palettes = new Map();
596
733
  plugins = [];
597
734
  presets = new Map();
598
- shapeDrawers = new Map();
599
- updaters = new Map();
600
735
  #allLoadersSet = new Set();
601
736
  #configs = new Map();
602
737
  #engine;
@@ -614,57 +749,79 @@
614
749
  }
615
750
  return res;
616
751
  }
752
+ get initialized() {
753
+ return this.#initialized;
754
+ }
617
755
  addColorManager(name, manager) {
756
+ if (this.#initialized) {
757
+ return;
758
+ }
618
759
  this.colorManagers.set(name, manager);
619
760
  }
620
761
  addConfig(config) {
762
+ if (this.#initialized) {
763
+ return;
764
+ }
621
765
  const key = config.key ?? config.name ?? "default";
622
766
  this.#configs.set(key, config);
623
767
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
624
768
  }
625
769
  addEasing(name, easing) {
770
+ if (this.#initialized) {
771
+ return;
772
+ }
626
773
  if (this.easingFunctions.get(name)) {
627
774
  return;
628
775
  }
629
776
  this.easingFunctions.set(name, easing);
630
777
  }
631
778
  addEffect(effect, drawer) {
779
+ if (this.#initialized) {
780
+ return;
781
+ }
632
782
  this.initializers.effects.set(effect, drawer);
633
783
  }
634
784
  addPalette(name, palette) {
785
+ if (this.#initialized) {
786
+ return;
787
+ }
635
788
  this.palettes.set(name, palette);
636
789
  }
637
790
  addParticleUpdater(name, updaterInitializer) {
791
+ if (this.#initialized) {
792
+ return;
793
+ }
638
794
  this.initializers.updaters.set(name, updaterInitializer);
639
795
  }
640
796
  addPlugin(plugin) {
797
+ if (this.#initialized) {
798
+ return;
799
+ }
641
800
  if (this.getPlugin(plugin.id)) {
642
801
  return;
643
802
  }
644
803
  this.plugins.push(plugin);
645
804
  }
646
805
  addPreset(preset, options, override = false) {
806
+ if (this.#initialized) {
807
+ return;
808
+ }
647
809
  if (!(override || !this.getPreset(preset))) {
648
810
  return;
649
811
  }
650
812
  this.presets.set(preset, options);
651
813
  }
652
814
  addShape(shapes, drawer) {
815
+ if (this.#initialized) {
816
+ return;
817
+ }
653
818
  for (const shape of shapes) {
654
819
  this.initializers.shapes.set(shape, drawer);
655
820
  }
656
821
  }
657
- clearPlugins(container) {
658
- this.effectDrawers.delete(container);
659
- this.shapeDrawers.delete(container);
660
- this.updaters.delete(container);
661
- }
662
822
  getEasing(name) {
663
823
  return this.easingFunctions.get(name) ?? ((value) => value);
664
824
  }
665
- getEffectDrawers(container, force = false) {
666
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
667
- }
668
825
  getPalette(name) {
669
826
  return this.palettes.get(name);
670
827
  }
@@ -674,12 +831,6 @@
674
831
  getPreset(preset) {
675
832
  return this.presets.get(preset);
676
833
  }
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
834
  async init() {
684
835
  if (this.#initialized || this.#isRunningLoaders) {
685
836
  return;
@@ -698,16 +849,9 @@
698
849
  this.#initialized = true;
699
850
  }
700
851
  }
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
852
  async register(...loaders) {
709
853
  if (this.#initialized) {
710
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
854
+ throw new Error(getErrorMessage(ErrorCodes$2.pluginRegisterAfterLoad));
711
855
  }
712
856
  for (const loader of loaders) {
713
857
  if (this.#isRunningLoaders) {
@@ -719,88 +863,15 @@
719
863
  }
720
864
  }
721
865
  async #runLoader(loader, executed, allLoaders) {
722
- if (executed.has(loader))
866
+ if (executed.has(loader)) {
723
867
  return;
868
+ }
724
869
  executed.add(loader);
725
870
  allLoaders.add(loader);
726
871
  await loader(this.#engine);
727
872
  }
728
873
  }
729
874
 
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
875
  class Engine {
805
876
  pluginManager = new PluginManager(this);
806
877
  #domArray = [];
@@ -810,7 +881,7 @@
810
881
  return this.#domArray;
811
882
  }
812
883
  get version() {
813
- return "4.4.0";
884
+ return "4.5.1";
814
885
  }
815
886
  addEventListener(type, listener) {
816
887
  this.#eventDispatcher.addEventListener(type, listener);
@@ -819,7 +890,7 @@
819
890
  if (this.version === pluginVersion) {
820
891
  return;
821
892
  }
822
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
893
+ throw new Error(getErrorMessage(ErrorCodes$2.engineVersionMismatch, this.version, pluginVersion));
823
894
  }
824
895
  dispatchEvent(type, args) {
825
896
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1068,20 +1139,6 @@
1068
1139
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1069
1140
  })(DrawLayer || (DrawLayer = {}));
1070
1141
 
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
1142
  function loadProperty(obj, key, value) {
1086
1143
  if (value !== undefined) {
1087
1144
  obj[key] = value;
@@ -1113,6 +1170,20 @@
1113
1170
  }
1114
1171
  }
1115
1172
 
1173
+ class OptionLoader {
1174
+ load(data) {
1175
+ if (isNull(data)) {
1176
+ return;
1177
+ }
1178
+ this.doLoad(data);
1179
+ }
1180
+ }
1181
+ function loadOptions(options, ...sourceOptionsArr) {
1182
+ for (const sourceOptions of sourceOptionsArr) {
1183
+ options.load(sourceOptions);
1184
+ }
1185
+ }
1186
+
1116
1187
  class AnimationOptions extends OptionLoader {
1117
1188
  count = 0;
1118
1189
  decay = 0;
@@ -1263,15 +1334,14 @@
1263
1334
  peakNits = defaultPeakNits;
1264
1335
  doLoad(data) {
1265
1336
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1266
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1337
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrEnableInvalid, String(data.enable)));
1267
1338
  }
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)}"`);
1339
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1340
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1271
1341
  }
1272
1342
  if (data.peakNits !== undefined &&
1273
1343
  (!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)}"`);
1344
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrPeakNitsInvalid, String(data.peakNits)));
1275
1345
  }
1276
1346
  loadProperty(this, "enable", data.enable);
1277
1347
  loadProperty(this, "mode", data.mode);
@@ -1649,12 +1719,10 @@
1649
1719
  plugin.loadParticlesOptions(this.#container, this, data);
1650
1720
  }
1651
1721
  }
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
- }
1722
+ const updaters = this.#container.particleUpdaters;
1723
+ for (const updater of updaters) {
1724
+ if (updater.loadOptions) {
1725
+ updater.loadOptions(this, data);
1658
1726
  }
1659
1727
  }
1660
1728
  }
@@ -1987,9 +2055,9 @@
1987
2055
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1988
2056
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1989
2057
  }
1990
- const temp1 = lNormalized < half
2058
+ const temp2 = lNormalized < half
1991
2059
  ? 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));
2060
+ : 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
2061
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1994
2062
  }
1995
2063
  function hslToRgbFloat(hsl) {
@@ -1998,9 +2066,9 @@
1998
2066
  const grayscaleValue = lNormalized * rgbMax;
1999
2067
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
2000
2068
  }
2001
- const temp1 = lNormalized < half
2069
+ const temp2 = lNormalized < half
2002
2070
  ? 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));
2071
+ : 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
2072
  return { r: red, g: green, b: blue };
2005
2073
  }
2006
2074
  function hslaToRgba(hsla) {
@@ -2214,7 +2282,7 @@
2214
2282
  }
2215
2283
 
2216
2284
  async function loadBlendPlugin(engine) {
2217
- engine.checkVersion("4.4.0");
2285
+ engine.checkVersion("4.5.1");
2218
2286
  await engine.pluginManager.register(e => {
2219
2287
  e.pluginManager.addPlugin(new BlendPlugin());
2220
2288
  });
@@ -2251,7 +2319,7 @@
2251
2319
  }
2252
2320
 
2253
2321
  async function loadCircleShape(engine) {
2254
- engine.checkVersion("4.4.0");
2322
+ engine.checkVersion("4.5.1");
2255
2323
  await engine.pluginManager.register(e => {
2256
2324
  e.pluginManager.addShape(["circle"], () => {
2257
2325
  return Promise.resolve(new CircleDrawer());
@@ -2266,7 +2334,7 @@
2266
2334
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2267
2335
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2268
2336
  })(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;
2337
+ 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
2338
  class HexColorManager {
2271
2339
  accepts(input) {
2272
2340
  return input.startsWith("#");
@@ -2299,7 +2367,7 @@
2299
2367
  }
2300
2368
 
2301
2369
  async function loadHexColorPlugin(engine) {
2302
- engine.checkVersion("4.4.0");
2370
+ engine.checkVersion("4.5.1");
2303
2371
  await engine.pluginManager.register(e => {
2304
2372
  e.pluginManager.addColorManager("hex", new HexColorManager());
2305
2373
  });
@@ -2339,7 +2407,7 @@
2339
2407
  if (!this.accepts(input)) {
2340
2408
  return;
2341
2409
  }
2342
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2410
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2343
2411
  return result
2344
2412
  ? hslaToRgba({
2345
2413
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2352,12 +2420,20 @@
2352
2420
  }
2353
2421
 
2354
2422
  async function loadHslColorPlugin(engine) {
2355
- engine.checkVersion("4.4.0");
2423
+ engine.checkVersion("4.5.1");
2356
2424
  await engine.pluginManager.register(e => {
2357
2425
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2358
2426
  });
2359
2427
  }
2360
2428
 
2429
+ const ErrorCodes$1 = {
2430
+ baseMoverNotLoaded: "TSP-3203",
2431
+ };
2432
+
2433
+ const ErrorMessages$1 = {
2434
+ [ErrorCodes$1.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2435
+ };
2436
+
2361
2437
  class MovePlugin {
2362
2438
  id = "move";
2363
2439
  #pluginManager;
@@ -2376,20 +2452,20 @@
2376
2452
  }
2377
2453
 
2378
2454
  async function loadMovePlugin(engine) {
2379
- engine.checkVersion("4.4.0");
2455
+ engine.checkVersion("4.5.1");
2456
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2457
+ addErrorMessages(ErrorMessages$1);
2458
+ }
2380
2459
  await engine.pluginManager.register(e => {
2381
2460
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2382
2461
  movePluginManager.initializers.pathGenerators ??= new Map();
2383
- movePluginManager.pathGenerators ??= new Map();
2384
2462
  movePluginManager.addPathGenerator = (name, generator) => {
2463
+ if (movePluginManager.initialized) {
2464
+ return;
2465
+ }
2385
2466
  movePluginManager.initializers.pathGenerators ??= new Map();
2386
2467
  movePluginManager.initializers.pathGenerators.set(name, generator);
2387
2468
  };
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
2469
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2394
2470
  });
2395
2471
  }
@@ -2586,7 +2662,7 @@
2586
2662
  }
2587
2663
 
2588
2664
  async function loadOpacityUpdater(engine) {
2589
- engine.checkVersion("4.4.0");
2665
+ engine.checkVersion("4.5.1");
2590
2666
  await engine.pluginManager.register(e => {
2591
2667
  e.pluginManager.addParticleUpdater("opacity", container => {
2592
2668
  return Promise.resolve(new OpacityUpdater(container));
@@ -2595,70 +2671,60 @@
2595
2671
  }
2596
2672
 
2597
2673
  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)) {
2674
+ const bounceAxisConfigs = {
2675
+ [OutModeDirection.left]: {
2676
+ axis: "x",
2677
+ canvas: "width",
2678
+ bounce: "horizontal",
2679
+ negativeEdge: "right",
2680
+ positiveEdge: "left",
2681
+ negative: true,
2682
+ },
2683
+ [OutModeDirection.right]: {
2684
+ axis: "x",
2685
+ canvas: "width",
2686
+ bounce: "horizontal",
2687
+ negativeEdge: "right",
2688
+ positiveEdge: "left",
2689
+ negative: false,
2690
+ },
2691
+ [OutModeDirection.top]: {
2692
+ axis: "y",
2693
+ canvas: "height",
2694
+ bounce: "vertical",
2695
+ negativeEdge: "bottom",
2696
+ positiveEdge: "top",
2697
+ negative: true,
2698
+ },
2699
+ [OutModeDirection.bottom]: {
2700
+ axis: "y",
2701
+ canvas: "height",
2702
+ bounce: "vertical",
2703
+ negativeEdge: "bottom",
2704
+ positiveEdge: "top",
2705
+ negative: false,
2706
+ },
2707
+ };
2708
+ function bounce(data) {
2709
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2635
2710
  return;
2636
2711
  }
2637
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2638
- data.particle.position.y = data.size + data.offset.y;
2712
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2713
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2714
+ position[config.axis] = data.size + data.offset[config.axis];
2639
2715
  }
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;
2716
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2717
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2642
2718
  }
2643
- const velocity = data.particle.velocity.y;
2644
- let bounced = false;
2645
2719
  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;
2720
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2721
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2651
2722
  }
2652
- if (!bounced) {
2723
+ else {
2653
2724
  return;
2654
2725
  }
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
- }
2726
+ const minPos = data.offset[config.axis] + data.size;
2727
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2662
2728
  if (data.outMode === OutMode.split) {
2663
2729
  data.particle.destroy();
2664
2730
  }
@@ -2674,26 +2740,19 @@
2674
2740
  OutMode.bounce,
2675
2741
  OutMode.split,
2676
2742
  ];
2677
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2743
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2678
2744
  }
2679
2745
  update(particle, direction, delta, outMode) {
2680
2746
  if (!this.modes.includes(outMode)) {
2681
2747
  return;
2682
2748
  }
2683
- const container = this.#container;
2684
- let handled = false;
2685
2749
  for (const plugin of this.#particleBouncePlugins) {
2686
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2687
- if (handled) {
2688
- break;
2750
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2751
+ return;
2689
2752
  }
2690
2753
  }
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 });
2754
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2755
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2697
2756
  }
2698
2757
  }
2699
2758
 
@@ -2782,108 +2841,109 @@
2782
2841
  return;
2783
2842
  }
2784
2843
  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;
2844
+ if (particle.outType === ParticleOutType.inside) {
2845
+ this.#applyOutModeInside(particle, container);
2846
+ }
2847
+ else {
2848
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2849
+ return;
2812
2850
  }
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;
2851
+ if (particle.outType === ParticleOutType.outside) {
2852
+ this.#applyParticleModeOutside(particle);
2853
+ }
2854
+ else {
2855
+ this.#applyOutModeNormal(particle, container, direction);
2884
2856
  }
2885
2857
  }
2886
2858
  }
2859
+ #applyOutModeInside(particle, container) {
2860
+ const { x: vx, y: vy } = particle.velocity;
2861
+ updateVector.setTo(originPoint);
2862
+ updateVector.length = particle.moveCenter.radius;
2863
+ updateVector.angle = particle.velocity.angle + Math.PI;
2864
+ updateVector.addTo(particle.moveCenter);
2865
+ const { dx, dy } = getDistances(particle.position, updateVector);
2866
+ if ((vx <= minVelocity && dx >= minDistance) ||
2867
+ (vy <= minVelocity && dy >= minDistance) ||
2868
+ (vx >= minVelocity && dx <= minDistance) ||
2869
+ (vy >= minVelocity && dy <= minDistance)) {
2870
+ return;
2871
+ }
2872
+ particle.position.x = Math.floor(randomInRangeValue({
2873
+ min: 0,
2874
+ max: container.canvas.size.width,
2875
+ }));
2876
+ particle.position.y = Math.floor(randomInRangeValue({
2877
+ min: 0,
2878
+ max: container.canvas.size.height,
2879
+ }));
2880
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2881
+ particle.direction = Math.atan2(-newDy, -newDx);
2882
+ particle.velocity.angle = particle.direction;
2883
+ particle.justWarped = true;
2884
+ }
2885
+ #applyOutModeNormal(particle, container, direction) {
2886
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2887
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2888
+ left: -sizeValue - particle.offset.x,
2889
+ right: canvasSize.width + sizeValue + particle.offset.x,
2890
+ top: -sizeValue - particle.offset.y,
2891
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2892
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2893
+ particle.position.x = newPos.left;
2894
+ particle.initialPosition.x = particle.position.x;
2895
+ if (!warp) {
2896
+ particle.position.y = getRandom() * canvasSize.height;
2897
+ particle.initialPosition.y = particle.position.y;
2898
+ }
2899
+ particle.justWarped = true;
2900
+ }
2901
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2902
+ particle.position.x = newPos.right;
2903
+ particle.initialPosition.x = particle.position.x;
2904
+ if (!warp) {
2905
+ particle.position.y = getRandom() * canvasSize.height;
2906
+ particle.initialPosition.y = particle.position.y;
2907
+ }
2908
+ particle.justWarped = true;
2909
+ }
2910
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2911
+ if (!warp) {
2912
+ particle.position.x = getRandom() * canvasSize.width;
2913
+ particle.initialPosition.x = particle.position.x;
2914
+ }
2915
+ particle.position.y = newPos.top;
2916
+ particle.initialPosition.y = particle.position.y;
2917
+ particle.justWarped = true;
2918
+ }
2919
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2920
+ if (!warp) {
2921
+ particle.position.x = getRandom() * canvasSize.width;
2922
+ particle.initialPosition.x = particle.position.x;
2923
+ }
2924
+ particle.position.y = newPos.bottom;
2925
+ particle.initialPosition.y = particle.position.y;
2926
+ particle.justWarped = true;
2927
+ }
2928
+ }
2929
+ #applyParticleModeOutside(particle) {
2930
+ particle.position.x =
2931
+ Math.floor(randomInRangeValue({
2932
+ min: -particle.moveCenter.radius,
2933
+ max: particle.moveCenter.radius,
2934
+ })) + particle.moveCenter.x;
2935
+ particle.position.y =
2936
+ Math.floor(randomInRangeValue({
2937
+ min: -particle.moveCenter.radius,
2938
+ max: particle.moveCenter.radius,
2939
+ })) + particle.moveCenter.y;
2940
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2941
+ if (particle.moveCenter.radius) {
2942
+ particle.direction = Math.atan2(dy, dx);
2943
+ particle.velocity.angle = particle.direction;
2944
+ }
2945
+ particle.justWarped = true;
2946
+ }
2887
2947
  }
2888
2948
 
2889
2949
  const checkOutMode = (outModes, outMode) => {
@@ -2931,7 +2991,7 @@
2931
2991
  }
2932
2992
 
2933
2993
  async function loadOutModesUpdater(engine) {
2934
- engine.checkVersion("4.4.0");
2994
+ engine.checkVersion("4.5.1");
2935
2995
  await engine.pluginManager.register(e => {
2936
2996
  e.pluginManager.addParticleUpdater("outModes", container => {
2937
2997
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2948,39 +3008,9 @@
2948
3008
  this.#pluginManager = pluginManager;
2949
3009
  }
2950
3010
  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
- }
3011
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3012
+ this.#initFill(paint, particle, container);
3013
+ this.#initStroke(paint, particle, container);
2984
3014
  }
2985
3015
  isEnabled(particle) {
2986
3016
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2999,10 +3029,48 @@
2999
3029
  updateColor(particle.fillColor, delta, this.#container.hdr);
3000
3030
  updateColor(particle.strokeColor, delta, this.#container.hdr);
3001
3031
  }
3032
+ #initFill(paint, particle, container) {
3033
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3034
+ if (!fill) {
3035
+ particle.fillEnabled = false;
3036
+ particle.fillAnimation = undefined;
3037
+ particle.fillColor = undefined;
3038
+ particle.fillOpacity = defaultOpacity;
3039
+ return;
3040
+ }
3041
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3042
+ particle.fillEnabled = fill.enable;
3043
+ particle.fillOpacity = getRangeValue(fill.opacity);
3044
+ particle.fillAnimation = fillColor.animation;
3045
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3046
+ if (!fillHslColor) {
3047
+ return;
3048
+ }
3049
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3050
+ }
3051
+ #initStroke(paint, particle, container) {
3052
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3053
+ if (!stroke) {
3054
+ particle.strokeAnimation = undefined;
3055
+ particle.strokeColor = undefined;
3056
+ particle.strokeOpacity = defaultOpacity;
3057
+ particle.strokeWidth = 0;
3058
+ return;
3059
+ }
3060
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3061
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3062
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3063
+ particle.strokeAnimation = strokeColor.animation;
3064
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3065
+ if (!strokeHslColor) {
3066
+ return;
3067
+ }
3068
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3069
+ }
3002
3070
  }
3003
3071
 
3004
3072
  async function loadPaintUpdater(engine) {
3005
- engine.checkVersion("4.4.0");
3073
+ engine.checkVersion("4.5.1");
3006
3074
  await engine.pluginManager.register(e => {
3007
3075
  e.pluginManager.addParticleUpdater("paint", container => {
3008
3076
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3044,7 +3112,7 @@
3044
3112
  if (!this.accepts(input)) {
3045
3113
  return;
3046
3114
  }
3047
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3115
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3048
3116
  return result
3049
3117
  ? {
3050
3118
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3057,7 +3125,7 @@
3057
3125
  }
3058
3126
 
3059
3127
  async function loadRgbColorPlugin(engine) {
3060
- engine.checkVersion("4.4.0");
3128
+ engine.checkVersion("4.5.1");
3061
3129
  await engine.pluginManager.register(e => {
3062
3130
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3063
3131
  });
@@ -3142,7 +3210,7 @@
3142
3210
  }
3143
3211
 
3144
3212
  async function loadSizeUpdater(engine) {
3145
- engine.checkVersion("4.4.0");
3213
+ engine.checkVersion("4.5.1");
3146
3214
  await engine.pluginManager.register(e => {
3147
3215
  e.pluginManager.addParticleUpdater("size", container => {
3148
3216
  return Promise.resolve(new SizeUpdater(container));
@@ -3151,7 +3219,7 @@
3151
3219
  }
3152
3220
 
3153
3221
  async function loadBasic(engine) {
3154
- engine.checkVersion("4.4.0");
3222
+ engine.checkVersion("4.5.1");
3155
3223
  await engine.pluginManager.register(async (e) => {
3156
3224
  await Promise.all([
3157
3225
  loadBlendPlugin(e),
@@ -3260,6 +3328,8 @@
3260
3328
  autoPlay = true;
3261
3329
  direction;
3262
3330
  domId;
3331
+ draggable = false;
3332
+ draw = false;
3263
3333
  fill = true;
3264
3334
  life = new EmitterLife();
3265
3335
  name;
@@ -3283,6 +3353,8 @@
3283
3353
  }
3284
3354
  loadProperty(this, "direction", data.direction);
3285
3355
  loadProperty(this, "domId", data.domId);
3356
+ loadProperty(this, "draggable", data.draggable);
3357
+ loadProperty(this, "draw", data.draw);
3286
3358
  loadProperty(this, "fill", data.fill);
3287
3359
  this.life.load(data.life);
3288
3360
  loadProperty(this, "name", data.name);
@@ -3344,6 +3416,14 @@
3344
3416
  }
3345
3417
  }
3346
3418
 
3419
+ const ErrorCodes = {
3420
+ emittersPluginNotLoaded: "TSP-3101",
3421
+ };
3422
+
3423
+ const ErrorMessages = {
3424
+ [ErrorCodes.emittersPluginNotLoaded]: "tsParticles Emitters Plugin is not loaded",
3425
+ };
3426
+
3347
3427
  async function addEmittersShapesManager(e) {
3348
3428
  const { ShapeManager } = await Promise.resolve().then(function () { return ShapeManager$1; }), pluginManager = e.pluginManager;
3349
3429
  pluginManager.emitterShapeManager ??= new ShapeManager();
@@ -3374,7 +3454,10 @@
3374
3454
  })(EmitterClickMode || (EmitterClickMode = {}));
3375
3455
 
3376
3456
  async function loadEmittersPluginSimple(engine) {
3377
- engine.checkVersion("4.4.0");
3457
+ engine.checkVersion("4.5.1");
3458
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
3459
+ addErrorMessages(ErrorMessages);
3460
+ }
3378
3461
  await engine.pluginManager.register(async (e) => {
3379
3462
  const instancesManager = await getEmittersInstancesManager(e);
3380
3463
  await addEmittersShapesManager(e);
@@ -3514,21 +3597,28 @@
3514
3597
  });
3515
3598
  }
3516
3599
  clear() {
3600
+ const visited = new Set();
3517
3601
  for (const plugin of this.#canvasClearPlugins) {
3602
+ visited.add(plugin);
3518
3603
  if (plugin.canvasClear?.() ?? false) {
3519
- return;
3604
+ return plugin;
3520
3605
  }
3521
3606
  }
3522
3607
  for (const layer of Object.values(DrawLayer)) {
3523
3608
  if (typeof layer === "number") {
3524
3609
  for (const plugin of this.#getLayerPlugins(layer)) {
3610
+ if (visited.has(plugin)) {
3611
+ continue;
3612
+ }
3613
+ visited.add(plugin);
3525
3614
  if (plugin.canvasClear?.() ?? false) {
3526
- return;
3615
+ return plugin;
3527
3616
  }
3528
3617
  }
3529
3618
  }
3530
3619
  }
3531
3620
  this.canvasClear();
3621
+ return undefined;
3532
3622
  }
3533
3623
  destroy() {
3534
3624
  this.stop();
@@ -3606,8 +3696,7 @@
3606
3696
  });
3607
3697
  }
3608
3698
  drawParticles(delta) {
3609
- const { particles, actualOptions } = this.#container;
3610
- this.clear();
3699
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3611
3700
  particles.update(delta);
3612
3701
  this.draw(ctx => {
3613
3702
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -3629,7 +3718,9 @@
3629
3718
  }
3630
3719
  }
3631
3720
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3632
- plugin.canvasPaint?.();
3721
+ if (plugin !== clearedPlugin) {
3722
+ plugin.canvasPaint?.();
3723
+ }
3633
3724
  }
3634
3725
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3635
3726
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -3661,35 +3752,7 @@
3661
3752
  this.#layers[layer] = [];
3662
3753
  }
3663
3754
  }
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
- }
3755
+ this.#initPluginsArray();
3693
3756
  }
3694
3757
  initUpdaters() {
3695
3758
  this.#preDrawUpdaters = [];
@@ -3898,6 +3961,45 @@
3898
3961
  this.#reusablePluginColors[sColorIndex] = sColor;
3899
3962
  return this.#reusablePluginColors;
3900
3963
  }
3964
+ #initLayerPlugin(plugin) {
3965
+ const layerMap = [
3966
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
3967
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
3968
+ [DrawLayer.PluginContent, !!plugin.draw],
3969
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
3970
+ ];
3971
+ for (const [layer, active] of layerMap) {
3972
+ if (active) {
3973
+ this.#layers[layer].push(plugin);
3974
+ }
3975
+ }
3976
+ }
3977
+ #initPluginsArray() {
3978
+ for (const plugin of this.#container.plugins) {
3979
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3980
+ this.#colorPlugins.push(plugin);
3981
+ }
3982
+ if (plugin.drawParticle) {
3983
+ this.#drawParticlePlugins.push(plugin);
3984
+ }
3985
+ if (plugin.drawParticleSetup) {
3986
+ this.#drawParticlesSetupPlugins.push(plugin);
3987
+ }
3988
+ if (plugin.drawParticleCleanup) {
3989
+ this.#drawParticlesCleanupPlugins.push(plugin);
3990
+ }
3991
+ if (plugin.canvasClear) {
3992
+ this.#canvasClearPlugins.push(plugin);
3993
+ }
3994
+ this.#initLayerPlugin(plugin);
3995
+ }
3996
+ }
3997
+ #isSupportedElement(element) {
3998
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
3999
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
4000
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
4001
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
4002
+ }
3901
4003
  #resolveBackgroundElement() {
3902
4004
  const background = this.#container.actualOptions.background;
3903
4005
  this.#backgroundElement = null;
@@ -3907,9 +4009,7 @@
3907
4009
  if (typeof background.element === "string") {
3908
4010
  if (typeof document !== "undefined") {
3909
4011
  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)) {
4012
+ if (node && this.#isSupportedElement(node)) {
3913
4013
  this.#backgroundElement = node;
3914
4014
  }
3915
4015
  else if (node) {
@@ -3920,10 +4020,7 @@
3920
4020
  }
3921
4021
  }
3922
4022
  }
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)) {
4023
+ else if (this.#isSupportedElement(background.element)) {
3927
4024
  this.#backgroundElement = background.element;
3928
4025
  }
3929
4026
  }
@@ -3941,7 +4038,7 @@
3941
4038
  return transferredCanvas;
3942
4039
  }
3943
4040
  if (typeof canvas.transferControlToOffscreen !== "function") {
3944
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
4041
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasUnsupported));
3945
4042
  }
3946
4043
  try {
3947
4044
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -3949,10 +4046,16 @@
3949
4046
  return offscreenCanvas;
3950
4047
  }
3951
4048
  catch {
3952
- throw new TypeError("OffscreenCanvas transfer failed");
4049
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasTransferFailed));
3953
4050
  }
3954
4051
  }, isHtmlCanvasElement = (canvas) => {
3955
4052
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
4053
+ }, getEffectiveColorSpace = (context) => {
4054
+ if (!context) {
4055
+ return undefined;
4056
+ }
4057
+ const colorInfo = context;
4058
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
3956
4059
  };
3957
4060
  function setStyle(canvas, style, important = false) {
3958
4061
  if (!style) {
@@ -4077,7 +4180,7 @@
4077
4180
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
4078
4181
  if (color) {
4079
4182
  const hdrOptions = container.actualOptions.hdr;
4080
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
4183
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
4081
4184
  }
4082
4185
  else {
4083
4186
  elementStyle.backgroundColor = "";
@@ -4197,11 +4300,11 @@
4197
4300
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4198
4301
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4199
4302
  safeMatchMedia("(dynamic-range: high)")?.matches;
4200
- container.hdr = canSupportHdr ?? false;
4201
- container.hdrMode = container.actualOptions.hdr.mode;
4303
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4202
4304
  container.peakNits = container.actualOptions.hdr.peakNits;
4203
4305
  const renderCanvas = this.renderCanvas;
4204
4306
  if (!renderCanvas) {
4307
+ container.hdr = canSupportHdr ?? false;
4205
4308
  return;
4206
4309
  }
4207
4310
  this.render.setContextSettings({
@@ -4220,7 +4323,6 @@
4220
4323
  context = null;
4221
4324
  }
4222
4325
  if (canSupportHdr && !context) {
4223
- container.hdr = false;
4224
4326
  const sdrSettings = {
4225
4327
  alpha: true,
4226
4328
  desynchronized: true,
@@ -4236,11 +4338,16 @@
4236
4338
  }
4237
4339
  }
4238
4340
  this.render.setContext(context);
4341
+ if (context) {
4342
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4343
+ if (effectiveColorSpace !== undefined) {
4344
+ container.hdr = effectiveColorSpace === "display-p3";
4345
+ }
4346
+ }
4239
4347
  }
4240
4348
  #initHdrListeners() {
4241
4349
  this.#removeHdrListeners();
4242
4350
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4243
- this.#recreateRenderCanvas();
4244
4351
  this.#initContext();
4245
4352
  this.initBackground();
4246
4353
  }, listeners = [];
@@ -4276,16 +4383,6 @@
4276
4383
  element.style.setProperty(key, value, "important");
4277
4384
  }
4278
4385
  }
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
4386
  #removeHdrListeners() {
4290
4387
  if (!this.#hdrMediaListeners) {
4291
4388
  return;
@@ -4357,6 +4454,10 @@
4357
4454
  this.#manageListeners(true);
4358
4455
  }
4359
4456
  removeListeners() {
4457
+ if (this.#resizeTimeout !== undefined) {
4458
+ clearTimeout(this.#resizeTimeout);
4459
+ this.#resizeTimeout = undefined;
4460
+ }
4360
4461
  this.#manageListeners(false);
4361
4462
  }
4362
4463
  #handleVisibilityChange() {
@@ -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,40 +5106,46 @@
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;
5018
5113
  }
5019
5114
  queryCircle(position, radius, check, out = []) {
5020
- const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
5021
- this.#releaseShapes();
5022
- return result;
5115
+ const circle = this.#acquireCircle(position.x, position.y, radius);
5116
+ try {
5117
+ return this.query(circle, check, out);
5118
+ }
5119
+ finally {
5120
+ this.#circlePoolIdx--;
5121
+ }
5023
5122
  }
5024
5123
  queryRectangle(position, size, check, out = []) {
5025
- const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
5026
- this.#releaseShapes();
5027
- return result;
5028
- }
5029
- setCellSize(cellSize) {
5030
- this.#pendingCellSize = cellSize;
5124
+ const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height);
5125
+ try {
5126
+ return this.query(rect, check, out);
5127
+ }
5128
+ finally {
5129
+ this.#rectanglePoolIdx--;
5130
+ }
5031
5131
  }
5032
5132
  #acquireCircle(x, y, r) {
5033
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
5133
+ const idx = this.#circlePoolIdx++;
5134
+ let circle = this.#circlePool[idx];
5135
+ if (!circle) {
5136
+ circle = new Circle(x, y, r);
5137
+ this.#circlePool[idx] = circle;
5138
+ }
5139
+ return circle.reset(x, y, r);
5034
5140
  }
5035
5141
  #acquireRectangle(x, y, w, h) {
5036
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
5142
+ const idx = this.#rectanglePoolIdx++;
5143
+ let rectangle = this.#rectanglePool[idx];
5144
+ if (!rectangle) {
5145
+ rectangle = new Rectangle(x, y, w, h);
5146
+ this.#rectanglePool[idx] = rectangle;
5147
+ }
5148
+ return rectangle.reset(x, y, w, h);
5037
5149
  }
5038
5150
  #cellKeyFromCoords(x, y) {
5039
5151
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -5060,17 +5172,27 @@
5060
5172
  }
5061
5173
  return null;
5062
5174
  }
5063
- #releaseShapes() {
5064
- this.#circlePoolIdx = 0;
5065
- this.#rectanglePoolIdx = 0;
5175
+ #queryCell(cx, cy, range, check, out) {
5176
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5177
+ if (!cellParticles) {
5178
+ return;
5179
+ }
5180
+ for (const p of cellParticles) {
5181
+ if (check && !check(p)) {
5182
+ continue;
5183
+ }
5184
+ if (range.contains(p.getPosition())) {
5185
+ out.push(p);
5186
+ }
5187
+ }
5066
5188
  }
5067
5189
  }
5068
5190
 
5069
5191
  class ParticlesManager {
5070
5192
  checkParticlePositionPlugins;
5071
- grid;
5072
5193
  #array;
5073
5194
  #container;
5195
+ #grid;
5074
5196
  #groupLimits;
5075
5197
  #limit;
5076
5198
  #nextId;
@@ -5094,7 +5216,7 @@
5094
5216
  this.#groupLimits = new Map();
5095
5217
  this.#particleBuckets = new Map();
5096
5218
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
5097
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5219
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
5098
5220
  this.checkParticlePositionPlugins = [];
5099
5221
  this.#particleResetPlugins = [];
5100
5222
  this.#particleUpdatePlugins = [];
@@ -5105,23 +5227,12 @@
5105
5227
  get count() {
5106
5228
  return this.#array.length;
5107
5229
  }
5230
+ get grid() {
5231
+ return this.#grid;
5232
+ }
5108
5233
  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
- }
5234
+ if (this.#checkAndApplyLimits(group, this.count)) {
5235
+ return;
5125
5236
  }
5126
5237
  try {
5127
5238
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5185,7 +5296,7 @@
5185
5296
  return this.#array[index];
5186
5297
  }
5187
5298
  async init() {
5188
- const container = this.#container, options = container.actualOptions;
5299
+ const container = this.#container;
5189
5300
  this.checkParticlePositionPlugins = [];
5190
5301
  this.#updatePlugins = [];
5191
5302
  this.#particleUpdatePlugins = [];
@@ -5194,59 +5305,16 @@
5194
5305
  this.#postParticleUpdatePlugins = [];
5195
5306
  this.#particleBuckets.clear();
5196
5307
  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();
5308
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5309
+ this.#applyLimits(container.actualOptions.particles);
5310
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5311
+ for (const group in groups) {
5312
+ const groupData = groups[group];
5313
+ if (groupData) {
5314
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5248
5315
  }
5249
5316
  }
5317
+ await this.#initPlugins();
5250
5318
  }
5251
5319
  push(nb, position, overrideOptions, group) {
5252
5320
  for (let i = 0; i < nb; i++) {
@@ -5261,13 +5329,13 @@
5261
5329
  remove(particle, group, override) {
5262
5330
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5263
5331
  }
5264
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5332
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5265
5333
  if (index < minIndex || index > this.count) {
5266
5334
  return;
5267
5335
  }
5268
5336
  let deleted = 0;
5269
5337
  for (let i = index; deleted < quantity && i < this.count; i++) {
5270
- if (this.#removeParticle(i, group, override)) {
5338
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5271
5339
  i--;
5272
5340
  deleted++;
5273
5341
  }
@@ -5298,7 +5366,7 @@
5298
5366
  this.#resizeFactor = factor;
5299
5367
  }
5300
5368
  update(delta) {
5301
- this.grid.clear();
5369
+ this.#grid.clear();
5302
5370
  for (const plugin of this.#updatePlugins) {
5303
5371
  plugin.update?.(delta);
5304
5372
  }
@@ -5309,7 +5377,7 @@
5309
5377
  this.#updateParticlesPhase2(delta, particlesToDelete);
5310
5378
  if (particlesToDelete.size) {
5311
5379
  for (const particle of particlesToDelete) {
5312
- this.remove(particle);
5380
+ this.remove(particle, particle.group);
5313
5381
  }
5314
5382
  }
5315
5383
  this.#resizeFactor = undefined;
@@ -5320,12 +5388,7 @@
5320
5388
  #applyDensity(options, pluginsCount, group, groupOptions) {
5321
5389
  const numberOptions = options.number;
5322
5390
  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
- }
5391
+ this.#applyLimits(options, group, groupOptions);
5329
5392
  return;
5330
5393
  }
5331
5394
  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 +5396,10 @@
5333
5396
  this.#limit = numberOptions.limit.value * densityFactor;
5334
5397
  }
5335
5398
  else {
5336
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5399
+ this.#groupLimits.set(group, {
5400
+ limit: numberOptions.limit.value * densityFactor,
5401
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5402
+ });
5337
5403
  }
5338
5404
  if (particlesCount < particlesNumber) {
5339
5405
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5342,6 +5408,55 @@
5342
5408
  this.removeQuantity(particlesCount - particlesNumber, group);
5343
5409
  }
5344
5410
  }
5411
+ #applyLimits(options, group, groupOptions) {
5412
+ if (group === undefined) {
5413
+ this.#limit = options.number.limit.value;
5414
+ const groups = this.#container.actualOptions.particles.groups;
5415
+ for (const existingGroup of this.#groupLimits.keys()) {
5416
+ if (!(existingGroup in groups)) {
5417
+ this.#groupLimits.delete(existingGroup);
5418
+ }
5419
+ }
5420
+ }
5421
+ else {
5422
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5423
+ if (limitValue > minLimit) {
5424
+ this.#groupLimits.set(group, {
5425
+ limit: limitValue,
5426
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5427
+ });
5428
+ }
5429
+ else {
5430
+ this.#groupLimits.delete(group);
5431
+ }
5432
+ }
5433
+ }
5434
+ #checkAndApplyLimits(group, currentCount) {
5435
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5436
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5437
+ return true;
5438
+ }
5439
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5440
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5441
+ const groupCount = this.filter(t => t.group === group).length;
5442
+ if (groupCount >= groupLimitEntry.limit) {
5443
+ return true;
5444
+ }
5445
+ }
5446
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5447
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5448
+ if (groupCountToRemove > minCount) {
5449
+ this.removeQuantity(groupCountToRemove, group);
5450
+ }
5451
+ }
5452
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5453
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5454
+ if (globalCountToRemove > minCount) {
5455
+ this.#removeAny(globalCountToRemove);
5456
+ }
5457
+ }
5458
+ return false;
5459
+ }
5345
5460
  #createBuckets(zLayers) {
5346
5461
  const bucketCount = Math.max(Math.floor(zLayers), one);
5347
5462
  return Array.from({ length: bucketCount }, () => []);
@@ -5364,6 +5479,70 @@
5364
5479
  }
5365
5480
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5366
5481
  }
5482
+ async #initDrawersAndUpdaters() {
5483
+ const container = this.#container;
5484
+ await container.initDrawersAndUpdaters();
5485
+ for (const drawer of container.effectDrawers.values()) {
5486
+ await drawer.init?.(container);
5487
+ }
5488
+ for (const drawer of container.shapeDrawers.values()) {
5489
+ await drawer.init?.(container);
5490
+ }
5491
+ }
5492
+ async #initPlugins() {
5493
+ await this.#initPluginsArrays();
5494
+ await this.#initDrawersAndUpdaters();
5495
+ let handled = false;
5496
+ const container = this.#container;
5497
+ for (const plugin of container.plugins) {
5498
+ handled = plugin.particlesInitialization?.() ?? handled;
5499
+ if (handled) {
5500
+ break;
5501
+ }
5502
+ }
5503
+ if (handled) {
5504
+ return;
5505
+ }
5506
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5507
+ for (const group in groups) {
5508
+ const groupOptions = groups[group];
5509
+ if (!groupOptions) {
5510
+ continue;
5511
+ }
5512
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5513
+ this.addParticle(undefined, groupOptions, group);
5514
+ }
5515
+ }
5516
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5517
+ this.addParticle();
5518
+ }
5519
+ }
5520
+ async #initPluginsArrays() {
5521
+ const container = this.#container;
5522
+ for (const plugin of container.plugins) {
5523
+ if (plugin.redrawInit) {
5524
+ await plugin.redrawInit();
5525
+ }
5526
+ if (plugin.checkParticlePosition) {
5527
+ this.checkParticlePositionPlugins.push(plugin);
5528
+ }
5529
+ if (plugin.update) {
5530
+ this.#updatePlugins.push(plugin);
5531
+ }
5532
+ if (plugin.particleUpdate) {
5533
+ this.#particleUpdatePlugins.push(plugin);
5534
+ }
5535
+ if (plugin.postUpdate) {
5536
+ this.#postUpdatePlugins.push(plugin);
5537
+ }
5538
+ if (plugin.particleReset) {
5539
+ this.#particleResetPlugins.push(plugin);
5540
+ }
5541
+ if (plugin.postParticleUpdate) {
5542
+ this.#postParticleUpdatePlugins.push(plugin);
5543
+ }
5544
+ }
5545
+ }
5367
5546
  #insertParticleIntoBucket(particle) {
5368
5547
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5369
5548
  if (!bucket) {
@@ -5372,12 +5551,15 @@
5372
5551
  bucket.push(particle);
5373
5552
  this.#particleBuckets.set(particle.id, bucketIndex);
5374
5553
  }
5375
- #removeParticle(index, group, override) {
5554
+ #removeAny(quantity) {
5555
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5556
+ }
5557
+ #removeParticle(index, group, override, matchGroup = true) {
5376
5558
  const particle = this.#array[index];
5377
5559
  if (!particle) {
5378
5560
  return false;
5379
5561
  }
5380
- if (particle.group !== group) {
5562
+ if (matchGroup && particle.group !== group) {
5381
5563
  return false;
5382
5564
  }
5383
5565
  this.#array.splice(index, deleteCount);
@@ -5464,7 +5646,7 @@
5464
5646
  particlesToDelete.add(particle);
5465
5647
  continue;
5466
5648
  }
5467
- this.grid.insert(particle);
5649
+ this.#grid.insert(particle);
5468
5650
  }
5469
5651
  return particlesToDelete;
5470
5652
  }
@@ -5551,6 +5733,7 @@
5551
5733
  started;
5552
5734
  zLayers;
5553
5735
  #delay;
5736
+ #delayResolve;
5554
5737
  #delayTimeout;
5555
5738
  #delta = { value: 0, factor: 0 };
5556
5739
  #dispatchCallback;
@@ -5640,7 +5823,6 @@
5640
5823
  this.shapeDrawers = new Map();
5641
5824
  this.particleUpdaters = [];
5642
5825
  this.plugins.length = 0;
5643
- this.#pluginManager.clearPlugins(this);
5644
5826
  this.destroyed = true;
5645
5827
  this.#onDestroy(remove);
5646
5828
  this.dispatchEvent(EventType.containerDestroyed);
@@ -5693,31 +5875,14 @@
5693
5875
  await this.initDrawersAndUpdaters();
5694
5876
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
5695
5877
  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
- }
5878
+ this.#initPlugins(allContainerPlugins);
5714
5879
  this.retina.init();
5715
5880
  this.canvas.init();
5716
5881
  this.updateActualOptions();
5717
5882
  this.canvas.initBackground();
5718
5883
  this.canvas.resize();
5719
5884
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
5720
- this.hdrMode = hdrOptions.mode;
5885
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
5721
5886
  this.peakNits = hdrOptions.peakNits;
5722
5887
  this.zLayers = zLayers;
5723
5888
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -5738,9 +5903,9 @@
5738
5903
  }
5739
5904
  async initDrawersAndUpdaters() {
5740
5905
  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);
5906
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
5907
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
5908
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
5744
5909
  }
5745
5910
  pause() {
5746
5911
  if (!guardCheck(this)) {
@@ -5807,7 +5972,10 @@
5807
5972
  await this.init();
5808
5973
  this.started = true;
5809
5974
  await new Promise(resolve => {
5975
+ this.#delayResolve = resolve;
5810
5976
  const start = async () => {
5977
+ this.#delayTimeout = undefined;
5978
+ this.#delayResolve = undefined;
5811
5979
  this.#eventListeners.addListeners();
5812
5980
  for (const plugin of this.plugins) {
5813
5981
  await plugin.start?.();
@@ -5823,9 +5991,11 @@
5823
5991
  if (!guardCheck(this) || !this.started) {
5824
5992
  return;
5825
5993
  }
5826
- if (this.#delayTimeout) {
5994
+ if (this.#delayTimeout !== undefined) {
5827
5995
  clearTimeout(this.#delayTimeout);
5828
5996
  this.#delayTimeout = undefined;
5997
+ this.#delayResolve?.();
5998
+ this.#delayResolve = undefined;
5829
5999
  }
5830
6000
  this.#firstStart = true;
5831
6001
  this.started = false;
@@ -5851,6 +6021,27 @@
5851
6021
  }
5852
6022
  return refresh;
5853
6023
  }
6024
+ #initPlugins(allContainerPlugins) {
6025
+ this.plugins.length = 0;
6026
+ this.particleDestroyedPlugins.length = 0;
6027
+ this.particleCreatedPlugins.length = 0;
6028
+ this.particlePositionPlugins.length = 0;
6029
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
6030
+ if (!plugin.needsPlugin(this.actualOptions)) {
6031
+ continue;
6032
+ }
6033
+ this.plugins.push(containerPlugin);
6034
+ if (containerPlugin.particleCreated) {
6035
+ this.particleCreatedPlugins.push(containerPlugin);
6036
+ }
6037
+ if (containerPlugin.particleDestroyed) {
6038
+ this.particleDestroyedPlugins.push(containerPlugin);
6039
+ }
6040
+ if (containerPlugin.particlePosition) {
6041
+ this.particlePositionPlugins.push(containerPlugin);
6042
+ }
6043
+ }
6044
+ }
5854
6045
  #nextFrame(timestamp) {
5855
6046
  try {
5856
6047
  if (!this.#smooth &&
@@ -6127,11 +6318,11 @@
6127
6318
  }
6128
6319
  }
6129
6320
  async #init() {
6130
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6131
- if (!availablePathGenerators) {
6321
+ const pluginManager = this.#pluginManager;
6322
+ if (!pluginManager.initializers.pathGenerators) {
6132
6323
  return;
6133
6324
  }
6134
- this.availablePathGenerators = availablePathGenerators;
6325
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6135
6326
  this.pathGenerators = new Map();
6136
6327
  for (const pathGenerator of this.pathGenerators.values()) {
6137
6328
  pathGenerator.init();
@@ -6152,6 +6343,11 @@
6152
6343
  this.#container = container;
6153
6344
  this.#instancesManager.initContainer(container);
6154
6345
  }
6346
+ draw(context, _delta) {
6347
+ for (const emitter of this.#instancesManager.getArray(this.#container)) {
6348
+ emitter.draw(context);
6349
+ }
6350
+ }
6155
6351
  async init() {
6156
6352
  const emittersOptions = this.#container.actualOptions.emitters;
6157
6353
  if (isArray(emittersOptions)) {
@@ -6222,14 +6418,21 @@
6222
6418
  this.#pluginManager = pluginManager;
6223
6419
  }
6224
6420
  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;
6421
+ return this.addEmitters(container, [{ options, position }]).then(t => t[defaultIndex]);
6422
+ }
6423
+ async addEmitters(container, emitters) {
6424
+ const { EmitterInstance } = await Promise.resolve().then(function () { return EmitterInstance$1; }), res = [];
6425
+ for (const { options, position } of emitters) {
6426
+ const emitterOptions = new Emitter();
6427
+ emitterOptions.load(options);
6428
+ const emitter = new EmitterInstance(this.#pluginManager, container, (emitter) => {
6429
+ this.removeEmitter(container, emitter);
6430
+ }, emitterOptions, position);
6431
+ await emitter.init();
6432
+ this.getArray(container).push(emitter);
6433
+ res.push(emitter);
6434
+ }
6435
+ return res;
6233
6436
  }
6234
6437
  clear(container) {
6235
6438
  this.initContainer(container);
@@ -6256,6 +6459,7 @@
6256
6459
  : array.find(t => t.name === idxOrName);
6257
6460
  };
6258
6461
  container.addEmitter = async (options, position) => this.addEmitter(container, options, position);
6462
+ container.addEmitters = async (emitters) => this.addEmitters(container, emitters);
6259
6463
  container.removeEmitter = (idxOrName) => {
6260
6464
  const emitter = container.getEmitter?.(idxOrName);
6261
6465
  if (emitter) {
@@ -6276,7 +6480,7 @@
6276
6480
  };
6277
6481
  }
6278
6482
  removeEmitter(container, emitter) {
6279
- const index = this.getArray(container).indexOf(emitter), minIndex = 0, deleteCount = 1;
6483
+ const index = this.getArray(container).indexOf(emitter), deleteCount = 1;
6280
6484
  if (index >= minIndex) {
6281
6485
  this.getArray(container).splice(index, deleteCount);
6282
6486
  }
@@ -6331,6 +6535,7 @@
6331
6535
  #particlesOptions;
6332
6536
  #paused;
6333
6537
  #pluginManager;
6538
+ #positionOverride = false;
6334
6539
  #removeCallback;
6335
6540
  #resizeObserver;
6336
6541
  #shape;
@@ -6407,6 +6612,31 @@
6407
6612
  });
6408
6613
  this.play();
6409
6614
  }
6615
+ draw(context) {
6616
+ if (!this.options.draw || !this.#shape) {
6617
+ return;
6618
+ }
6619
+ const container = this.#container, fillEnabled = this.options.spawn.fill?.enable ?? true;
6620
+ context.save();
6621
+ context.beginPath();
6622
+ if (fillEnabled) {
6623
+ context.fillStyle = this.spawnFillColor
6624
+ ? getStyleFromHsl(this.spawnFillColor, container.hdr, this.spawnFillOpacity ?? defaultOpacity$1, container.peakNits, container.hdrMode)
6625
+ : getStyleFromRgb({ r: 0, g: 0, b: 0 }, container.hdr, defaultOpacity$1, container.peakNits, container.hdrMode);
6626
+ }
6627
+ if (this.spawnStrokeColor) {
6628
+ context.strokeStyle = getStyleFromHsl(this.spawnStrokeColor, container.hdr, this.spawnStrokeOpacity ?? defaultOpacity$1, container.peakNits, container.hdrMode);
6629
+ context.lineWidth = this.spawnStrokeWidth ?? defaultStrokeWidth;
6630
+ }
6631
+ this.#shape.draw?.(context);
6632
+ if (fillEnabled) {
6633
+ context.fill();
6634
+ }
6635
+ if (this.spawnStrokeColor) {
6636
+ context.stroke();
6637
+ }
6638
+ context.restore();
6639
+ }
6410
6640
  externalPause() {
6411
6641
  this.#paused = true;
6412
6642
  this.pause();
@@ -6444,11 +6674,14 @@
6444
6674
  }
6445
6675
  }
6446
6676
  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();
6677
+ const container = this.#container;
6678
+ if (!this.#positionOverride) {
6679
+ const initialPosition = this.#initialPosition;
6680
+ this.position =
6681
+ initialPosition && isPointInside(initialPosition, container.canvas.size, Vector.origin)
6682
+ ? initialPosition
6683
+ : this.#calcPosition();
6684
+ }
6452
6685
  this.#size = this.#calcSize();
6453
6686
  this.size =
6454
6687
  this.#size.mode === PixelMode.percent
@@ -6459,6 +6692,11 @@
6459
6692
  : { width: this.#size.width, height: this.#size.height };
6460
6693
  this.#shape?.resize(this.position, this.size);
6461
6694
  }
6695
+ setPosition(position) {
6696
+ this.#positionOverride = true;
6697
+ this.position = { ...position };
6698
+ this.#shape?.resize(this.position, this.size);
6699
+ }
6462
6700
  update(delta) {
6463
6701
  if (this.#paused) {
6464
6702
  return;
@@ -6484,8 +6722,10 @@
6484
6722
  this.#lifeCount--;
6485
6723
  }
6486
6724
  if (this.#lifeCount > minLifeCount || this.#immortal) {
6487
- this.position = this.#calcPosition();
6488
- this.#shape?.resize(this.position, this.size);
6725
+ if (!this.#positionOverride) {
6726
+ this.position = this.#calcPosition();
6727
+ this.#shape?.resize(this.position, this.size);
6728
+ }
6489
6729
  this.#spawnDelay = container.retina.reduceFactor
6490
6730
  ? (getRangeValue(this.options.life.delay ?? defaultLifeDelay) * millisecondsToSeconds) /
6491
6731
  container.retina.reduceFactor
@@ -6654,6 +6894,7 @@
6654
6894
  exports.loadBubblesPreset = loadBubblesPreset;
6655
6895
  exports.tsParticles = tsParticles;
6656
6896
 
6657
- }));
6897
+ })(g.__tsParticlesInternals.presets.bubbles = g.__tsParticlesInternals.presets.bubbles || {});
6898
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6899
+
6658
6900
  Object.assign(globalThis.window || globalThis, { loadBubblesPreset: (globalThis.__tsParticlesInternals.presets.bubbles || {}).loadBubblesPreset });
6659
- delete (globalThis.window || globalThis).tsparticlesInternalExports;