@tsparticles/preset-ambient 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,35 @@
1
- (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.4.0 */
3
- (function (global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.presets = global.__tsParticlesInternals.presets || {}, global.__tsParticlesInternals.presets.ambient = global.__tsParticlesInternals.presets.ambient || {})));
7
- })(this, (function (exports) { 'use strict';
1
+ (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
+ (function (g) {
3
+ /* Preset v4.5.0 */
4
+ g.__tsParticlesInternals = g.__tsParticlesInternals || {};
5
+ g.__tsParticlesInternals.presets = g.__tsParticlesInternals.presets || {};
6
+ (function (exports) {
7
+ 'use strict';
8
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;
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
  }
@@ -270,11 +301,61 @@
270
301
  };
271
302
  }
272
303
  function parseAlpha(input) {
273
- const defaultAlpha = 1;
274
304
  if (!input) {
275
305
  return defaultAlpha;
276
306
  }
277
- return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
307
+ return input.endsWith("%") ? Number.parseFloat(input) / percentDenominator : Number.parseFloat(input);
308
+ }
309
+
310
+ const ErrorCodes$1 = {
311
+ engineVersionMismatch: "TSP-1001",
312
+ particlePositionNotFound: "TSP-1002",
313
+ pluginRegisterAfterLoad: "TSP-1003",
314
+ offscreenCanvasUnsupported: "TSP-1101",
315
+ offscreenCanvasTransferFailed: "TSP-1102",
316
+ hdrEnableInvalid: "TSP-1201",
317
+ hdrModeInvalid: "TSP-1202",
318
+ hdrPeakNitsInvalid: "TSP-1203",
319
+ };
320
+
321
+ const messages = {
322
+ [ErrorCodes$1.engineVersionMismatch]: "The tsParticles version is different from the loaded plugins version. Engine version: {0}. Plugin version: {1}",
323
+ [ErrorCodes$1.particlePositionNotFound]: "a valid position cannot be found for particle",
324
+ [ErrorCodes$1.pluginRegisterAfterLoad]: "Register plugins can only be done before calling tsParticles.load()",
325
+ [ErrorCodes$1.offscreenCanvasUnsupported]: "OffscreenCanvas is required but not supported by this browser",
326
+ [ErrorCodes$1.offscreenCanvasTransferFailed]: "OffscreenCanvas transfer failed",
327
+ [ErrorCodes$1.hdrEnableInvalid]: 'Invalid HDR "enable" value: expected a boolean, got "{0}"',
328
+ [ErrorCodes$1.hdrModeInvalid]: 'Invalid HDR "mode" value: expected one of {0}, got "{1}"',
329
+ [ErrorCodes$1.hdrPeakNitsInvalid]: 'Invalid HDR "peakNits" value: expected a positive number, got "{0}"',
330
+ };
331
+
332
+ let registeredMessages;
333
+ const ensureRegisteredMessages = () => {
334
+ registeredMessages ??= new Map(Object.entries(messages));
335
+ return registeredMessages;
336
+ }, toStringValue$1 = (value) => String(value), format = (template, args) => {
337
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
338
+ const arg = args[Number(index)];
339
+ return arg === undefined ? match : toStringValue$1(arg);
340
+ });
341
+ };
342
+ function addErrorMessages(extraMessages) {
343
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
344
+ return;
345
+ }
346
+ const catalog = ensureRegisteredMessages();
347
+ for (const [code, message] of Object.entries(extraMessages)) {
348
+ if (message !== undefined) {
349
+ catalog.set(code, message);
350
+ }
351
+ }
352
+ }
353
+ function getErrorMessage(code, ...args) {
354
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
355
+ return `[tsParticles Error ${code}] https://particles.js.org/errors/#${code}`;
356
+ }
357
+ const template = ensureRegisteredMessages().get(code) ?? `Unknown error code ${code}`;
358
+ return format(template, args);
278
359
  }
279
360
 
280
361
  var OutModeDirection;
@@ -291,7 +372,6 @@
291
372
  PixelMode["percent"] = "percent";
292
373
  })(PixelMode || (PixelMode = {}));
293
374
 
294
- const minRadius = 0;
295
375
  function isForbiddenKey(key) {
296
376
  return key === "__proto__" || key === "constructor" || key === "prototype";
297
377
  }
@@ -487,22 +567,84 @@
487
567
  element.removeEventListener(event, handler, removeOptions);
488
568
  }
489
569
  }
490
- async function getItemsFromInitializer(container, map, initializers, force = false) {
491
- let res = map.get(container);
492
- if (!res || force) {
493
- res = await Promise.all([...initializers.values()].map(t => t(container)));
494
- map.set(container, res);
570
+ async function getItemsFromInitializer(container, initializers) {
571
+ return await Promise.all([...initializers.values()].map(t => t(container)));
572
+ }
573
+ async function getItemMapFromInitializer(container, initializers) {
574
+ const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
575
+ return new Map(entries);
576
+ }
577
+ async function getDataFromUrl(data) {
578
+ const url = itemFromSingleOrMultiple(data.url, data.index);
579
+ if (!url) {
580
+ return data.fallback;
495
581
  }
496
- return res;
582
+ try {
583
+ const response = await fetch(url);
584
+ if (response.ok) {
585
+ const current = (await response.json());
586
+ if (isSourceOptionsValue(current)) {
587
+ return current;
588
+ }
589
+ getLogger().error("invalid configuration data while retrieving config file");
590
+ }
591
+ else {
592
+ getLogger().error(`${response.status.toString()} while retrieving config file`);
593
+ }
594
+ }
595
+ catch (error) {
596
+ getLogger().error(`error while retrieving config file: ${error}`);
597
+ }
598
+ return data.fallback;
497
599
  }
498
- async function getItemMapFromInitializer(container, map, initializers, force = false) {
499
- let res = map.get(container);
500
- if (!res || force) {
501
- const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
502
- res = new Map(entries);
503
- map.set(container, res);
600
+ function isSourceOptionsValue(value) {
601
+ if (isArray(value)) {
602
+ return value.every((entry) => isObject(entry) && !isArray(entry));
504
603
  }
505
- return res;
604
+ return isObject(value) && !isArray(value);
605
+ }
606
+ function getCanvasFromContainer(domContainer) {
607
+ let canvasEl;
608
+ const documentSafe = safeDocument(), isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
609
+ if (isCanvas) {
610
+ canvasEl = domContainer;
611
+ canvasEl.dataset[generatedAttribute] ??= generatedFalse;
612
+ if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
613
+ canvasEl.style.width ||= fullPercent;
614
+ canvasEl.style.height ||= fullPercent;
615
+ canvasEl.style.pointerEvents = "none";
616
+ canvasEl.style.setProperty("pointer-events", "none");
617
+ }
618
+ }
619
+ else {
620
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
621
+ if (foundCanvas) {
622
+ canvasEl = foundCanvas;
623
+ canvasEl.dataset[generatedAttribute] = generatedFalse;
624
+ }
625
+ else {
626
+ canvasEl = documentSafe.createElement(canvasTag);
627
+ canvasEl.dataset[generatedAttribute] = generatedTrue;
628
+ domContainer.appendChild(canvasEl);
629
+ }
630
+ canvasEl.style.width ||= fullPercent;
631
+ canvasEl.style.height ||= fullPercent;
632
+ canvasEl.style.pointerEvents = "none";
633
+ canvasEl.style.setProperty("pointer-events", "none");
634
+ }
635
+ return canvasEl;
636
+ }
637
+ function getDomContainer(id, source) {
638
+ const documentSafe = safeDocument();
639
+ let domContainer = source ?? documentSafe.getElementById(id);
640
+ if (domContainer) {
641
+ return domContainer;
642
+ }
643
+ domContainer = documentSafe.createElement("canvas");
644
+ domContainer.id = id;
645
+ domContainer.dataset[generatedAttribute] = generatedTrue;
646
+ documentSafe.body.append(domContainer);
647
+ return domContainer;
506
648
  }
507
649
 
508
650
  class EventDispatcher {
@@ -573,7 +715,6 @@
573
715
  class PluginManager {
574
716
  colorManagers = new Map();
575
717
  easingFunctions = new Map();
576
- effectDrawers = new Map();
577
718
  initializers = {
578
719
  effects: new Map(),
579
720
  shapes: new Map(),
@@ -582,8 +723,6 @@
582
723
  palettes = new Map();
583
724
  plugins = [];
584
725
  presets = new Map();
585
- shapeDrawers = new Map();
586
- updaters = new Map();
587
726
  #allLoadersSet = new Set();
588
727
  #configs = new Map();
589
728
  #engine;
@@ -601,57 +740,79 @@
601
740
  }
602
741
  return res;
603
742
  }
743
+ get initialized() {
744
+ return this.#initialized;
745
+ }
604
746
  addColorManager(name, manager) {
747
+ if (this.#initialized) {
748
+ return;
749
+ }
605
750
  this.colorManagers.set(name, manager);
606
751
  }
607
752
  addConfig(config) {
753
+ if (this.#initialized) {
754
+ return;
755
+ }
608
756
  const key = config.key ?? config.name ?? "default";
609
757
  this.#configs.set(key, config);
610
758
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
611
759
  }
612
760
  addEasing(name, easing) {
761
+ if (this.#initialized) {
762
+ return;
763
+ }
613
764
  if (this.easingFunctions.get(name)) {
614
765
  return;
615
766
  }
616
767
  this.easingFunctions.set(name, easing);
617
768
  }
618
769
  addEffect(effect, drawer) {
770
+ if (this.#initialized) {
771
+ return;
772
+ }
619
773
  this.initializers.effects.set(effect, drawer);
620
774
  }
621
775
  addPalette(name, palette) {
776
+ if (this.#initialized) {
777
+ return;
778
+ }
622
779
  this.palettes.set(name, palette);
623
780
  }
624
781
  addParticleUpdater(name, updaterInitializer) {
782
+ if (this.#initialized) {
783
+ return;
784
+ }
625
785
  this.initializers.updaters.set(name, updaterInitializer);
626
786
  }
627
787
  addPlugin(plugin) {
788
+ if (this.#initialized) {
789
+ return;
790
+ }
628
791
  if (this.getPlugin(plugin.id)) {
629
792
  return;
630
793
  }
631
794
  this.plugins.push(plugin);
632
795
  }
633
796
  addPreset(preset, options, override = false) {
797
+ if (this.#initialized) {
798
+ return;
799
+ }
634
800
  if (!(override || !this.getPreset(preset))) {
635
801
  return;
636
802
  }
637
803
  this.presets.set(preset, options);
638
804
  }
639
805
  addShape(shapes, drawer) {
806
+ if (this.#initialized) {
807
+ return;
808
+ }
640
809
  for (const shape of shapes) {
641
810
  this.initializers.shapes.set(shape, drawer);
642
811
  }
643
812
  }
644
- clearPlugins(container) {
645
- this.effectDrawers.delete(container);
646
- this.shapeDrawers.delete(container);
647
- this.updaters.delete(container);
648
- }
649
813
  getEasing(name) {
650
814
  return this.easingFunctions.get(name) ?? ((value) => value);
651
815
  }
652
- getEffectDrawers(container, force = false) {
653
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
654
- }
655
816
  getPalette(name) {
656
817
  return this.palettes.get(name);
657
818
  }
@@ -661,12 +822,6 @@
661
822
  getPreset(preset) {
662
823
  return this.presets.get(preset);
663
824
  }
664
- async getShapeDrawers(container, force = false) {
665
- return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
666
- }
667
- async getUpdaters(container, force = false) {
668
- return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
669
- }
670
825
  async init() {
671
826
  if (this.#initialized || this.#isRunningLoaders) {
672
827
  return;
@@ -685,16 +840,9 @@
685
840
  this.#initialized = true;
686
841
  }
687
842
  }
688
- loadParticlesOptions(container, options, ...sourceOptions) {
689
- const updaters = this.updaters.get(container);
690
- if (!updaters) {
691
- return;
692
- }
693
- updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
694
- }
695
843
  async register(...loaders) {
696
844
  if (this.#initialized) {
697
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
845
+ throw new Error(getErrorMessage(ErrorCodes$1.pluginRegisterAfterLoad));
698
846
  }
699
847
  for (const loader of loaders) {
700
848
  if (this.#isRunningLoaders) {
@@ -706,88 +854,15 @@
706
854
  }
707
855
  }
708
856
  async #runLoader(loader, executed, allLoaders) {
709
- if (executed.has(loader))
857
+ if (executed.has(loader)) {
710
858
  return;
859
+ }
711
860
  executed.add(loader);
712
861
  allLoaders.add(loader);
713
862
  await loader(this.#engine);
714
863
  }
715
864
  }
716
865
 
717
- const errorPrefix = "tsParticles - Error";
718
- const wrap = (fn) => (...args) => {
719
- fn(...args);
720
- }, _logger = {
721
- debug: wrap(console.debug),
722
- error: (message, ...optionalParams) => {
723
- console.error(`${errorPrefix} - ${message}`, ...optionalParams);
724
- },
725
- info: wrap(console.info),
726
- log: wrap(console.log),
727
- trace: wrap(console.trace),
728
- verbose: wrap(console.log),
729
- warning: wrap(console.warn),
730
- };
731
- function getLogger() {
732
- return _logger;
733
- }
734
-
735
- const fullPercent = "100%";
736
- async function getDataFromUrl(data) {
737
- const url = itemFromSingleOrMultiple(data.url, data.index);
738
- if (!url) {
739
- return data.fallback;
740
- }
741
- const response = await fetch(url);
742
- if (response.ok) {
743
- return (await response.json());
744
- }
745
- getLogger().error(`${response.status.toString()} while retrieving config file`);
746
- return data.fallback;
747
- }
748
- const getCanvasFromContainer = (domContainer) => {
749
- const documentSafe = safeDocument();
750
- let canvasEl;
751
- const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
752
- if (isCanvas) {
753
- canvasEl = domContainer;
754
- canvasEl.dataset[generatedAttribute] ??= generatedFalse;
755
- if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
756
- canvasEl.style.width ||= fullPercent;
757
- canvasEl.style.height ||= fullPercent;
758
- canvasEl.style.pointerEvents = "none";
759
- canvasEl.style.setProperty("pointer-events", "none");
760
- }
761
- }
762
- else {
763
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
764
- if (foundCanvas) {
765
- canvasEl = foundCanvas;
766
- canvasEl.dataset[generatedAttribute] = generatedFalse;
767
- }
768
- else {
769
- canvasEl = documentSafe.createElement(canvasTag);
770
- canvasEl.dataset[generatedAttribute] = generatedTrue;
771
- domContainer.appendChild(canvasEl);
772
- }
773
- canvasEl.style.width ||= fullPercent;
774
- canvasEl.style.height ||= fullPercent;
775
- canvasEl.style.pointerEvents = "none";
776
- canvasEl.style.setProperty("pointer-events", "none");
777
- }
778
- return canvasEl;
779
- }, getDomContainer = (id, source) => {
780
- const documentSafe = safeDocument();
781
- let domContainer = source ?? documentSafe.getElementById(id);
782
- if (domContainer) {
783
- return domContainer;
784
- }
785
- domContainer = documentSafe.createElement("canvas");
786
- domContainer.id = id;
787
- domContainer.dataset[generatedAttribute] = generatedTrue;
788
- documentSafe.body.append(domContainer);
789
- return domContainer;
790
- };
791
866
  class Engine {
792
867
  pluginManager = new PluginManager(this);
793
868
  #domArray = [];
@@ -797,7 +872,7 @@
797
872
  return this.#domArray;
798
873
  }
799
874
  get version() {
800
- return "4.4.0";
875
+ return "4.5.0";
801
876
  }
802
877
  addEventListener(type, listener) {
803
878
  this.#eventDispatcher.addEventListener(type, listener);
@@ -806,7 +881,7 @@
806
881
  if (this.version === pluginVersion) {
807
882
  return;
808
883
  }
809
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
884
+ throw new Error(getErrorMessage(ErrorCodes$1.engineVersionMismatch, this.version, pluginVersion));
810
885
  }
811
886
  dispatchEvent(type, args) {
812
887
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1055,20 +1130,6 @@
1055
1130
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1056
1131
  })(DrawLayer || (DrawLayer = {}));
1057
1132
 
1058
- class OptionLoader {
1059
- load(data) {
1060
- if (isNull(data)) {
1061
- return;
1062
- }
1063
- this.doLoad(data);
1064
- }
1065
- }
1066
- function loadOptions(options, ...sourceOptionsArr) {
1067
- for (const sourceOptions of sourceOptionsArr) {
1068
- options.load(sourceOptions);
1069
- }
1070
- }
1071
-
1072
1133
  function loadProperty(obj, key, value) {
1073
1134
  if (value !== undefined) {
1074
1135
  obj[key] = value;
@@ -1100,6 +1161,20 @@
1100
1161
  }
1101
1162
  }
1102
1163
 
1164
+ class OptionLoader {
1165
+ load(data) {
1166
+ if (isNull(data)) {
1167
+ return;
1168
+ }
1169
+ this.doLoad(data);
1170
+ }
1171
+ }
1172
+ function loadOptions(options, ...sourceOptionsArr) {
1173
+ for (const sourceOptions of sourceOptionsArr) {
1174
+ options.load(sourceOptions);
1175
+ }
1176
+ }
1177
+
1103
1178
  class AnimationOptions extends OptionLoader {
1104
1179
  count = 0;
1105
1180
  decay = 0;
@@ -1250,15 +1325,14 @@
1250
1325
  peakNits = defaultPeakNits;
1251
1326
  doLoad(data) {
1252
1327
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1253
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1328
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrEnableInvalid, String(data.enable)));
1254
1329
  }
1255
- if (data.mode !== undefined &&
1256
- (typeof data.mode !== "string" || !Object.values(HdrMode).includes(data.mode))) {
1257
- throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
1330
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1331
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1258
1332
  }
1259
1333
  if (data.peakNits !== undefined &&
1260
1334
  (!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
1261
- throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
1335
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrPeakNitsInvalid, String(data.peakNits)));
1262
1336
  }
1263
1337
  loadProperty(this, "enable", data.enable);
1264
1338
  loadProperty(this, "mode", data.mode);
@@ -1636,12 +1710,10 @@
1636
1710
  plugin.loadParticlesOptions(this.#container, this, data);
1637
1711
  }
1638
1712
  }
1639
- const updaters = this.#pluginManager.updaters.get(this.#container);
1640
- if (updaters) {
1641
- for (const updater of updaters) {
1642
- if (updater.loadOptions) {
1643
- updater.loadOptions(this, data);
1644
- }
1713
+ const updaters = this.#container.particleUpdaters;
1714
+ for (const updater of updaters) {
1715
+ if (updater.loadOptions) {
1716
+ updater.loadOptions(this, data);
1645
1717
  }
1646
1718
  }
1647
1719
  }
@@ -1974,9 +2046,9 @@
1974
2046
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1975
2047
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1976
2048
  }
1977
- const temp1 = lNormalized < half
2049
+ const temp2 = lNormalized < half
1978
2050
  ? lNormalized * (sNormalizedOffset + sNormalized)
1979
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2051
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1980
2052
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1981
2053
  }
1982
2054
  function hslToRgbFloat(hsl) {
@@ -1985,9 +2057,9 @@
1985
2057
  const grayscaleValue = lNormalized * rgbMax;
1986
2058
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1987
2059
  }
1988
- const temp1 = lNormalized < half
2060
+ const temp2 = lNormalized < half
1989
2061
  ? lNormalized * (sNormalizedOffset + sNormalized)
1990
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2062
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1991
2063
  return { r: red, g: green, b: blue };
1992
2064
  }
1993
2065
  function hslaToRgba(hsla) {
@@ -2201,7 +2273,7 @@
2201
2273
  }
2202
2274
 
2203
2275
  async function loadBlendPlugin(engine) {
2204
- engine.checkVersion("4.4.0");
2276
+ engine.checkVersion("4.5.0");
2205
2277
  await engine.pluginManager.register(e => {
2206
2278
  e.pluginManager.addPlugin(new BlendPlugin());
2207
2279
  });
@@ -2238,7 +2310,7 @@
2238
2310
  }
2239
2311
 
2240
2312
  async function loadCircleShape(engine) {
2241
- engine.checkVersion("4.4.0");
2313
+ engine.checkVersion("4.5.0");
2242
2314
  await engine.pluginManager.register(e => {
2243
2315
  e.pluginManager.addShape(["circle"], () => {
2244
2316
  return Promise.resolve(new CircleDrawer());
@@ -2253,7 +2325,7 @@
2253
2325
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2254
2326
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2255
2327
  })(RgbIndexes$1 || (RgbIndexes$1 = {}));
2256
- const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
2328
+ const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, alphaFactor = 0xff;
2257
2329
  class HexColorManager {
2258
2330
  accepts(input) {
2259
2331
  return input.startsWith("#");
@@ -2286,7 +2358,7 @@
2286
2358
  }
2287
2359
 
2288
2360
  async function loadHexColorPlugin(engine) {
2289
- engine.checkVersion("4.4.0");
2361
+ engine.checkVersion("4.5.0");
2290
2362
  await engine.pluginManager.register(e => {
2291
2363
  e.pluginManager.addColorManager("hex", new HexColorManager());
2292
2364
  });
@@ -2326,7 +2398,7 @@
2326
2398
  if (!this.accepts(input)) {
2327
2399
  return;
2328
2400
  }
2329
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2401
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2330
2402
  return result
2331
2403
  ? hslaToRgba({
2332
2404
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2339,12 +2411,20 @@
2339
2411
  }
2340
2412
 
2341
2413
  async function loadHslColorPlugin(engine) {
2342
- engine.checkVersion("4.4.0");
2414
+ engine.checkVersion("4.5.0");
2343
2415
  await engine.pluginManager.register(e => {
2344
2416
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2345
2417
  });
2346
2418
  }
2347
2419
 
2420
+ const ErrorCodes = {
2421
+ baseMoverNotLoaded: "TSP-3203",
2422
+ };
2423
+
2424
+ const ErrorMessages = {
2425
+ [ErrorCodes.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2426
+ };
2427
+
2348
2428
  class MovePlugin {
2349
2429
  id = "move";
2350
2430
  #pluginManager;
@@ -2363,20 +2443,20 @@
2363
2443
  }
2364
2444
 
2365
2445
  async function loadMovePlugin(engine) {
2366
- engine.checkVersion("4.4.0");
2446
+ engine.checkVersion("4.5.0");
2447
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2448
+ addErrorMessages(ErrorMessages);
2449
+ }
2367
2450
  await engine.pluginManager.register(e => {
2368
2451
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2369
2452
  movePluginManager.initializers.pathGenerators ??= new Map();
2370
- movePluginManager.pathGenerators ??= new Map();
2371
2453
  movePluginManager.addPathGenerator = (name, generator) => {
2454
+ if (movePluginManager.initialized) {
2455
+ return;
2456
+ }
2372
2457
  movePluginManager.initializers.pathGenerators ??= new Map();
2373
2458
  movePluginManager.initializers.pathGenerators.set(name, generator);
2374
2459
  };
2375
- movePluginManager.getPathGenerators = async (container, force = false) => {
2376
- movePluginManager.initializers.pathGenerators ??= new Map();
2377
- movePluginManager.pathGenerators ??= new Map();
2378
- return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
2379
- };
2380
2460
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2381
2461
  });
2382
2462
  }
@@ -2573,7 +2653,7 @@
2573
2653
  }
2574
2654
 
2575
2655
  async function loadOpacityUpdater(engine) {
2576
- engine.checkVersion("4.4.0");
2656
+ engine.checkVersion("4.5.0");
2577
2657
  await engine.pluginManager.register(e => {
2578
2658
  e.pluginManager.addParticleUpdater("opacity", container => {
2579
2659
  return Promise.resolve(new OpacityUpdater(container));
@@ -2582,70 +2662,60 @@
2582
2662
  }
2583
2663
 
2584
2664
  const boundsMin = 0;
2585
- function bounceHorizontal(data) {
2586
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2587
- (data.direction !== OutModeDirection.left && data.direction !== OutModeDirection.right)) {
2665
+ const bounceAxisConfigs = {
2666
+ [OutModeDirection.left]: {
2667
+ axis: "x",
2668
+ canvas: "width",
2669
+ bounce: "horizontal",
2670
+ negativeEdge: "right",
2671
+ positiveEdge: "left",
2672
+ negative: true,
2673
+ },
2674
+ [OutModeDirection.right]: {
2675
+ axis: "x",
2676
+ canvas: "width",
2677
+ bounce: "horizontal",
2678
+ negativeEdge: "right",
2679
+ positiveEdge: "left",
2680
+ negative: false,
2681
+ },
2682
+ [OutModeDirection.top]: {
2683
+ axis: "y",
2684
+ canvas: "height",
2685
+ bounce: "vertical",
2686
+ negativeEdge: "bottom",
2687
+ positiveEdge: "top",
2688
+ negative: true,
2689
+ },
2690
+ [OutModeDirection.bottom]: {
2691
+ axis: "y",
2692
+ canvas: "height",
2693
+ bounce: "vertical",
2694
+ negativeEdge: "bottom",
2695
+ positiveEdge: "top",
2696
+ negative: false,
2697
+ },
2698
+ };
2699
+ function bounce(data) {
2700
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2588
2701
  return;
2589
2702
  }
2590
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2591
- data.particle.position.x = data.size + data.offset.x;
2703
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2704
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2705
+ position[config.axis] = data.size + data.offset[config.axis];
2592
2706
  }
2593
- else if (data.bounds.left > data.canvasSize.width && data.direction === OutModeDirection.right) {
2594
- data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
2707
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2708
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2595
2709
  }
2596
- const velocity = data.particle.velocity.x;
2597
- let bounced = false;
2598
2710
  if (data.outOfCanvas &&
2599
- ((data.direction === OutModeDirection.right && velocity > minVelocity) ||
2600
- (data.direction === OutModeDirection.left && velocity < minVelocity))) {
2601
- const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
2602
- data.particle.velocity.x *= -newVelocity;
2603
- bounced = true;
2711
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2712
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2604
2713
  }
2605
- if (!bounced) {
2714
+ else {
2606
2715
  return;
2607
2716
  }
2608
- const minPos = data.offset.x + data.size;
2609
- if (data.outOfCanvas && data.direction === OutModeDirection.right) {
2610
- data.particle.position.x = data.canvasSize.width - minPos;
2611
- }
2612
- else if (data.outOfCanvas && data.direction === OutModeDirection.left) {
2613
- data.particle.position.x = minPos;
2614
- }
2615
- if (data.outMode === OutMode.split) {
2616
- data.particle.destroy();
2617
- }
2618
- }
2619
- function bounceVertical(data) {
2620
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2621
- (data.direction !== OutModeDirection.bottom && data.direction !== OutModeDirection.top)) {
2622
- return;
2623
- }
2624
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2625
- data.particle.position.y = data.size + data.offset.y;
2626
- }
2627
- else if (data.bounds.top > data.canvasSize.height && data.direction === OutModeDirection.bottom) {
2628
- data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
2629
- }
2630
- const velocity = data.particle.velocity.y;
2631
- let bounced = false;
2632
- if (data.outOfCanvas &&
2633
- ((data.direction === OutModeDirection.bottom && velocity > minVelocity) ||
2634
- (data.direction === OutModeDirection.top && velocity < minVelocity))) {
2635
- const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
2636
- data.particle.velocity.y *= -newVelocity;
2637
- bounced = true;
2638
- }
2639
- if (!bounced) {
2640
- return;
2641
- }
2642
- const minPos = data.offset.y + data.size;
2643
- if (data.outOfCanvas && data.direction === OutModeDirection.bottom) {
2644
- data.particle.position.y = data.canvasSize.height - minPos;
2645
- }
2646
- else if (data.outOfCanvas && data.direction === OutModeDirection.top) {
2647
- data.particle.position.y = minPos;
2648
- }
2717
+ const minPos = data.offset[config.axis] + data.size;
2718
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2649
2719
  if (data.outMode === OutMode.split) {
2650
2720
  data.particle.destroy();
2651
2721
  }
@@ -2661,26 +2731,19 @@
2661
2731
  OutMode.bounce,
2662
2732
  OutMode.split,
2663
2733
  ];
2664
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2734
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2665
2735
  }
2666
2736
  update(particle, direction, delta, outMode) {
2667
2737
  if (!this.modes.includes(outMode)) {
2668
2738
  return;
2669
2739
  }
2670
- const container = this.#container;
2671
- let handled = false;
2672
2740
  for (const plugin of this.#particleBouncePlugins) {
2673
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2674
- if (handled) {
2675
- break;
2741
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2742
+ return;
2676
2743
  }
2677
2744
  }
2678
- if (handled) {
2679
- return;
2680
- }
2681
- const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2682
- bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2683
- bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2745
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2746
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2684
2747
  }
2685
2748
  }
2686
2749
 
@@ -2769,107 +2832,108 @@
2769
2832
  return;
2770
2833
  }
2771
2834
  const container = this.#container;
2772
- switch (particle.outType) {
2773
- case ParticleOutType.inside: {
2774
- const { x: vx, y: vy } = particle.velocity;
2775
- updateVector.setTo(originPoint);
2776
- updateVector.length = particle.moveCenter.radius;
2777
- updateVector.angle = particle.velocity.angle + Math.PI;
2778
- updateVector.addTo(particle.moveCenter);
2779
- const { dx, dy } = getDistances(particle.position, updateVector);
2780
- if ((vx <= minVelocity && dx >= minDistance) ||
2781
- (vy <= minVelocity && dy >= minDistance) ||
2782
- (vx >= minVelocity && dx <= minDistance) ||
2783
- (vy >= minVelocity && dy <= minDistance)) {
2784
- return;
2785
- }
2786
- particle.position.x = Math.floor(randomInRangeValue({
2787
- min: 0,
2788
- max: container.canvas.size.width,
2789
- }));
2790
- particle.position.y = Math.floor(randomInRangeValue({
2791
- min: 0,
2792
- max: container.canvas.size.height,
2793
- }));
2794
- const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2795
- particle.direction = Math.atan2(-newDy, -newDx);
2796
- particle.velocity.angle = particle.direction;
2797
- particle.justWarped = true;
2798
- break;
2835
+ if (particle.outType === ParticleOutType.inside) {
2836
+ this.#applyOutModeInside(particle, container);
2837
+ }
2838
+ else {
2839
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2840
+ return;
2799
2841
  }
2800
- default: {
2801
- if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2802
- return;
2803
- }
2804
- switch (particle.outType) {
2805
- case ParticleOutType.outside: {
2806
- particle.position.x =
2807
- Math.floor(randomInRangeValue({
2808
- min: -particle.moveCenter.radius,
2809
- max: particle.moveCenter.radius,
2810
- })) + particle.moveCenter.x;
2811
- particle.position.y =
2812
- Math.floor(randomInRangeValue({
2813
- min: -particle.moveCenter.radius,
2814
- max: particle.moveCenter.radius,
2815
- })) + particle.moveCenter.y;
2816
- const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2817
- if (particle.moveCenter.radius) {
2818
- particle.direction = Math.atan2(dy, dx);
2819
- particle.velocity.angle = particle.direction;
2820
- }
2821
- particle.justWarped = true;
2822
- break;
2823
- }
2824
- case ParticleOutType.normal: {
2825
- const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
2826
- bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
2827
- left: -particle.getRadius() - particle.offset.x,
2828
- right: canvasSize.width + particle.getRadius() + particle.offset.x,
2829
- top: -particle.getRadius() - particle.offset.y,
2830
- }, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
2831
- if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2832
- particle.position.x = newPos.left;
2833
- particle.initialPosition.x = particle.position.x;
2834
- if (!warp) {
2835
- particle.position.y = getRandom() * canvasSize.height;
2836
- particle.initialPosition.y = particle.position.y;
2837
- }
2838
- particle.justWarped = true;
2839
- }
2840
- else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2841
- particle.position.x = newPos.right;
2842
- particle.initialPosition.x = particle.position.x;
2843
- if (!warp) {
2844
- particle.position.y = getRandom() * canvasSize.height;
2845
- particle.initialPosition.y = particle.position.y;
2846
- }
2847
- particle.justWarped = true;
2848
- }
2849
- if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2850
- if (!warp) {
2851
- particle.position.x = getRandom() * canvasSize.width;
2852
- particle.initialPosition.x = particle.position.x;
2853
- }
2854
- particle.position.y = newPos.top;
2855
- particle.initialPosition.y = particle.position.y;
2856
- particle.justWarped = true;
2857
- }
2858
- else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2859
- if (!warp) {
2860
- particle.position.x = getRandom() * canvasSize.width;
2861
- particle.initialPosition.x = particle.position.x;
2862
- }
2863
- particle.position.y = newPos.bottom;
2864
- particle.initialPosition.y = particle.position.y;
2865
- particle.justWarped = true;
2866
- }
2867
- break;
2868
- }
2869
- }
2870
- break;
2842
+ if (particle.outType === ParticleOutType.outside) {
2843
+ this.#applyParticleModeOutside(particle);
2871
2844
  }
2845
+ else {
2846
+ this.#applyOutModeNormal(particle, container, direction);
2847
+ }
2848
+ }
2849
+ }
2850
+ #applyOutModeInside(particle, container) {
2851
+ const { x: vx, y: vy } = particle.velocity;
2852
+ updateVector.setTo(originPoint);
2853
+ updateVector.length = particle.moveCenter.radius;
2854
+ updateVector.angle = particle.velocity.angle + Math.PI;
2855
+ updateVector.addTo(particle.moveCenter);
2856
+ const { dx, dy } = getDistances(particle.position, updateVector);
2857
+ if ((vx <= minVelocity && dx >= minDistance) ||
2858
+ (vy <= minVelocity && dy >= minDistance) ||
2859
+ (vx >= minVelocity && dx <= minDistance) ||
2860
+ (vy >= minVelocity && dy <= minDistance)) {
2861
+ return;
2872
2862
  }
2863
+ particle.position.x = Math.floor(randomInRangeValue({
2864
+ min: 0,
2865
+ max: container.canvas.size.width,
2866
+ }));
2867
+ particle.position.y = Math.floor(randomInRangeValue({
2868
+ min: 0,
2869
+ max: container.canvas.size.height,
2870
+ }));
2871
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2872
+ particle.direction = Math.atan2(-newDy, -newDx);
2873
+ particle.velocity.angle = particle.direction;
2874
+ particle.justWarped = true;
2875
+ }
2876
+ #applyOutModeNormal(particle, container, direction) {
2877
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2878
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2879
+ left: -sizeValue - particle.offset.x,
2880
+ right: canvasSize.width + sizeValue + particle.offset.x,
2881
+ top: -sizeValue - particle.offset.y,
2882
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2883
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2884
+ particle.position.x = newPos.left;
2885
+ particle.initialPosition.x = particle.position.x;
2886
+ if (!warp) {
2887
+ particle.position.y = getRandom() * canvasSize.height;
2888
+ particle.initialPosition.y = particle.position.y;
2889
+ }
2890
+ particle.justWarped = true;
2891
+ }
2892
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2893
+ particle.position.x = newPos.right;
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
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2902
+ if (!warp) {
2903
+ particle.position.x = getRandom() * canvasSize.width;
2904
+ particle.initialPosition.x = particle.position.x;
2905
+ }
2906
+ particle.position.y = newPos.top;
2907
+ particle.initialPosition.y = particle.position.y;
2908
+ particle.justWarped = true;
2909
+ }
2910
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -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.bottom;
2916
+ particle.initialPosition.y = particle.position.y;
2917
+ particle.justWarped = true;
2918
+ }
2919
+ }
2920
+ #applyParticleModeOutside(particle) {
2921
+ particle.position.x =
2922
+ Math.floor(randomInRangeValue({
2923
+ min: -particle.moveCenter.radius,
2924
+ max: particle.moveCenter.radius,
2925
+ })) + particle.moveCenter.x;
2926
+ particle.position.y =
2927
+ Math.floor(randomInRangeValue({
2928
+ min: -particle.moveCenter.radius,
2929
+ max: particle.moveCenter.radius,
2930
+ })) + particle.moveCenter.y;
2931
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2932
+ if (particle.moveCenter.radius) {
2933
+ particle.direction = Math.atan2(dy, dx);
2934
+ particle.velocity.angle = particle.direction;
2935
+ }
2936
+ particle.justWarped = true;
2873
2937
  }
2874
2938
  }
2875
2939
 
@@ -2918,56 +2982,26 @@
2918
2982
  }
2919
2983
 
2920
2984
  async function loadOutModesUpdater(engine) {
2921
- engine.checkVersion("4.4.0");
2985
+ engine.checkVersion("4.5.0");
2922
2986
  await engine.pluginManager.register(e => {
2923
2987
  e.pluginManager.addParticleUpdater("outModes", container => {
2924
2988
  return Promise.resolve(new OutOfCanvasUpdater(container));
2925
2989
  });
2926
- });
2927
- }
2928
-
2929
- const defaultOpacity = 1;
2930
- class PaintUpdater {
2931
- #container;
2932
- #pluginManager;
2933
- constructor(pluginManager, container) {
2934
- this.#container = container;
2935
- this.#pluginManager = pluginManager;
2936
- }
2937
- init(particle) {
2938
- 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;
2939
- if (fill) {
2940
- const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
2941
- particle.fillEnabled = fill.enable;
2942
- particle.fillOpacity = getRangeValue(fill.opacity);
2943
- particle.fillAnimation = fillColor.animation;
2944
- const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
2945
- if (fillHslColor) {
2946
- particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
2947
- }
2948
- }
2949
- else {
2950
- particle.fillEnabled = false;
2951
- particle.fillAnimation = undefined;
2952
- particle.fillColor = undefined;
2953
- particle.fillOpacity = defaultOpacity;
2954
- }
2955
- if (stroke) {
2956
- const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
2957
- particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
2958
- particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
2959
- particle.strokeAnimation = strokeColor.animation;
2960
- const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
2961
- if (strokeHslColor) {
2962
- particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
2963
- }
2964
- }
2965
- else {
2966
- particle.strokeAnimation = undefined;
2967
- particle.strokeColor = undefined;
2968
- particle.strokeOpacity = defaultOpacity;
2969
- particle.strokeWidth = 0;
2970
- }
2990
+ });
2991
+ }
2992
+
2993
+ const defaultOpacity = 1;
2994
+ class PaintUpdater {
2995
+ #container;
2996
+ #pluginManager;
2997
+ constructor(pluginManager, container) {
2998
+ this.#container = container;
2999
+ this.#pluginManager = pluginManager;
3000
+ }
3001
+ init(particle) {
3002
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3003
+ this.#initFill(paint, particle, container);
3004
+ this.#initStroke(paint, particle, container);
2971
3005
  }
2972
3006
  isEnabled(particle) {
2973
3007
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2986,10 +3020,48 @@
2986
3020
  updateColor(particle.fillColor, delta, this.#container.hdr);
2987
3021
  updateColor(particle.strokeColor, delta, this.#container.hdr);
2988
3022
  }
3023
+ #initFill(paint, particle, container) {
3024
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3025
+ if (!fill) {
3026
+ particle.fillEnabled = false;
3027
+ particle.fillAnimation = undefined;
3028
+ particle.fillColor = undefined;
3029
+ particle.fillOpacity = defaultOpacity;
3030
+ return;
3031
+ }
3032
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3033
+ particle.fillEnabled = fill.enable;
3034
+ particle.fillOpacity = getRangeValue(fill.opacity);
3035
+ particle.fillAnimation = fillColor.animation;
3036
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3037
+ if (!fillHslColor) {
3038
+ return;
3039
+ }
3040
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3041
+ }
3042
+ #initStroke(paint, particle, container) {
3043
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3044
+ if (!stroke) {
3045
+ particle.strokeAnimation = undefined;
3046
+ particle.strokeColor = undefined;
3047
+ particle.strokeOpacity = defaultOpacity;
3048
+ particle.strokeWidth = 0;
3049
+ return;
3050
+ }
3051
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3052
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3053
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3054
+ particle.strokeAnimation = strokeColor.animation;
3055
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3056
+ if (!strokeHslColor) {
3057
+ return;
3058
+ }
3059
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3060
+ }
2989
3061
  }
2990
3062
 
2991
3063
  async function loadPaintUpdater(engine) {
2992
- engine.checkVersion("4.4.0");
3064
+ engine.checkVersion("4.5.0");
2993
3065
  await engine.pluginManager.register(e => {
2994
3066
  e.pluginManager.addParticleUpdater("paint", container => {
2995
3067
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3031,7 +3103,7 @@
3031
3103
  if (!this.accepts(input)) {
3032
3104
  return;
3033
3105
  }
3034
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3106
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3035
3107
  return result
3036
3108
  ? {
3037
3109
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3044,7 +3116,7 @@
3044
3116
  }
3045
3117
 
3046
3118
  async function loadRgbColorPlugin(engine) {
3047
- engine.checkVersion("4.4.0");
3119
+ engine.checkVersion("4.5.0");
3048
3120
  await engine.pluginManager.register(e => {
3049
3121
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3050
3122
  });
@@ -3129,7 +3201,7 @@
3129
3201
  }
3130
3202
 
3131
3203
  async function loadSizeUpdater(engine) {
3132
- engine.checkVersion("4.4.0");
3204
+ engine.checkVersion("4.5.0");
3133
3205
  await engine.pluginManager.register(e => {
3134
3206
  e.pluginManager.addParticleUpdater("size", container => {
3135
3207
  return Promise.resolve(new SizeUpdater(container));
@@ -3138,7 +3210,7 @@
3138
3210
  }
3139
3211
 
3140
3212
  async function loadBasic(engine) {
3141
- engine.checkVersion("4.4.0");
3213
+ engine.checkVersion("4.5.0");
3142
3214
  await engine.pluginManager.register(async (e) => {
3143
3215
  await Promise.all([
3144
3216
  loadBlendPlugin(e),
@@ -3332,21 +3404,28 @@
3332
3404
  });
3333
3405
  }
3334
3406
  clear() {
3407
+ const visited = new Set();
3335
3408
  for (const plugin of this.#canvasClearPlugins) {
3409
+ visited.add(plugin);
3336
3410
  if (plugin.canvasClear?.() ?? false) {
3337
- return;
3411
+ return plugin;
3338
3412
  }
3339
3413
  }
3340
3414
  for (const layer of Object.values(DrawLayer)) {
3341
3415
  if (typeof layer === "number") {
3342
3416
  for (const plugin of this.#getLayerPlugins(layer)) {
3417
+ if (visited.has(plugin)) {
3418
+ continue;
3419
+ }
3420
+ visited.add(plugin);
3343
3421
  if (plugin.canvasClear?.() ?? false) {
3344
- return;
3422
+ return plugin;
3345
3423
  }
3346
3424
  }
3347
3425
  }
3348
3426
  }
3349
3427
  this.canvasClear();
3428
+ return undefined;
3350
3429
  }
3351
3430
  destroy() {
3352
3431
  this.stop();
@@ -3424,8 +3503,7 @@
3424
3503
  });
3425
3504
  }
3426
3505
  drawParticles(delta) {
3427
- const { particles, actualOptions } = this.#container;
3428
- this.clear();
3506
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3429
3507
  particles.update(delta);
3430
3508
  this.draw(ctx => {
3431
3509
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -3447,7 +3525,9 @@
3447
3525
  }
3448
3526
  }
3449
3527
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3450
- plugin.canvasPaint?.();
3528
+ if (plugin !== clearedPlugin) {
3529
+ plugin.canvasPaint?.();
3530
+ }
3451
3531
  }
3452
3532
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3453
3533
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -3479,35 +3559,7 @@
3479
3559
  this.#layers[layer] = [];
3480
3560
  }
3481
3561
  }
3482
- for (const plugin of this.#container.plugins) {
3483
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3484
- this.#colorPlugins.push(plugin);
3485
- }
3486
- if (plugin.drawParticle) {
3487
- this.#drawParticlePlugins.push(plugin);
3488
- }
3489
- if (plugin.drawParticleSetup) {
3490
- this.#drawParticlesSetupPlugins.push(plugin);
3491
- }
3492
- if (plugin.drawParticleCleanup) {
3493
- this.#drawParticlesCleanupPlugins.push(plugin);
3494
- }
3495
- if (plugin.canvasClear) {
3496
- this.#canvasClearPlugins.push(plugin);
3497
- }
3498
- if (plugin.canvasPaint) {
3499
- this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
3500
- }
3501
- if (plugin.drawSettingsSetup) {
3502
- this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
3503
- }
3504
- if (plugin.draw) {
3505
- this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
3506
- }
3507
- if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
3508
- this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
3509
- }
3510
- }
3562
+ this.#initPluginsArray();
3511
3563
  }
3512
3564
  initUpdaters() {
3513
3565
  this.#preDrawUpdaters = [];
@@ -3716,6 +3768,45 @@
3716
3768
  this.#reusablePluginColors[sColorIndex] = sColor;
3717
3769
  return this.#reusablePluginColors;
3718
3770
  }
3771
+ #initLayerPlugin(plugin) {
3772
+ const layerMap = [
3773
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
3774
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
3775
+ [DrawLayer.PluginContent, !!plugin.draw],
3776
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
3777
+ ];
3778
+ for (const [layer, active] of layerMap) {
3779
+ if (active) {
3780
+ this.#layers[layer].push(plugin);
3781
+ }
3782
+ }
3783
+ }
3784
+ #initPluginsArray() {
3785
+ for (const plugin of this.#container.plugins) {
3786
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3787
+ this.#colorPlugins.push(plugin);
3788
+ }
3789
+ if (plugin.drawParticle) {
3790
+ this.#drawParticlePlugins.push(plugin);
3791
+ }
3792
+ if (plugin.drawParticleSetup) {
3793
+ this.#drawParticlesSetupPlugins.push(plugin);
3794
+ }
3795
+ if (plugin.drawParticleCleanup) {
3796
+ this.#drawParticlesCleanupPlugins.push(plugin);
3797
+ }
3798
+ if (plugin.canvasClear) {
3799
+ this.#canvasClearPlugins.push(plugin);
3800
+ }
3801
+ this.#initLayerPlugin(plugin);
3802
+ }
3803
+ }
3804
+ #isSupportedElement(element) {
3805
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
3806
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
3807
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
3808
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
3809
+ }
3719
3810
  #resolveBackgroundElement() {
3720
3811
  const background = this.#container.actualOptions.background;
3721
3812
  this.#backgroundElement = null;
@@ -3725,9 +3816,7 @@
3725
3816
  if (typeof background.element === "string") {
3726
3817
  if (typeof document !== "undefined") {
3727
3818
  const node = document.querySelector(background.element);
3728
- if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
3729
- (typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
3730
- (typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
3819
+ if (node && this.#isSupportedElement(node)) {
3731
3820
  this.#backgroundElement = node;
3732
3821
  }
3733
3822
  else if (node) {
@@ -3738,10 +3827,7 @@
3738
3827
  }
3739
3828
  }
3740
3829
  }
3741
- else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
3742
- (typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
3743
- (typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
3744
- (typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
3830
+ else if (this.#isSupportedElement(background.element)) {
3745
3831
  this.#backgroundElement = background.element;
3746
3832
  }
3747
3833
  }
@@ -3759,7 +3845,7 @@
3759
3845
  return transferredCanvas;
3760
3846
  }
3761
3847
  if (typeof canvas.transferControlToOffscreen !== "function") {
3762
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
3848
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasUnsupported));
3763
3849
  }
3764
3850
  try {
3765
3851
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -3767,10 +3853,16 @@
3767
3853
  return offscreenCanvas;
3768
3854
  }
3769
3855
  catch {
3770
- throw new TypeError("OffscreenCanvas transfer failed");
3856
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasTransferFailed));
3771
3857
  }
3772
3858
  }, isHtmlCanvasElement = (canvas) => {
3773
3859
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
3860
+ }, getEffectiveColorSpace = (context) => {
3861
+ if (!context) {
3862
+ return undefined;
3863
+ }
3864
+ const colorInfo = context;
3865
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
3774
3866
  };
3775
3867
  function setStyle(canvas, style, important = false) {
3776
3868
  if (!style) {
@@ -3895,7 +3987,7 @@
3895
3987
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
3896
3988
  if (color) {
3897
3989
  const hdrOptions = container.actualOptions.hdr;
3898
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
3990
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
3899
3991
  }
3900
3992
  else {
3901
3993
  elementStyle.backgroundColor = "";
@@ -4015,11 +4107,11 @@
4015
4107
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4016
4108
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4017
4109
  safeMatchMedia("(dynamic-range: high)")?.matches;
4018
- container.hdr = canSupportHdr ?? false;
4019
- container.hdrMode = container.actualOptions.hdr.mode;
4110
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4020
4111
  container.peakNits = container.actualOptions.hdr.peakNits;
4021
4112
  const renderCanvas = this.renderCanvas;
4022
4113
  if (!renderCanvas) {
4114
+ container.hdr = canSupportHdr ?? false;
4023
4115
  return;
4024
4116
  }
4025
4117
  this.render.setContextSettings({
@@ -4038,7 +4130,6 @@
4038
4130
  context = null;
4039
4131
  }
4040
4132
  if (canSupportHdr && !context) {
4041
- container.hdr = false;
4042
4133
  const sdrSettings = {
4043
4134
  alpha: true,
4044
4135
  desynchronized: true,
@@ -4054,11 +4145,16 @@
4054
4145
  }
4055
4146
  }
4056
4147
  this.render.setContext(context);
4148
+ if (context) {
4149
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4150
+ if (effectiveColorSpace !== undefined) {
4151
+ container.hdr = effectiveColorSpace === "display-p3";
4152
+ }
4153
+ }
4057
4154
  }
4058
4155
  #initHdrListeners() {
4059
4156
  this.#removeHdrListeners();
4060
4157
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4061
- this.#recreateRenderCanvas();
4062
4158
  this.#initContext();
4063
4159
  this.initBackground();
4064
4160
  }, listeners = [];
@@ -4094,16 +4190,6 @@
4094
4190
  element.style.setProperty(key, value, "important");
4095
4191
  }
4096
4192
  }
4097
- #recreateRenderCanvas() {
4098
- const renderCanvas = this.renderCanvas;
4099
- if (!renderCanvas) {
4100
- return;
4101
- }
4102
- if (this.domElement) {
4103
- return;
4104
- }
4105
- this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
4106
- }
4107
4193
  #removeHdrListeners() {
4108
4194
  if (!this.#hdrMediaListeners) {
4109
4195
  return;
@@ -4202,8 +4288,7 @@
4202
4288
  this.#resizeTimeout = undefined;
4203
4289
  }
4204
4290
  const handleResize = async () => {
4205
- const canvas = this.#container.canvas;
4206
- await canvas.windowResize();
4291
+ await this.#container.canvas.windowResize();
4207
4292
  };
4208
4293
  this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
4209
4294
  }
@@ -4231,8 +4316,8 @@
4231
4316
  }
4232
4317
  else if (!this.#resizeObserver && add && canvasEl) {
4233
4318
  this.#resizeObserver = new ResizeObserver((entries) => {
4234
- const entry = entries.find(e => e.target === canvasEl);
4235
- if (!entry) {
4319
+ const res = entries.some(e => e.target === canvasEl);
4320
+ if (!res) {
4236
4321
  return;
4237
4322
  }
4238
4323
  this.#handleWindowResize();
@@ -4496,10 +4581,10 @@
4496
4581
  return this.#modifiers.find(m => m.id === id);
4497
4582
  }
4498
4583
  getOpacity() {
4499
- 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;
4500
- this.#cachedOpacityData.fillOpacity = fillOpacity;
4584
+ 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;
4585
+ this.#cachedOpacityData.fillOpacity = this.fillOpacity ?? defaultOpacity$1;
4501
4586
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
4502
- this.#cachedOpacityData.strokeOpacity = strokeOpacity;
4587
+ this.#cachedOpacityData.strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
4503
4588
  return this.#cachedOpacityData;
4504
4589
  }
4505
4590
  getPosition() {
@@ -4746,7 +4831,7 @@
4746
4831
  #initPosition(position) {
4747
4832
  const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
4748
4833
  if (!initialPosition) {
4749
- throw new Error("a valid position cannot be found for particle");
4834
+ throw new Error(getErrorMessage(ErrorCodes$1.particlePositionNotFound));
4750
4835
  }
4751
4836
  this.position = initialPosition;
4752
4837
  this.initialPosition = this.position.copy();
@@ -4795,6 +4880,12 @@
4795
4880
  this.#circlePoolIdx = 0;
4796
4881
  this.#rectanglePoolIdx = 0;
4797
4882
  }
4883
+ get cellSize() {
4884
+ return this.#cellSize;
4885
+ }
4886
+ set cellSize(value) {
4887
+ this.#pendingCellSize = value;
4888
+ }
4798
4889
  clear() {
4799
4890
  this.#cells.clear();
4800
4891
  const pendingCellSize = this.#pendingCellSize;
@@ -4818,18 +4909,7 @@
4818
4909
  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);
4819
4910
  for (let cx = minCellX; cx <= maxCellX; cx++) {
4820
4911
  for (let cy = minCellY; cy <= maxCellY; cy++) {
4821
- const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
4822
- if (!cellParticles) {
4823
- continue;
4824
- }
4825
- for (const p of cellParticles) {
4826
- if (check && !check(p)) {
4827
- continue;
4828
- }
4829
- if (range.contains(p.getPosition())) {
4830
- out.push(p);
4831
- }
4832
- }
4912
+ this.#queryCell(cx, cy, range, check, out);
4833
4913
  }
4834
4914
  }
4835
4915
  return out;
@@ -4844,14 +4924,23 @@
4844
4924
  this.#releaseShapes();
4845
4925
  return result;
4846
4926
  }
4847
- setCellSize(cellSize) {
4848
- this.#pendingCellSize = cellSize;
4849
- }
4850
4927
  #acquireCircle(x, y, r) {
4851
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
4928
+ const idx = this.#circlePoolIdx++;
4929
+ let circle = this.#circlePool[idx];
4930
+ if (!circle) {
4931
+ circle = new Circle(x, y, r);
4932
+ this.#circlePool[idx] = circle;
4933
+ }
4934
+ return circle.reset(x, y, r);
4852
4935
  }
4853
4936
  #acquireRectangle(x, y, w, h) {
4854
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
4937
+ const idx = this.#rectanglePoolIdx++;
4938
+ let rectangle = this.#rectanglePool[idx];
4939
+ if (!rectangle) {
4940
+ rectangle = new Rectangle(x, y, w, h);
4941
+ this.#rectanglePool[idx] = rectangle;
4942
+ }
4943
+ return rectangle.reset(x, y, w, h);
4855
4944
  }
4856
4945
  #cellKeyFromCoords(x, y) {
4857
4946
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -4878,6 +4967,20 @@
4878
4967
  }
4879
4968
  return null;
4880
4969
  }
4970
+ #queryCell(cx, cy, range, check, out) {
4971
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
4972
+ if (!cellParticles) {
4973
+ return;
4974
+ }
4975
+ for (const p of cellParticles) {
4976
+ if (check && !check(p)) {
4977
+ continue;
4978
+ }
4979
+ if (range.contains(p.getPosition())) {
4980
+ out.push(p);
4981
+ }
4982
+ }
4983
+ }
4881
4984
  #releaseShapes() {
4882
4985
  this.#circlePoolIdx = 0;
4883
4986
  this.#rectanglePoolIdx = 0;
@@ -4886,9 +4989,9 @@
4886
4989
 
4887
4990
  class ParticlesManager {
4888
4991
  checkParticlePositionPlugins;
4889
- grid;
4890
4992
  #array;
4891
4993
  #container;
4994
+ #grid;
4892
4995
  #groupLimits;
4893
4996
  #limit;
4894
4997
  #nextId;
@@ -4912,7 +5015,7 @@
4912
5015
  this.#groupLimits = new Map();
4913
5016
  this.#particleBuckets = new Map();
4914
5017
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
4915
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5018
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
4916
5019
  this.checkParticlePositionPlugins = [];
4917
5020
  this.#particleResetPlugins = [];
4918
5021
  this.#particleUpdatePlugins = [];
@@ -4923,23 +5026,12 @@
4923
5026
  get count() {
4924
5027
  return this.#array.length;
4925
5028
  }
5029
+ get grid() {
5030
+ return this.#grid;
5031
+ }
4926
5032
  addParticle(position, overrideOptions, group, initializer) {
4927
- const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
4928
- if (limit > minLimit) {
4929
- switch (limitMode) {
4930
- case LimitMode.delete: {
4931
- const countToRemove = currentCount + countOffset - limit;
4932
- if (countToRemove > minCount) {
4933
- this.removeQuantity(countToRemove);
4934
- }
4935
- break;
4936
- }
4937
- case LimitMode.wait:
4938
- if (currentCount >= limit) {
4939
- return;
4940
- }
4941
- break;
4942
- }
5033
+ if (this.#checkAndApplyLimits(group, this.count)) {
5034
+ return;
4943
5035
  }
4944
5036
  try {
4945
5037
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5003,7 +5095,7 @@
5003
5095
  return this.#array[index];
5004
5096
  }
5005
5097
  async init() {
5006
- const container = this.#container, options = container.actualOptions;
5098
+ const container = this.#container;
5007
5099
  this.checkParticlePositionPlugins = [];
5008
5100
  this.#updatePlugins = [];
5009
5101
  this.#particleUpdatePlugins = [];
@@ -5012,59 +5104,16 @@
5012
5104
  this.#postParticleUpdatePlugins = [];
5013
5105
  this.#particleBuckets.clear();
5014
5106
  this.#resetBuckets(container.zLayers);
5015
- this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5016
- for (const plugin of container.plugins) {
5017
- if (plugin.redrawInit) {
5018
- await plugin.redrawInit();
5019
- }
5020
- if (plugin.checkParticlePosition) {
5021
- this.checkParticlePositionPlugins.push(plugin);
5022
- }
5023
- if (plugin.update) {
5024
- this.#updatePlugins.push(plugin);
5025
- }
5026
- if (plugin.particleUpdate) {
5027
- this.#particleUpdatePlugins.push(plugin);
5028
- }
5029
- if (plugin.postUpdate) {
5030
- this.#postUpdatePlugins.push(plugin);
5031
- }
5032
- if (plugin.particleReset) {
5033
- this.#particleResetPlugins.push(plugin);
5034
- }
5035
- if (plugin.postParticleUpdate) {
5036
- this.#postParticleUpdatePlugins.push(plugin);
5037
- }
5038
- }
5039
- await this.#container.initDrawersAndUpdaters();
5040
- for (const drawer of this.#container.effectDrawers.values()) {
5041
- await drawer.init?.(container);
5042
- }
5043
- for (const drawer of this.#container.shapeDrawers.values()) {
5044
- await drawer.init?.(container);
5045
- }
5046
- let handled = false;
5047
- for (const plugin of container.plugins) {
5048
- handled = plugin.particlesInitialization?.() ?? handled;
5049
- if (handled) {
5050
- break;
5051
- }
5052
- }
5053
- if (!handled) {
5054
- const particlesOptions = options.particles, groups = particlesOptions.groups;
5055
- for (const group in groups) {
5056
- const groupOptions = groups[group];
5057
- if (!groupOptions) {
5058
- continue;
5059
- }
5060
- for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5061
- this.addParticle(undefined, groupOptions, group);
5062
- }
5063
- }
5064
- for (let i = this.count; i < particlesOptions.number.value; i++) {
5065
- this.addParticle();
5107
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5108
+ this.#applyLimits(container.actualOptions.particles);
5109
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5110
+ for (const group in groups) {
5111
+ const groupData = groups[group];
5112
+ if (groupData) {
5113
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5066
5114
  }
5067
5115
  }
5116
+ await this.#initPlugins();
5068
5117
  }
5069
5118
  push(nb, position, overrideOptions, group) {
5070
5119
  for (let i = 0; i < nb; i++) {
@@ -5079,13 +5128,13 @@
5079
5128
  remove(particle, group, override) {
5080
5129
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5081
5130
  }
5082
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5131
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5083
5132
  if (index < minIndex || index > this.count) {
5084
5133
  return;
5085
5134
  }
5086
5135
  let deleted = 0;
5087
5136
  for (let i = index; deleted < quantity && i < this.count; i++) {
5088
- if (this.#removeParticle(i, group, override)) {
5137
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5089
5138
  i--;
5090
5139
  deleted++;
5091
5140
  }
@@ -5116,7 +5165,7 @@
5116
5165
  this.#resizeFactor = factor;
5117
5166
  }
5118
5167
  update(delta) {
5119
- this.grid.clear();
5168
+ this.#grid.clear();
5120
5169
  for (const plugin of this.#updatePlugins) {
5121
5170
  plugin.update?.(delta);
5122
5171
  }
@@ -5127,7 +5176,7 @@
5127
5176
  this.#updateParticlesPhase2(delta, particlesToDelete);
5128
5177
  if (particlesToDelete.size) {
5129
5178
  for (const particle of particlesToDelete) {
5130
- this.remove(particle);
5179
+ this.remove(particle, particle.group);
5131
5180
  }
5132
5181
  }
5133
5182
  this.#resizeFactor = undefined;
@@ -5138,12 +5187,7 @@
5138
5187
  #applyDensity(options, pluginsCount, group, groupOptions) {
5139
5188
  const numberOptions = options.number;
5140
5189
  if (!numberOptions.density.enable) {
5141
- if (group === undefined) {
5142
- this.#limit = numberOptions.limit.value;
5143
- }
5144
- else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
5145
- this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
5146
- }
5190
+ this.#applyLimits(options, group, groupOptions);
5147
5191
  return;
5148
5192
  }
5149
5193
  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);
@@ -5151,7 +5195,10 @@
5151
5195
  this.#limit = numberOptions.limit.value * densityFactor;
5152
5196
  }
5153
5197
  else {
5154
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5198
+ this.#groupLimits.set(group, {
5199
+ limit: numberOptions.limit.value * densityFactor,
5200
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5201
+ });
5155
5202
  }
5156
5203
  if (particlesCount < particlesNumber) {
5157
5204
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5160,6 +5207,55 @@
5160
5207
  this.removeQuantity(particlesCount - particlesNumber, group);
5161
5208
  }
5162
5209
  }
5210
+ #applyLimits(options, group, groupOptions) {
5211
+ if (group === undefined) {
5212
+ this.#limit = options.number.limit.value;
5213
+ const groups = this.#container.actualOptions.particles.groups;
5214
+ for (const existingGroup of this.#groupLimits.keys()) {
5215
+ if (!(existingGroup in groups)) {
5216
+ this.#groupLimits.delete(existingGroup);
5217
+ }
5218
+ }
5219
+ }
5220
+ else {
5221
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5222
+ if (limitValue > minLimit) {
5223
+ this.#groupLimits.set(group, {
5224
+ limit: limitValue,
5225
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5226
+ });
5227
+ }
5228
+ else {
5229
+ this.#groupLimits.delete(group);
5230
+ }
5231
+ }
5232
+ }
5233
+ #checkAndApplyLimits(group, currentCount) {
5234
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5235
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5236
+ return true;
5237
+ }
5238
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5239
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5240
+ const groupCount = this.filter(t => t.group === group).length;
5241
+ if (groupCount >= groupLimitEntry.limit) {
5242
+ return true;
5243
+ }
5244
+ }
5245
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5246
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5247
+ if (groupCountToRemove > minCount) {
5248
+ this.removeQuantity(groupCountToRemove, group);
5249
+ }
5250
+ }
5251
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5252
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5253
+ if (globalCountToRemove > minCount) {
5254
+ this.#removeAny(globalCountToRemove);
5255
+ }
5256
+ }
5257
+ return false;
5258
+ }
5163
5259
  #createBuckets(zLayers) {
5164
5260
  const bucketCount = Math.max(Math.floor(zLayers), one);
5165
5261
  return Array.from({ length: bucketCount }, () => []);
@@ -5182,6 +5278,70 @@
5182
5278
  }
5183
5279
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5184
5280
  }
5281
+ async #initDrawersAndUpdaters() {
5282
+ const container = this.#container;
5283
+ await container.initDrawersAndUpdaters();
5284
+ for (const drawer of container.effectDrawers.values()) {
5285
+ await drawer.init?.(container);
5286
+ }
5287
+ for (const drawer of container.shapeDrawers.values()) {
5288
+ await drawer.init?.(container);
5289
+ }
5290
+ }
5291
+ async #initPlugins() {
5292
+ await this.#initPluginsArrays();
5293
+ await this.#initDrawersAndUpdaters();
5294
+ let handled = false;
5295
+ const container = this.#container;
5296
+ for (const plugin of container.plugins) {
5297
+ handled = plugin.particlesInitialization?.() ?? handled;
5298
+ if (handled) {
5299
+ break;
5300
+ }
5301
+ }
5302
+ if (handled) {
5303
+ return;
5304
+ }
5305
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5306
+ for (const group in groups) {
5307
+ const groupOptions = groups[group];
5308
+ if (!groupOptions) {
5309
+ continue;
5310
+ }
5311
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5312
+ this.addParticle(undefined, groupOptions, group);
5313
+ }
5314
+ }
5315
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5316
+ this.addParticle();
5317
+ }
5318
+ }
5319
+ async #initPluginsArrays() {
5320
+ const container = this.#container;
5321
+ for (const plugin of container.plugins) {
5322
+ if (plugin.redrawInit) {
5323
+ await plugin.redrawInit();
5324
+ }
5325
+ if (plugin.checkParticlePosition) {
5326
+ this.checkParticlePositionPlugins.push(plugin);
5327
+ }
5328
+ if (plugin.update) {
5329
+ this.#updatePlugins.push(plugin);
5330
+ }
5331
+ if (plugin.particleUpdate) {
5332
+ this.#particleUpdatePlugins.push(plugin);
5333
+ }
5334
+ if (plugin.postUpdate) {
5335
+ this.#postUpdatePlugins.push(plugin);
5336
+ }
5337
+ if (plugin.particleReset) {
5338
+ this.#particleResetPlugins.push(plugin);
5339
+ }
5340
+ if (plugin.postParticleUpdate) {
5341
+ this.#postParticleUpdatePlugins.push(plugin);
5342
+ }
5343
+ }
5344
+ }
5185
5345
  #insertParticleIntoBucket(particle) {
5186
5346
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5187
5347
  if (!bucket) {
@@ -5190,12 +5350,15 @@
5190
5350
  bucket.push(particle);
5191
5351
  this.#particleBuckets.set(particle.id, bucketIndex);
5192
5352
  }
5193
- #removeParticle(index, group, override) {
5353
+ #removeAny(quantity) {
5354
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5355
+ }
5356
+ #removeParticle(index, group, override, matchGroup = true) {
5194
5357
  const particle = this.#array[index];
5195
5358
  if (!particle) {
5196
5359
  return false;
5197
5360
  }
5198
- if (particle.group !== group) {
5361
+ if (matchGroup && particle.group !== group) {
5199
5362
  return false;
5200
5363
  }
5201
5364
  this.#array.splice(index, deleteCount);
@@ -5282,7 +5445,7 @@
5282
5445
  particlesToDelete.add(particle);
5283
5446
  continue;
5284
5447
  }
5285
- this.grid.insert(particle);
5448
+ this.#grid.insert(particle);
5286
5449
  }
5287
5450
  return particlesToDelete;
5288
5451
  }
@@ -5458,7 +5621,6 @@
5458
5621
  this.shapeDrawers = new Map();
5459
5622
  this.particleUpdaters = [];
5460
5623
  this.plugins.length = 0;
5461
- this.#pluginManager.clearPlugins(this);
5462
5624
  this.destroyed = true;
5463
5625
  this.#onDestroy(remove);
5464
5626
  this.dispatchEvent(EventType.containerDestroyed);
@@ -5511,31 +5673,14 @@
5511
5673
  await this.initDrawersAndUpdaters();
5512
5674
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
5513
5675
  this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
5514
- this.plugins.length = 0;
5515
- this.particleDestroyedPlugins.length = 0;
5516
- this.particleCreatedPlugins.length = 0;
5517
- this.particlePositionPlugins.length = 0;
5518
- for (const [plugin, containerPlugin] of allContainerPlugins) {
5519
- if (plugin.needsPlugin(this.actualOptions)) {
5520
- this.plugins.push(containerPlugin);
5521
- if (containerPlugin.particleCreated) {
5522
- this.particleCreatedPlugins.push(containerPlugin);
5523
- }
5524
- if (containerPlugin.particleDestroyed) {
5525
- this.particleDestroyedPlugins.push(containerPlugin);
5526
- }
5527
- if (containerPlugin.particlePosition) {
5528
- this.particlePositionPlugins.push(containerPlugin);
5529
- }
5530
- }
5531
- }
5676
+ this.#initPlugins(allContainerPlugins);
5532
5677
  this.retina.init();
5533
5678
  this.canvas.init();
5534
5679
  this.updateActualOptions();
5535
5680
  this.canvas.initBackground();
5536
5681
  this.canvas.resize();
5537
5682
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
5538
- this.hdrMode = hdrOptions.mode;
5683
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
5539
5684
  this.peakNits = hdrOptions.peakNits;
5540
5685
  this.zLayers = zLayers;
5541
5686
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -5556,9 +5701,9 @@
5556
5701
  }
5557
5702
  async initDrawersAndUpdaters() {
5558
5703
  const pluginManager = this.#pluginManager;
5559
- this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
5560
- this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
5561
- this.particleUpdaters = await pluginManager.getUpdaters(this, true);
5704
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
5705
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
5706
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
5562
5707
  }
5563
5708
  pause() {
5564
5709
  if (!guardCheck(this)) {
@@ -5669,6 +5814,27 @@
5669
5814
  }
5670
5815
  return refresh;
5671
5816
  }
5817
+ #initPlugins(allContainerPlugins) {
5818
+ this.plugins.length = 0;
5819
+ this.particleDestroyedPlugins.length = 0;
5820
+ this.particleCreatedPlugins.length = 0;
5821
+ this.particlePositionPlugins.length = 0;
5822
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
5823
+ if (!plugin.needsPlugin(this.actualOptions)) {
5824
+ continue;
5825
+ }
5826
+ this.plugins.push(containerPlugin);
5827
+ if (containerPlugin.particleCreated) {
5828
+ this.particleCreatedPlugins.push(containerPlugin);
5829
+ }
5830
+ if (containerPlugin.particleDestroyed) {
5831
+ this.particleDestroyedPlugins.push(containerPlugin);
5832
+ }
5833
+ if (containerPlugin.particlePosition) {
5834
+ this.particlePositionPlugins.push(containerPlugin);
5835
+ }
5836
+ }
5837
+ }
5672
5838
  #nextFrame(timestamp) {
5673
5839
  try {
5674
5840
  if (!this.#smooth &&
@@ -5945,11 +6111,11 @@
5945
6111
  }
5946
6112
  }
5947
6113
  async #init() {
5948
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
5949
- if (!availablePathGenerators) {
6114
+ const pluginManager = this.#pluginManager;
6115
+ if (!pluginManager.initializers.pathGenerators) {
5950
6116
  return;
5951
6117
  }
5952
- this.availablePathGenerators = availablePathGenerators;
6118
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
5953
6119
  this.pathGenerators = new Map();
5954
6120
  for (const pathGenerator of this.pathGenerators.values()) {
5955
6121
  pathGenerator.init();
@@ -5965,6 +6131,7 @@
5965
6131
  exports.loadAmbientPreset = loadAmbientPreset;
5966
6132
  exports.tsParticles = tsParticles;
5967
6133
 
5968
- }));
6134
+ })(g.__tsParticlesInternals.presets.ambient = g.__tsParticlesInternals.presets.ambient || {});
6135
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6136
+
5969
6137
  Object.assign(globalThis.window || globalThis, { loadAmbientPreset: (globalThis.__tsParticlesInternals.presets.ambient || {}).loadAmbientPreset });
5970
- delete (globalThis.window || globalThis).tsparticlesInternalExports;