@tsparticles/preset-snow 4.4.0 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,35 @@
1
- (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.4.0 */
3
- (function (global, factory) {
4
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
6
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.presets = global.__tsParticlesInternals.presets || {}, global.__tsParticlesInternals.presets.snow = global.__tsParticlesInternals.presets.snow || {})));
7
- })(this, (function (exports) { 'use strict';
8
-
9
- const generatedAttribute = "generated", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
10
- x: 0,
11
- y: 0,
12
- z: 0,
13
- }, defaultTransform = {
14
- a: 1,
15
- b: 0,
16
- c: 0,
17
- d: 1,
18
- }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
1
+ (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
+ (function (g) {
3
+ /* Preset v4.5.1 */
4
+ g.__tsParticlesInternals = g.__tsParticlesInternals || {};
5
+ g.__tsParticlesInternals.presets = g.__tsParticlesInternals.presets || {};
6
+ (function (exports) {
7
+ 'use strict';
8
+
9
+ const errorPrefix = "tsParticles - Error", codedErrorPattern = /^\[tsParticles Error (TSP-\d+)\]\s*/, fullMatchIndex = 0, codeIndex = 1;
10
+ const wrap = (fn) => (...args) => {
11
+ fn(...args);
12
+ }, _logger = {
13
+ debug: wrap(console.debug),
14
+ error: (message, ...optionalParams) => {
15
+ if (typeof message === "string") {
16
+ const match = codedErrorPattern.exec(message);
17
+ if (match?.[codeIndex]) {
18
+ console.error(`tsParticles - ${match[codeIndex]} - ${message.slice(match[fullMatchIndex].length)}`, ...optionalParams);
19
+ return;
20
+ }
21
+ }
22
+ console.error(`${errorPrefix} - ${message}`, ...optionalParams);
23
+ },
24
+ info: wrap(console.info),
25
+ log: wrap(console.log),
26
+ trace: wrap(console.trace),
27
+ verbose: wrap(console.log),
28
+ warning: wrap(console.warn),
29
+ };
30
+ function getLogger() {
31
+ return _logger;
32
+ }
19
33
 
20
34
  var MoveDirection;
21
35
  (function (MoveDirection) {
@@ -32,6 +46,17 @@
32
46
  MoveDirection["inside"] = "inside";
33
47
  })(MoveDirection || (MoveDirection = {}));
34
48
 
49
+ const generatedAttribute = "generated", fullPercent = "100%", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
50
+ x: 0,
51
+ y: 0,
52
+ z: 0,
53
+ }, defaultTransform = {
54
+ a: 1,
55
+ b: 0,
56
+ c: 0,
57
+ d: 1,
58
+ }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, minRadius = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
59
+
35
60
  function getZ(source) {
36
61
  return "z" in source ? source.z : originPoint.z;
37
62
  }
@@ -147,6 +172,12 @@
147
172
  function isNumber(arg) {
148
173
  return typeof arg === "number";
149
174
  }
175
+ function isEnumValue(enumObject, arg) {
176
+ return Object.values(enumObject).some(enumValue => enumValue === arg);
177
+ }
178
+ function toEnumValue(enumObject, arg, defaultValue) {
179
+ return isEnumValue(enumObject, arg) ? arg : defaultValue;
180
+ }
150
181
  function isObject(arg) {
151
182
  return typeof arg === "object" && arg !== null;
152
183
  }
@@ -270,11 +301,61 @@
270
301
  };
271
302
  }
272
303
  function parseAlpha(input) {
273
- const defaultAlpha = 1;
274
304
  if (!input) {
275
305
  return defaultAlpha;
276
306
  }
277
- return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
307
+ return input.endsWith("%") ? Number.parseFloat(input) / percentDenominator : Number.parseFloat(input);
308
+ }
309
+
310
+ const ErrorCodes$1 = {
311
+ engineVersionMismatch: "TSP-1001",
312
+ particlePositionNotFound: "TSP-1002",
313
+ pluginRegisterAfterLoad: "TSP-1003",
314
+ offscreenCanvasUnsupported: "TSP-1101",
315
+ offscreenCanvasTransferFailed: "TSP-1102",
316
+ hdrEnableInvalid: "TSP-1201",
317
+ hdrModeInvalid: "TSP-1202",
318
+ hdrPeakNitsInvalid: "TSP-1203",
319
+ };
320
+
321
+ const messages = {
322
+ [ErrorCodes$1.engineVersionMismatch]: "The tsParticles version is different from the loaded plugins version. Engine version: {0}. Plugin version: {1}",
323
+ [ErrorCodes$1.particlePositionNotFound]: "a valid position cannot be found for particle",
324
+ [ErrorCodes$1.pluginRegisterAfterLoad]: "Register plugins can only be done before calling tsParticles.load()",
325
+ [ErrorCodes$1.offscreenCanvasUnsupported]: "OffscreenCanvas is required but not supported by this browser",
326
+ [ErrorCodes$1.offscreenCanvasTransferFailed]: "OffscreenCanvas transfer failed",
327
+ [ErrorCodes$1.hdrEnableInvalid]: 'Invalid HDR "enable" value: expected a boolean, got "{0}"',
328
+ [ErrorCodes$1.hdrModeInvalid]: 'Invalid HDR "mode" value: expected one of {0}, got "{1}"',
329
+ [ErrorCodes$1.hdrPeakNitsInvalid]: 'Invalid HDR "peakNits" value: expected a positive number, got "{0}"',
330
+ };
331
+
332
+ let registeredMessages;
333
+ const ensureRegisteredMessages = () => {
334
+ registeredMessages ??= new Map(Object.entries(messages));
335
+ return registeredMessages;
336
+ }, toStringValue$1 = (value) => String(value), format = (template, args) => {
337
+ return template.replace(/\{(\d+)\}/g, (match, index) => {
338
+ const arg = args[Number(index)];
339
+ return arg === undefined ? match : toStringValue$1(arg);
340
+ });
341
+ };
342
+ function addErrorMessages(extraMessages) {
343
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
344
+ return;
345
+ }
346
+ const catalog = ensureRegisteredMessages();
347
+ for (const [code, message] of Object.entries(extraMessages)) {
348
+ if (message !== undefined) {
349
+ catalog.set(code, message);
350
+ }
351
+ }
352
+ }
353
+ function getErrorMessage(code, ...args) {
354
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
355
+ return `[tsParticles Error ${code}] https://particles.js.org/errors/#${code}`;
356
+ }
357
+ const template = ensureRegisteredMessages().get(code) ?? `Unknown error code ${code}`;
358
+ return format(template, args);
278
359
  }
279
360
 
280
361
  var OutModeDirection;
@@ -291,7 +372,6 @@
291
372
  PixelMode["percent"] = "percent";
292
373
  })(PixelMode || (PixelMode = {}));
293
374
 
294
- const minRadius = 0;
295
375
  function isForbiddenKey(key) {
296
376
  return key === "__proto__" || key === "constructor" || key === "prototype";
297
377
  }
@@ -487,22 +567,84 @@
487
567
  element.removeEventListener(event, handler, removeOptions);
488
568
  }
489
569
  }
490
- async function getItemsFromInitializer(container, map, initializers, force = false) {
491
- let res = map.get(container);
492
- if (!res || force) {
493
- res = await Promise.all([...initializers.values()].map(t => t(container)));
494
- map.set(container, res);
570
+ async function getItemsFromInitializer(container, initializers) {
571
+ return await Promise.all([...initializers.values()].map(t => t(container)));
572
+ }
573
+ async function getItemMapFromInitializer(container, initializers) {
574
+ const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
575
+ return new Map(entries);
576
+ }
577
+ async function getDataFromUrl(data) {
578
+ const url = itemFromSingleOrMultiple(data.url, data.index);
579
+ if (!url) {
580
+ return data.fallback;
495
581
  }
496
- return res;
582
+ try {
583
+ const response = await fetch(url);
584
+ if (response.ok) {
585
+ const current = (await response.json());
586
+ if (isSourceOptionsValue(current)) {
587
+ return current;
588
+ }
589
+ getLogger().error("invalid configuration data while retrieving config file");
590
+ }
591
+ else {
592
+ getLogger().error(`${response.status.toString()} while retrieving config file`);
593
+ }
594
+ }
595
+ catch (error) {
596
+ getLogger().error(`error while retrieving config file: ${error}`);
597
+ }
598
+ return data.fallback;
497
599
  }
498
- async function getItemMapFromInitializer(container, map, initializers, force = false) {
499
- let res = map.get(container);
500
- if (!res || force) {
501
- const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item])));
502
- res = new Map(entries);
503
- map.set(container, res);
600
+ function isSourceOptionsValue(value) {
601
+ if (isArray(value)) {
602
+ return value.every((entry) => isObject(entry) && !isArray(entry));
504
603
  }
505
- return res;
604
+ return isObject(value) && !isArray(value);
605
+ }
606
+ function getCanvasFromContainer(domContainer) {
607
+ let canvasEl;
608
+ const documentSafe = safeDocument(), isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
609
+ if (isCanvas) {
610
+ canvasEl = domContainer;
611
+ canvasEl.dataset[generatedAttribute] ??= generatedFalse;
612
+ if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
613
+ canvasEl.style.width ||= fullPercent;
614
+ canvasEl.style.height ||= fullPercent;
615
+ canvasEl.style.pointerEvents = "none";
616
+ canvasEl.style.setProperty("pointer-events", "none");
617
+ }
618
+ }
619
+ else {
620
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
621
+ if (foundCanvas) {
622
+ canvasEl = foundCanvas;
623
+ canvasEl.dataset[generatedAttribute] = generatedFalse;
624
+ }
625
+ else {
626
+ canvasEl = documentSafe.createElement(canvasTag);
627
+ canvasEl.dataset[generatedAttribute] = generatedTrue;
628
+ domContainer.appendChild(canvasEl);
629
+ }
630
+ canvasEl.style.width ||= fullPercent;
631
+ canvasEl.style.height ||= fullPercent;
632
+ canvasEl.style.pointerEvents = "none";
633
+ canvasEl.style.setProperty("pointer-events", "none");
634
+ }
635
+ return canvasEl;
636
+ }
637
+ function getDomContainer(id, source) {
638
+ const documentSafe = safeDocument();
639
+ let domContainer = source ?? documentSafe.getElementById(id);
640
+ if (domContainer) {
641
+ return domContainer;
642
+ }
643
+ domContainer = documentSafe.createElement("canvas");
644
+ domContainer.id = id;
645
+ domContainer.dataset[generatedAttribute] = generatedTrue;
646
+ documentSafe.body.append(domContainer);
647
+ return domContainer;
506
648
  }
507
649
 
508
650
  class EventDispatcher {
@@ -512,12 +654,8 @@
512
654
  }
513
655
  addEventListener(type, listener) {
514
656
  this.removeEventListener(type, listener);
515
- let arr = this.#listeners.get(type);
516
- if (!arr) {
517
- arr = [];
518
- this.#listeners.set(type, arr);
519
- }
520
- arr.push(listener);
657
+ const arr = this.#listeners.get(type);
658
+ this.#listeners.set(type, arr ? [...arr, listener] : [listener]);
521
659
  }
522
660
  dispatchEvent(type, args) {
523
661
  const listeners = this.#listeners.get(type);
@@ -549,7 +687,7 @@
549
687
  this.#listeners.delete(type);
550
688
  }
551
689
  else {
552
- arr.splice(idx, deleteCount);
690
+ this.#listeners.set(type, [...arr.slice(minIndex, idx), ...arr.slice(idx + deleteCount)]);
553
691
  }
554
692
  }
555
693
  }
@@ -573,7 +711,6 @@
573
711
  class PluginManager {
574
712
  colorManagers = new Map();
575
713
  easingFunctions = new Map();
576
- effectDrawers = new Map();
577
714
  initializers = {
578
715
  effects: new Map(),
579
716
  shapes: new Map(),
@@ -582,8 +719,6 @@
582
719
  palettes = new Map();
583
720
  plugins = [];
584
721
  presets = new Map();
585
- shapeDrawers = new Map();
586
- updaters = new Map();
587
722
  #allLoadersSet = new Set();
588
723
  #configs = new Map();
589
724
  #engine;
@@ -601,57 +736,79 @@
601
736
  }
602
737
  return res;
603
738
  }
739
+ get initialized() {
740
+ return this.#initialized;
741
+ }
604
742
  addColorManager(name, manager) {
743
+ if (this.#initialized) {
744
+ return;
745
+ }
605
746
  this.colorManagers.set(name, manager);
606
747
  }
607
748
  addConfig(config) {
749
+ if (this.#initialized) {
750
+ return;
751
+ }
608
752
  const key = config.key ?? config.name ?? "default";
609
753
  this.#configs.set(key, config);
610
754
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
611
755
  }
612
756
  addEasing(name, easing) {
757
+ if (this.#initialized) {
758
+ return;
759
+ }
613
760
  if (this.easingFunctions.get(name)) {
614
761
  return;
615
762
  }
616
763
  this.easingFunctions.set(name, easing);
617
764
  }
618
765
  addEffect(effect, drawer) {
766
+ if (this.#initialized) {
767
+ return;
768
+ }
619
769
  this.initializers.effects.set(effect, drawer);
620
770
  }
621
771
  addPalette(name, palette) {
772
+ if (this.#initialized) {
773
+ return;
774
+ }
622
775
  this.palettes.set(name, palette);
623
776
  }
624
777
  addParticleUpdater(name, updaterInitializer) {
778
+ if (this.#initialized) {
779
+ return;
780
+ }
625
781
  this.initializers.updaters.set(name, updaterInitializer);
626
782
  }
627
783
  addPlugin(plugin) {
784
+ if (this.#initialized) {
785
+ return;
786
+ }
628
787
  if (this.getPlugin(plugin.id)) {
629
788
  return;
630
789
  }
631
790
  this.plugins.push(plugin);
632
791
  }
633
792
  addPreset(preset, options, override = false) {
793
+ if (this.#initialized) {
794
+ return;
795
+ }
634
796
  if (!(override || !this.getPreset(preset))) {
635
797
  return;
636
798
  }
637
799
  this.presets.set(preset, options);
638
800
  }
639
801
  addShape(shapes, drawer) {
802
+ if (this.#initialized) {
803
+ return;
804
+ }
640
805
  for (const shape of shapes) {
641
806
  this.initializers.shapes.set(shape, drawer);
642
807
  }
643
808
  }
644
- clearPlugins(container) {
645
- this.effectDrawers.delete(container);
646
- this.shapeDrawers.delete(container);
647
- this.updaters.delete(container);
648
- }
649
809
  getEasing(name) {
650
810
  return this.easingFunctions.get(name) ?? ((value) => value);
651
811
  }
652
- getEffectDrawers(container, force = false) {
653
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
654
- }
655
812
  getPalette(name) {
656
813
  return this.palettes.get(name);
657
814
  }
@@ -661,12 +818,6 @@
661
818
  getPreset(preset) {
662
819
  return this.presets.get(preset);
663
820
  }
664
- async getShapeDrawers(container, force = false) {
665
- return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
666
- }
667
- async getUpdaters(container, force = false) {
668
- return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
669
- }
670
821
  async init() {
671
822
  if (this.#initialized || this.#isRunningLoaders) {
672
823
  return;
@@ -685,16 +836,9 @@
685
836
  this.#initialized = true;
686
837
  }
687
838
  }
688
- loadParticlesOptions(container, options, ...sourceOptions) {
689
- const updaters = this.updaters.get(container);
690
- if (!updaters) {
691
- return;
692
- }
693
- updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
694
- }
695
839
  async register(...loaders) {
696
840
  if (this.#initialized) {
697
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
841
+ throw new Error(getErrorMessage(ErrorCodes$1.pluginRegisterAfterLoad));
698
842
  }
699
843
  for (const loader of loaders) {
700
844
  if (this.#isRunningLoaders) {
@@ -706,88 +850,15 @@
706
850
  }
707
851
  }
708
852
  async #runLoader(loader, executed, allLoaders) {
709
- if (executed.has(loader))
853
+ if (executed.has(loader)) {
710
854
  return;
855
+ }
711
856
  executed.add(loader);
712
857
  allLoaders.add(loader);
713
858
  await loader(this.#engine);
714
859
  }
715
860
  }
716
861
 
717
- const errorPrefix = "tsParticles - Error";
718
- const wrap = (fn) => (...args) => {
719
- fn(...args);
720
- }, _logger = {
721
- debug: wrap(console.debug),
722
- error: (message, ...optionalParams) => {
723
- console.error(`${errorPrefix} - ${message}`, ...optionalParams);
724
- },
725
- info: wrap(console.info),
726
- log: wrap(console.log),
727
- trace: wrap(console.trace),
728
- verbose: wrap(console.log),
729
- warning: wrap(console.warn),
730
- };
731
- function getLogger() {
732
- return _logger;
733
- }
734
-
735
- const fullPercent = "100%";
736
- async function getDataFromUrl(data) {
737
- const url = itemFromSingleOrMultiple(data.url, data.index);
738
- if (!url) {
739
- return data.fallback;
740
- }
741
- const response = await fetch(url);
742
- if (response.ok) {
743
- return (await response.json());
744
- }
745
- getLogger().error(`${response.status.toString()} while retrieving config file`);
746
- return data.fallback;
747
- }
748
- const getCanvasFromContainer = (domContainer) => {
749
- const documentSafe = safeDocument();
750
- let canvasEl;
751
- const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
752
- if (isCanvas) {
753
- canvasEl = domContainer;
754
- canvasEl.dataset[generatedAttribute] ??= generatedFalse;
755
- if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
756
- canvasEl.style.width ||= fullPercent;
757
- canvasEl.style.height ||= fullPercent;
758
- canvasEl.style.pointerEvents = "none";
759
- canvasEl.style.setProperty("pointer-events", "none");
760
- }
761
- }
762
- else {
763
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
764
- if (foundCanvas) {
765
- canvasEl = foundCanvas;
766
- canvasEl.dataset[generatedAttribute] = generatedFalse;
767
- }
768
- else {
769
- canvasEl = documentSafe.createElement(canvasTag);
770
- canvasEl.dataset[generatedAttribute] = generatedTrue;
771
- domContainer.appendChild(canvasEl);
772
- }
773
- canvasEl.style.width ||= fullPercent;
774
- canvasEl.style.height ||= fullPercent;
775
- canvasEl.style.pointerEvents = "none";
776
- canvasEl.style.setProperty("pointer-events", "none");
777
- }
778
- return canvasEl;
779
- }, getDomContainer = (id, source) => {
780
- const documentSafe = safeDocument();
781
- let domContainer = source ?? documentSafe.getElementById(id);
782
- if (domContainer) {
783
- return domContainer;
784
- }
785
- domContainer = documentSafe.createElement("canvas");
786
- domContainer.id = id;
787
- domContainer.dataset[generatedAttribute] = generatedTrue;
788
- documentSafe.body.append(domContainer);
789
- return domContainer;
790
- };
791
862
  class Engine {
792
863
  pluginManager = new PluginManager(this);
793
864
  #domArray = [];
@@ -797,7 +868,7 @@
797
868
  return this.#domArray;
798
869
  }
799
870
  get version() {
800
- return "4.4.0";
871
+ return "4.5.1";
801
872
  }
802
873
  addEventListener(type, listener) {
803
874
  this.#eventDispatcher.addEventListener(type, listener);
@@ -806,7 +877,7 @@
806
877
  if (this.version === pluginVersion) {
807
878
  return;
808
879
  }
809
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
880
+ throw new Error(getErrorMessage(ErrorCodes$1.engineVersionMismatch, this.version, pluginVersion));
810
881
  }
811
882
  dispatchEvent(type, args) {
812
883
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -1055,20 +1126,6 @@
1055
1126
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1056
1127
  })(DrawLayer || (DrawLayer = {}));
1057
1128
 
1058
- class OptionLoader {
1059
- load(data) {
1060
- if (isNull(data)) {
1061
- return;
1062
- }
1063
- this.doLoad(data);
1064
- }
1065
- }
1066
- function loadOptions(options, ...sourceOptionsArr) {
1067
- for (const sourceOptions of sourceOptionsArr) {
1068
- options.load(sourceOptions);
1069
- }
1070
- }
1071
-
1072
1129
  function loadProperty(obj, key, value) {
1073
1130
  if (value !== undefined) {
1074
1131
  obj[key] = value;
@@ -1100,6 +1157,20 @@
1100
1157
  }
1101
1158
  }
1102
1159
 
1160
+ class OptionLoader {
1161
+ load(data) {
1162
+ if (isNull(data)) {
1163
+ return;
1164
+ }
1165
+ this.doLoad(data);
1166
+ }
1167
+ }
1168
+ function loadOptions(options, ...sourceOptionsArr) {
1169
+ for (const sourceOptions of sourceOptionsArr) {
1170
+ options.load(sourceOptions);
1171
+ }
1172
+ }
1173
+
1103
1174
  class AnimationOptions extends OptionLoader {
1104
1175
  count = 0;
1105
1176
  decay = 0;
@@ -1250,15 +1321,14 @@
1250
1321
  peakNits = defaultPeakNits;
1251
1322
  doLoad(data) {
1252
1323
  if (data.enable !== undefined && !isBoolean(data.enable)) {
1253
- throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
1324
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrEnableInvalid, String(data.enable)));
1254
1325
  }
1255
- if (data.mode !== undefined &&
1256
- (typeof data.mode !== "string" || !Object.values(HdrMode).includes(data.mode))) {
1257
- throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
1326
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1327
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1258
1328
  }
1259
1329
  if (data.peakNits !== undefined &&
1260
1330
  (!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
1261
- throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
1331
+ throw new Error(getErrorMessage(ErrorCodes$1.hdrPeakNitsInvalid, String(data.peakNits)));
1262
1332
  }
1263
1333
  loadProperty(this, "enable", data.enable);
1264
1334
  loadProperty(this, "mode", data.mode);
@@ -1636,12 +1706,10 @@
1636
1706
  plugin.loadParticlesOptions(this.#container, this, data);
1637
1707
  }
1638
1708
  }
1639
- const updaters = this.#pluginManager.updaters.get(this.#container);
1640
- if (updaters) {
1641
- for (const updater of updaters) {
1642
- if (updater.loadOptions) {
1643
- updater.loadOptions(this, data);
1644
- }
1709
+ const updaters = this.#container.particleUpdaters;
1710
+ for (const updater of updaters) {
1711
+ if (updater.loadOptions) {
1712
+ updater.loadOptions(this, data);
1645
1713
  }
1646
1714
  }
1647
1715
  }
@@ -1974,9 +2042,9 @@
1974
2042
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1975
2043
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1976
2044
  }
1977
- const temp1 = lNormalized < half
2045
+ const temp2 = lNormalized < half
1978
2046
  ? lNormalized * (sNormalizedOffset + sNormalized)
1979
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2047
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1980
2048
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1981
2049
  }
1982
2050
  function hslToRgbFloat(hsl) {
@@ -1985,9 +2053,9 @@
1985
2053
  const grayscaleValue = lNormalized * rgbMax;
1986
2054
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1987
2055
  }
1988
- const temp1 = lNormalized < half
2056
+ const temp2 = lNormalized < half
1989
2057
  ? lNormalized * (sNormalizedOffset + sNormalized)
1990
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2058
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1991
2059
  return { r: red, g: green, b: blue };
1992
2060
  }
1993
2061
  function hslaToRgba(hsla) {
@@ -2201,7 +2269,7 @@
2201
2269
  }
2202
2270
 
2203
2271
  async function loadBlendPlugin(engine) {
2204
- engine.checkVersion("4.4.0");
2272
+ engine.checkVersion("4.5.1");
2205
2273
  await engine.pluginManager.register(e => {
2206
2274
  e.pluginManager.addPlugin(new BlendPlugin());
2207
2275
  });
@@ -2238,7 +2306,7 @@
2238
2306
  }
2239
2307
 
2240
2308
  async function loadCircleShape(engine) {
2241
- engine.checkVersion("4.4.0");
2309
+ engine.checkVersion("4.5.1");
2242
2310
  await engine.pluginManager.register(e => {
2243
2311
  e.pluginManager.addShape(["circle"], () => {
2244
2312
  return Promise.resolve(new CircleDrawer());
@@ -2253,7 +2321,7 @@
2253
2321
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2254
2322
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2255
2323
  })(RgbIndexes$1 || (RgbIndexes$1 = {}));
2256
- const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
2324
+ const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, alphaFactor = 0xff;
2257
2325
  class HexColorManager {
2258
2326
  accepts(input) {
2259
2327
  return input.startsWith("#");
@@ -2286,7 +2354,7 @@
2286
2354
  }
2287
2355
 
2288
2356
  async function loadHexColorPlugin(engine) {
2289
- engine.checkVersion("4.4.0");
2357
+ engine.checkVersion("4.5.1");
2290
2358
  await engine.pluginManager.register(e => {
2291
2359
  e.pluginManager.addColorManager("hex", new HexColorManager());
2292
2360
  });
@@ -2326,7 +2394,7 @@
2326
2394
  if (!this.accepts(input)) {
2327
2395
  return;
2328
2396
  }
2329
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2397
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2330
2398
  return result
2331
2399
  ? hslaToRgba({
2332
2400
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2339,12 +2407,20 @@
2339
2407
  }
2340
2408
 
2341
2409
  async function loadHslColorPlugin(engine) {
2342
- engine.checkVersion("4.4.0");
2410
+ engine.checkVersion("4.5.1");
2343
2411
  await engine.pluginManager.register(e => {
2344
2412
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2345
2413
  });
2346
2414
  }
2347
2415
 
2416
+ const ErrorCodes = {
2417
+ baseMoverNotLoaded: "TSP-3203",
2418
+ };
2419
+
2420
+ const ErrorMessages = {
2421
+ [ErrorCodes.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2422
+ };
2423
+
2348
2424
  class MovePlugin {
2349
2425
  id = "move";
2350
2426
  #pluginManager;
@@ -2363,20 +2439,20 @@
2363
2439
  }
2364
2440
 
2365
2441
  async function loadMovePlugin(engine) {
2366
- engine.checkVersion("4.4.0");
2442
+ engine.checkVersion("4.5.1");
2443
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2444
+ addErrorMessages(ErrorMessages);
2445
+ }
2367
2446
  await engine.pluginManager.register(e => {
2368
2447
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2369
2448
  movePluginManager.initializers.pathGenerators ??= new Map();
2370
- movePluginManager.pathGenerators ??= new Map();
2371
2449
  movePluginManager.addPathGenerator = (name, generator) => {
2450
+ if (movePluginManager.initialized) {
2451
+ return;
2452
+ }
2372
2453
  movePluginManager.initializers.pathGenerators ??= new Map();
2373
2454
  movePluginManager.initializers.pathGenerators.set(name, generator);
2374
2455
  };
2375
- movePluginManager.getPathGenerators = async (container, force = false) => {
2376
- movePluginManager.initializers.pathGenerators ??= new Map();
2377
- movePluginManager.pathGenerators ??= new Map();
2378
- return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
2379
- };
2380
2456
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2381
2457
  });
2382
2458
  }
@@ -2573,7 +2649,7 @@
2573
2649
  }
2574
2650
 
2575
2651
  async function loadOpacityUpdater(engine) {
2576
- engine.checkVersion("4.4.0");
2652
+ engine.checkVersion("4.5.1");
2577
2653
  await engine.pluginManager.register(e => {
2578
2654
  e.pluginManager.addParticleUpdater("opacity", container => {
2579
2655
  return Promise.resolve(new OpacityUpdater(container));
@@ -2582,70 +2658,60 @@
2582
2658
  }
2583
2659
 
2584
2660
  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)) {
2588
- return;
2589
- }
2590
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2591
- data.particle.position.x = data.size + data.offset.x;
2592
- }
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;
2595
- }
2596
- const velocity = data.particle.velocity.x;
2597
- let bounced = false;
2598
- 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;
2604
- }
2605
- if (!bounced) {
2606
- return;
2607
- }
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)) {
2661
+ const bounceAxisConfigs = {
2662
+ [OutModeDirection.left]: {
2663
+ axis: "x",
2664
+ canvas: "width",
2665
+ bounce: "horizontal",
2666
+ negativeEdge: "right",
2667
+ positiveEdge: "left",
2668
+ negative: true,
2669
+ },
2670
+ [OutModeDirection.right]: {
2671
+ axis: "x",
2672
+ canvas: "width",
2673
+ bounce: "horizontal",
2674
+ negativeEdge: "right",
2675
+ positiveEdge: "left",
2676
+ negative: false,
2677
+ },
2678
+ [OutModeDirection.top]: {
2679
+ axis: "y",
2680
+ canvas: "height",
2681
+ bounce: "vertical",
2682
+ negativeEdge: "bottom",
2683
+ positiveEdge: "top",
2684
+ negative: true,
2685
+ },
2686
+ [OutModeDirection.bottom]: {
2687
+ axis: "y",
2688
+ canvas: "height",
2689
+ bounce: "vertical",
2690
+ negativeEdge: "bottom",
2691
+ positiveEdge: "top",
2692
+ negative: false,
2693
+ },
2694
+ };
2695
+ function bounce(data) {
2696
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2622
2697
  return;
2623
2698
  }
2624
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2625
- data.particle.position.y = data.size + data.offset.y;
2699
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2700
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2701
+ position[config.axis] = data.size + data.offset[config.axis];
2626
2702
  }
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;
2703
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2704
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2629
2705
  }
2630
- const velocity = data.particle.velocity.y;
2631
- let bounced = false;
2632
2706
  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;
2707
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2708
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2638
2709
  }
2639
- if (!bounced) {
2710
+ else {
2640
2711
  return;
2641
2712
  }
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
- }
2713
+ const minPos = data.offset[config.axis] + data.size;
2714
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2649
2715
  if (data.outMode === OutMode.split) {
2650
2716
  data.particle.destroy();
2651
2717
  }
@@ -2661,26 +2727,19 @@
2661
2727
  OutMode.bounce,
2662
2728
  OutMode.split,
2663
2729
  ];
2664
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2730
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2665
2731
  }
2666
2732
  update(particle, direction, delta, outMode) {
2667
2733
  if (!this.modes.includes(outMode)) {
2668
2734
  return;
2669
2735
  }
2670
- const container = this.#container;
2671
- let handled = false;
2672
2736
  for (const plugin of this.#particleBouncePlugins) {
2673
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2674
- if (handled) {
2675
- break;
2737
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2738
+ return;
2676
2739
  }
2677
2740
  }
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 });
2741
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2742
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2684
2743
  }
2685
2744
  }
2686
2745
 
@@ -2769,108 +2828,109 @@
2769
2828
  return;
2770
2829
  }
2771
2830
  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;
2831
+ if (particle.outType === ParticleOutType.inside) {
2832
+ this.#applyOutModeInside(particle, container);
2833
+ }
2834
+ else {
2835
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2836
+ return;
2799
2837
  }
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;
2838
+ if (particle.outType === ParticleOutType.outside) {
2839
+ this.#applyParticleModeOutside(particle);
2840
+ }
2841
+ else {
2842
+ this.#applyOutModeNormal(particle, container, direction);
2871
2843
  }
2872
2844
  }
2873
2845
  }
2846
+ #applyOutModeInside(particle, container) {
2847
+ const { x: vx, y: vy } = particle.velocity;
2848
+ updateVector.setTo(originPoint);
2849
+ updateVector.length = particle.moveCenter.radius;
2850
+ updateVector.angle = particle.velocity.angle + Math.PI;
2851
+ updateVector.addTo(particle.moveCenter);
2852
+ const { dx, dy } = getDistances(particle.position, updateVector);
2853
+ if ((vx <= minVelocity && dx >= minDistance) ||
2854
+ (vy <= minVelocity && dy >= minDistance) ||
2855
+ (vx >= minVelocity && dx <= minDistance) ||
2856
+ (vy >= minVelocity && dy <= minDistance)) {
2857
+ return;
2858
+ }
2859
+ particle.position.x = Math.floor(randomInRangeValue({
2860
+ min: 0,
2861
+ max: container.canvas.size.width,
2862
+ }));
2863
+ particle.position.y = Math.floor(randomInRangeValue({
2864
+ min: 0,
2865
+ max: container.canvas.size.height,
2866
+ }));
2867
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2868
+ particle.direction = Math.atan2(-newDy, -newDx);
2869
+ particle.velocity.angle = particle.direction;
2870
+ particle.justWarped = true;
2871
+ }
2872
+ #applyOutModeNormal(particle, container, direction) {
2873
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2874
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2875
+ left: -sizeValue - particle.offset.x,
2876
+ right: canvasSize.width + sizeValue + particle.offset.x,
2877
+ top: -sizeValue - particle.offset.y,
2878
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2879
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2880
+ particle.position.x = newPos.left;
2881
+ particle.initialPosition.x = particle.position.x;
2882
+ if (!warp) {
2883
+ particle.position.y = getRandom() * canvasSize.height;
2884
+ particle.initialPosition.y = particle.position.y;
2885
+ }
2886
+ particle.justWarped = true;
2887
+ }
2888
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2889
+ particle.position.x = newPos.right;
2890
+ particle.initialPosition.x = particle.position.x;
2891
+ if (!warp) {
2892
+ particle.position.y = getRandom() * canvasSize.height;
2893
+ particle.initialPosition.y = particle.position.y;
2894
+ }
2895
+ particle.justWarped = true;
2896
+ }
2897
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2898
+ if (!warp) {
2899
+ particle.position.x = getRandom() * canvasSize.width;
2900
+ particle.initialPosition.x = particle.position.x;
2901
+ }
2902
+ particle.position.y = newPos.top;
2903
+ particle.initialPosition.y = particle.position.y;
2904
+ particle.justWarped = true;
2905
+ }
2906
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2907
+ if (!warp) {
2908
+ particle.position.x = getRandom() * canvasSize.width;
2909
+ particle.initialPosition.x = particle.position.x;
2910
+ }
2911
+ particle.position.y = newPos.bottom;
2912
+ particle.initialPosition.y = particle.position.y;
2913
+ particle.justWarped = true;
2914
+ }
2915
+ }
2916
+ #applyParticleModeOutside(particle) {
2917
+ particle.position.x =
2918
+ Math.floor(randomInRangeValue({
2919
+ min: -particle.moveCenter.radius,
2920
+ max: particle.moveCenter.radius,
2921
+ })) + particle.moveCenter.x;
2922
+ particle.position.y =
2923
+ Math.floor(randomInRangeValue({
2924
+ min: -particle.moveCenter.radius,
2925
+ max: particle.moveCenter.radius,
2926
+ })) + particle.moveCenter.y;
2927
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2928
+ if (particle.moveCenter.radius) {
2929
+ particle.direction = Math.atan2(dy, dx);
2930
+ particle.velocity.angle = particle.direction;
2931
+ }
2932
+ particle.justWarped = true;
2933
+ }
2874
2934
  }
2875
2935
 
2876
2936
  const checkOutMode = (outModes, outMode) => {
@@ -2918,7 +2978,7 @@
2918
2978
  }
2919
2979
 
2920
2980
  async function loadOutModesUpdater(engine) {
2921
- engine.checkVersion("4.4.0");
2981
+ engine.checkVersion("4.5.1");
2922
2982
  await engine.pluginManager.register(e => {
2923
2983
  e.pluginManager.addParticleUpdater("outModes", container => {
2924
2984
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2935,39 +2995,9 @@
2935
2995
  this.#pluginManager = pluginManager;
2936
2996
  }
2937
2997
  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
- }
2998
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
2999
+ this.#initFill(paint, particle, container);
3000
+ this.#initStroke(paint, particle, container);
2971
3001
  }
2972
3002
  isEnabled(particle) {
2973
3003
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2986,10 +3016,48 @@
2986
3016
  updateColor(particle.fillColor, delta, this.#container.hdr);
2987
3017
  updateColor(particle.strokeColor, delta, this.#container.hdr);
2988
3018
  }
3019
+ #initFill(paint, particle, container) {
3020
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3021
+ if (!fill) {
3022
+ particle.fillEnabled = false;
3023
+ particle.fillAnimation = undefined;
3024
+ particle.fillColor = undefined;
3025
+ particle.fillOpacity = defaultOpacity;
3026
+ return;
3027
+ }
3028
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3029
+ particle.fillEnabled = fill.enable;
3030
+ particle.fillOpacity = getRangeValue(fill.opacity);
3031
+ particle.fillAnimation = fillColor.animation;
3032
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3033
+ if (!fillHslColor) {
3034
+ return;
3035
+ }
3036
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3037
+ }
3038
+ #initStroke(paint, particle, container) {
3039
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3040
+ if (!stroke) {
3041
+ particle.strokeAnimation = undefined;
3042
+ particle.strokeColor = undefined;
3043
+ particle.strokeOpacity = defaultOpacity;
3044
+ particle.strokeWidth = 0;
3045
+ return;
3046
+ }
3047
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3048
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3049
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3050
+ particle.strokeAnimation = strokeColor.animation;
3051
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3052
+ if (!strokeHslColor) {
3053
+ return;
3054
+ }
3055
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3056
+ }
2989
3057
  }
2990
3058
 
2991
3059
  async function loadPaintUpdater(engine) {
2992
- engine.checkVersion("4.4.0");
3060
+ engine.checkVersion("4.5.1");
2993
3061
  await engine.pluginManager.register(e => {
2994
3062
  e.pluginManager.addParticleUpdater("paint", container => {
2995
3063
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -3031,7 +3099,7 @@
3031
3099
  if (!this.accepts(input)) {
3032
3100
  return;
3033
3101
  }
3034
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3102
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
3035
3103
  return result
3036
3104
  ? {
3037
3105
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -3044,7 +3112,7 @@
3044
3112
  }
3045
3113
 
3046
3114
  async function loadRgbColorPlugin(engine) {
3047
- engine.checkVersion("4.4.0");
3115
+ engine.checkVersion("4.5.1");
3048
3116
  await engine.pluginManager.register(e => {
3049
3117
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
3050
3118
  });
@@ -3129,7 +3197,7 @@
3129
3197
  }
3130
3198
 
3131
3199
  async function loadSizeUpdater(engine) {
3132
- engine.checkVersion("4.4.0");
3200
+ engine.checkVersion("4.5.1");
3133
3201
  await engine.pluginManager.register(e => {
3134
3202
  e.pluginManager.addParticleUpdater("size", container => {
3135
3203
  return Promise.resolve(new SizeUpdater(container));
@@ -3138,7 +3206,7 @@
3138
3206
  }
3139
3207
 
3140
3208
  async function loadBasic(engine) {
3141
- engine.checkVersion("4.4.0");
3209
+ engine.checkVersion("4.5.1");
3142
3210
  await engine.pluginManager.register(async (e) => {
3143
3211
  await Promise.all([
3144
3212
  loadBlendPlugin(e),
@@ -3274,7 +3342,7 @@
3274
3342
  }
3275
3343
 
3276
3344
  async function loadWobbleUpdater(engine) {
3277
- engine.checkVersion("4.4.0");
3345
+ engine.checkVersion("4.5.1");
3278
3346
  await engine.pluginManager.register(e => {
3279
3347
  e.pluginManager.addParticleUpdater("wobble", container => {
3280
3348
  return Promise.resolve(new WobbleUpdater(container));
@@ -3386,21 +3454,28 @@
3386
3454
  });
3387
3455
  }
3388
3456
  clear() {
3457
+ const visited = new Set();
3389
3458
  for (const plugin of this.#canvasClearPlugins) {
3459
+ visited.add(plugin);
3390
3460
  if (plugin.canvasClear?.() ?? false) {
3391
- return;
3461
+ return plugin;
3392
3462
  }
3393
3463
  }
3394
3464
  for (const layer of Object.values(DrawLayer)) {
3395
3465
  if (typeof layer === "number") {
3396
3466
  for (const plugin of this.#getLayerPlugins(layer)) {
3467
+ if (visited.has(plugin)) {
3468
+ continue;
3469
+ }
3470
+ visited.add(plugin);
3397
3471
  if (plugin.canvasClear?.() ?? false) {
3398
- return;
3472
+ return plugin;
3399
3473
  }
3400
3474
  }
3401
3475
  }
3402
3476
  }
3403
3477
  this.canvasClear();
3478
+ return undefined;
3404
3479
  }
3405
3480
  destroy() {
3406
3481
  this.stop();
@@ -3478,8 +3553,7 @@
3478
3553
  });
3479
3554
  }
3480
3555
  drawParticles(delta) {
3481
- const { particles, actualOptions } = this.#container;
3482
- this.clear();
3556
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3483
3557
  particles.update(delta);
3484
3558
  this.draw(ctx => {
3485
3559
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -3501,7 +3575,9 @@
3501
3575
  }
3502
3576
  }
3503
3577
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3504
- plugin.canvasPaint?.();
3578
+ if (plugin !== clearedPlugin) {
3579
+ plugin.canvasPaint?.();
3580
+ }
3505
3581
  }
3506
3582
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3507
3583
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -3533,35 +3609,7 @@
3533
3609
  this.#layers[layer] = [];
3534
3610
  }
3535
3611
  }
3536
- for (const plugin of this.#container.plugins) {
3537
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3538
- this.#colorPlugins.push(plugin);
3539
- }
3540
- if (plugin.drawParticle) {
3541
- this.#drawParticlePlugins.push(plugin);
3542
- }
3543
- if (plugin.drawParticleSetup) {
3544
- this.#drawParticlesSetupPlugins.push(plugin);
3545
- }
3546
- if (plugin.drawParticleCleanup) {
3547
- this.#drawParticlesCleanupPlugins.push(plugin);
3548
- }
3549
- if (plugin.canvasClear) {
3550
- this.#canvasClearPlugins.push(plugin);
3551
- }
3552
- if (plugin.canvasPaint) {
3553
- this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
3554
- }
3555
- if (plugin.drawSettingsSetup) {
3556
- this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
3557
- }
3558
- if (plugin.draw) {
3559
- this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
3560
- }
3561
- if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
3562
- this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
3563
- }
3564
- }
3612
+ this.#initPluginsArray();
3565
3613
  }
3566
3614
  initUpdaters() {
3567
3615
  this.#preDrawUpdaters = [];
@@ -3770,6 +3818,45 @@
3770
3818
  this.#reusablePluginColors[sColorIndex] = sColor;
3771
3819
  return this.#reusablePluginColors;
3772
3820
  }
3821
+ #initLayerPlugin(plugin) {
3822
+ const layerMap = [
3823
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
3824
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
3825
+ [DrawLayer.PluginContent, !!plugin.draw],
3826
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
3827
+ ];
3828
+ for (const [layer, active] of layerMap) {
3829
+ if (active) {
3830
+ this.#layers[layer].push(plugin);
3831
+ }
3832
+ }
3833
+ }
3834
+ #initPluginsArray() {
3835
+ for (const plugin of this.#container.plugins) {
3836
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3837
+ this.#colorPlugins.push(plugin);
3838
+ }
3839
+ if (plugin.drawParticle) {
3840
+ this.#drawParticlePlugins.push(plugin);
3841
+ }
3842
+ if (plugin.drawParticleSetup) {
3843
+ this.#drawParticlesSetupPlugins.push(plugin);
3844
+ }
3845
+ if (plugin.drawParticleCleanup) {
3846
+ this.#drawParticlesCleanupPlugins.push(plugin);
3847
+ }
3848
+ if (plugin.canvasClear) {
3849
+ this.#canvasClearPlugins.push(plugin);
3850
+ }
3851
+ this.#initLayerPlugin(plugin);
3852
+ }
3853
+ }
3854
+ #isSupportedElement(element) {
3855
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
3856
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
3857
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
3858
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
3859
+ }
3773
3860
  #resolveBackgroundElement() {
3774
3861
  const background = this.#container.actualOptions.background;
3775
3862
  this.#backgroundElement = null;
@@ -3779,9 +3866,7 @@
3779
3866
  if (typeof background.element === "string") {
3780
3867
  if (typeof document !== "undefined") {
3781
3868
  const node = document.querySelector(background.element);
3782
- if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
3783
- (typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
3784
- (typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
3869
+ if (node && this.#isSupportedElement(node)) {
3785
3870
  this.#backgroundElement = node;
3786
3871
  }
3787
3872
  else if (node) {
@@ -3792,10 +3877,7 @@
3792
3877
  }
3793
3878
  }
3794
3879
  }
3795
- else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
3796
- (typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
3797
- (typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
3798
- (typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
3880
+ else if (this.#isSupportedElement(background.element)) {
3799
3881
  this.#backgroundElement = background.element;
3800
3882
  }
3801
3883
  }
@@ -3813,7 +3895,7 @@
3813
3895
  return transferredCanvas;
3814
3896
  }
3815
3897
  if (typeof canvas.transferControlToOffscreen !== "function") {
3816
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
3898
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasUnsupported));
3817
3899
  }
3818
3900
  try {
3819
3901
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -3821,10 +3903,16 @@
3821
3903
  return offscreenCanvas;
3822
3904
  }
3823
3905
  catch {
3824
- throw new TypeError("OffscreenCanvas transfer failed");
3906
+ throw new TypeError(getErrorMessage(ErrorCodes$1.offscreenCanvasTransferFailed));
3825
3907
  }
3826
3908
  }, isHtmlCanvasElement = (canvas) => {
3827
3909
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
3910
+ }, getEffectiveColorSpace = (context) => {
3911
+ if (!context) {
3912
+ return undefined;
3913
+ }
3914
+ const colorInfo = context;
3915
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
3828
3916
  };
3829
3917
  function setStyle(canvas, style, important = false) {
3830
3918
  if (!style) {
@@ -3949,7 +4037,7 @@
3949
4037
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
3950
4038
  if (color) {
3951
4039
  const hdrOptions = container.actualOptions.hdr;
3952
- elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
4040
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
3953
4041
  }
3954
4042
  else {
3955
4043
  elementStyle.backgroundColor = "";
@@ -4069,11 +4157,11 @@
4069
4157
  const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4070
4158
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4071
4159
  safeMatchMedia("(dynamic-range: high)")?.matches;
4072
- container.hdr = canSupportHdr ?? false;
4073
- container.hdrMode = container.actualOptions.hdr.mode;
4160
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4074
4161
  container.peakNits = container.actualOptions.hdr.peakNits;
4075
4162
  const renderCanvas = this.renderCanvas;
4076
4163
  if (!renderCanvas) {
4164
+ container.hdr = canSupportHdr ?? false;
4077
4165
  return;
4078
4166
  }
4079
4167
  this.render.setContextSettings({
@@ -4092,7 +4180,6 @@
4092
4180
  context = null;
4093
4181
  }
4094
4182
  if (canSupportHdr && !context) {
4095
- container.hdr = false;
4096
4183
  const sdrSettings = {
4097
4184
  alpha: true,
4098
4185
  desynchronized: true,
@@ -4108,11 +4195,16 @@
4108
4195
  }
4109
4196
  }
4110
4197
  this.render.setContext(context);
4198
+ if (context) {
4199
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4200
+ if (effectiveColorSpace !== undefined) {
4201
+ container.hdr = effectiveColorSpace === "display-p3";
4202
+ }
4203
+ }
4111
4204
  }
4112
4205
  #initHdrListeners() {
4113
4206
  this.#removeHdrListeners();
4114
4207
  const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4115
- this.#recreateRenderCanvas();
4116
4208
  this.#initContext();
4117
4209
  this.initBackground();
4118
4210
  }, listeners = [];
@@ -4148,16 +4240,6 @@
4148
4240
  element.style.setProperty(key, value, "important");
4149
4241
  }
4150
4242
  }
4151
- #recreateRenderCanvas() {
4152
- const renderCanvas = this.renderCanvas;
4153
- if (!renderCanvas) {
4154
- return;
4155
- }
4156
- if (this.domElement) {
4157
- return;
4158
- }
4159
- this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
4160
- }
4161
4243
  #removeHdrListeners() {
4162
4244
  if (!this.#hdrMediaListeners) {
4163
4245
  return;
@@ -4229,6 +4311,10 @@
4229
4311
  this.#manageListeners(true);
4230
4312
  }
4231
4313
  removeListeners() {
4314
+ if (this.#resizeTimeout !== undefined) {
4315
+ clearTimeout(this.#resizeTimeout);
4316
+ this.#resizeTimeout = undefined;
4317
+ }
4232
4318
  this.#manageListeners(false);
4233
4319
  }
4234
4320
  #handleVisibilityChange() {
@@ -4256,8 +4342,7 @@
4256
4342
  this.#resizeTimeout = undefined;
4257
4343
  }
4258
4344
  const handleResize = async () => {
4259
- const canvas = this.#container.canvas;
4260
- await canvas.windowResize();
4345
+ await this.#container.canvas.windowResize();
4261
4346
  };
4262
4347
  this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
4263
4348
  }
@@ -4285,8 +4370,8 @@
4285
4370
  }
4286
4371
  else if (!this.#resizeObserver && add && canvasEl) {
4287
4372
  this.#resizeObserver = new ResizeObserver((entries) => {
4288
- const entry = entries.find(e => e.target === canvasEl);
4289
- if (!entry) {
4373
+ const res = entries.some(e => e.target === canvasEl);
4374
+ if (!res) {
4290
4375
  return;
4291
4376
  }
4292
4377
  this.#handleWindowResize();
@@ -4550,10 +4635,10 @@
4550
4635
  return this.#modifiers.find(m => m.id === id);
4551
4636
  }
4552
4637
  getOpacity() {
4553
- 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;
4554
- this.#cachedOpacityData.fillOpacity = fillOpacity;
4638
+ 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;
4639
+ this.#cachedOpacityData.fillOpacity = this.fillOpacity ?? defaultOpacity$1;
4555
4640
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
4556
- this.#cachedOpacityData.strokeOpacity = strokeOpacity;
4641
+ this.#cachedOpacityData.strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
4557
4642
  return this.#cachedOpacityData;
4558
4643
  }
4559
4644
  getPosition() {
@@ -4800,7 +4885,7 @@
4800
4885
  #initPosition(position) {
4801
4886
  const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
4802
4887
  if (!initialPosition) {
4803
- throw new Error("a valid position cannot be found for particle");
4888
+ throw new Error(getErrorMessage(ErrorCodes$1.particlePositionNotFound));
4804
4889
  }
4805
4890
  this.position = initialPosition;
4806
4891
  this.initialPosition = this.position.copy();
@@ -4849,6 +4934,12 @@
4849
4934
  this.#circlePoolIdx = 0;
4850
4935
  this.#rectanglePoolIdx = 0;
4851
4936
  }
4937
+ get cellSize() {
4938
+ return this.#cellSize;
4939
+ }
4940
+ set cellSize(value) {
4941
+ this.#pendingCellSize = value;
4942
+ }
4852
4943
  clear() {
4853
4944
  this.#cells.clear();
4854
4945
  const pendingCellSize = this.#pendingCellSize;
@@ -4872,40 +4963,46 @@
4872
4963
  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);
4873
4964
  for (let cx = minCellX; cx <= maxCellX; cx++) {
4874
4965
  for (let cy = minCellY; cy <= maxCellY; cy++) {
4875
- const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
4876
- if (!cellParticles) {
4877
- continue;
4878
- }
4879
- for (const p of cellParticles) {
4880
- if (check && !check(p)) {
4881
- continue;
4882
- }
4883
- if (range.contains(p.getPosition())) {
4884
- out.push(p);
4885
- }
4886
- }
4966
+ this.#queryCell(cx, cy, range, check, out);
4887
4967
  }
4888
4968
  }
4889
4969
  return out;
4890
4970
  }
4891
4971
  queryCircle(position, radius, check, out = []) {
4892
- const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
4893
- this.#releaseShapes();
4894
- return result;
4972
+ const circle = this.#acquireCircle(position.x, position.y, radius);
4973
+ try {
4974
+ return this.query(circle, check, out);
4975
+ }
4976
+ finally {
4977
+ this.#circlePoolIdx--;
4978
+ }
4895
4979
  }
4896
4980
  queryRectangle(position, size, check, out = []) {
4897
- const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
4898
- this.#releaseShapes();
4899
- return result;
4900
- }
4901
- setCellSize(cellSize) {
4902
- this.#pendingCellSize = cellSize;
4981
+ const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height);
4982
+ try {
4983
+ return this.query(rect, check, out);
4984
+ }
4985
+ finally {
4986
+ this.#rectanglePoolIdx--;
4987
+ }
4903
4988
  }
4904
4989
  #acquireCircle(x, y, r) {
4905
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
4990
+ const idx = this.#circlePoolIdx++;
4991
+ let circle = this.#circlePool[idx];
4992
+ if (!circle) {
4993
+ circle = new Circle(x, y, r);
4994
+ this.#circlePool[idx] = circle;
4995
+ }
4996
+ return circle.reset(x, y, r);
4906
4997
  }
4907
4998
  #acquireRectangle(x, y, w, h) {
4908
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
4999
+ const idx = this.#rectanglePoolIdx++;
5000
+ let rectangle = this.#rectanglePool[idx];
5001
+ if (!rectangle) {
5002
+ rectangle = new Rectangle(x, y, w, h);
5003
+ this.#rectanglePool[idx] = rectangle;
5004
+ }
5005
+ return rectangle.reset(x, y, w, h);
4909
5006
  }
4910
5007
  #cellKeyFromCoords(x, y) {
4911
5008
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -4932,17 +5029,27 @@
4932
5029
  }
4933
5030
  return null;
4934
5031
  }
4935
- #releaseShapes() {
4936
- this.#circlePoolIdx = 0;
4937
- this.#rectanglePoolIdx = 0;
5032
+ #queryCell(cx, cy, range, check, out) {
5033
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5034
+ if (!cellParticles) {
5035
+ return;
5036
+ }
5037
+ for (const p of cellParticles) {
5038
+ if (check && !check(p)) {
5039
+ continue;
5040
+ }
5041
+ if (range.contains(p.getPosition())) {
5042
+ out.push(p);
5043
+ }
5044
+ }
4938
5045
  }
4939
5046
  }
4940
5047
 
4941
5048
  class ParticlesManager {
4942
5049
  checkParticlePositionPlugins;
4943
- grid;
4944
5050
  #array;
4945
5051
  #container;
5052
+ #grid;
4946
5053
  #groupLimits;
4947
5054
  #limit;
4948
5055
  #nextId;
@@ -4966,7 +5073,7 @@
4966
5073
  this.#groupLimits = new Map();
4967
5074
  this.#particleBuckets = new Map();
4968
5075
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
4969
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5076
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
4970
5077
  this.checkParticlePositionPlugins = [];
4971
5078
  this.#particleResetPlugins = [];
4972
5079
  this.#particleUpdatePlugins = [];
@@ -4977,23 +5084,12 @@
4977
5084
  get count() {
4978
5085
  return this.#array.length;
4979
5086
  }
5087
+ get grid() {
5088
+ return this.#grid;
5089
+ }
4980
5090
  addParticle(position, overrideOptions, group, initializer) {
4981
- const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
4982
- if (limit > minLimit) {
4983
- switch (limitMode) {
4984
- case LimitMode.delete: {
4985
- const countToRemove = currentCount + countOffset - limit;
4986
- if (countToRemove > minCount) {
4987
- this.removeQuantity(countToRemove);
4988
- }
4989
- break;
4990
- }
4991
- case LimitMode.wait:
4992
- if (currentCount >= limit) {
4993
- return;
4994
- }
4995
- break;
4996
- }
5091
+ if (this.#checkAndApplyLimits(group, this.count)) {
5092
+ return;
4997
5093
  }
4998
5094
  try {
4999
5095
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5057,7 +5153,7 @@
5057
5153
  return this.#array[index];
5058
5154
  }
5059
5155
  async init() {
5060
- const container = this.#container, options = container.actualOptions;
5156
+ const container = this.#container;
5061
5157
  this.checkParticlePositionPlugins = [];
5062
5158
  this.#updatePlugins = [];
5063
5159
  this.#particleUpdatePlugins = [];
@@ -5066,59 +5162,16 @@
5066
5162
  this.#postParticleUpdatePlugins = [];
5067
5163
  this.#particleBuckets.clear();
5068
5164
  this.#resetBuckets(container.zLayers);
5069
- this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5070
- for (const plugin of container.plugins) {
5071
- if (plugin.redrawInit) {
5072
- await plugin.redrawInit();
5073
- }
5074
- if (plugin.checkParticlePosition) {
5075
- this.checkParticlePositionPlugins.push(plugin);
5076
- }
5077
- if (plugin.update) {
5078
- this.#updatePlugins.push(plugin);
5079
- }
5080
- if (plugin.particleUpdate) {
5081
- this.#particleUpdatePlugins.push(plugin);
5082
- }
5083
- if (plugin.postUpdate) {
5084
- this.#postUpdatePlugins.push(plugin);
5085
- }
5086
- if (plugin.particleReset) {
5087
- this.#particleResetPlugins.push(plugin);
5088
- }
5089
- if (plugin.postParticleUpdate) {
5090
- this.#postParticleUpdatePlugins.push(plugin);
5091
- }
5092
- }
5093
- await this.#container.initDrawersAndUpdaters();
5094
- for (const drawer of this.#container.effectDrawers.values()) {
5095
- await drawer.init?.(container);
5096
- }
5097
- for (const drawer of this.#container.shapeDrawers.values()) {
5098
- await drawer.init?.(container);
5099
- }
5100
- let handled = false;
5101
- for (const plugin of container.plugins) {
5102
- handled = plugin.particlesInitialization?.() ?? handled;
5103
- if (handled) {
5104
- break;
5105
- }
5106
- }
5107
- if (!handled) {
5108
- const particlesOptions = options.particles, groups = particlesOptions.groups;
5109
- for (const group in groups) {
5110
- const groupOptions = groups[group];
5111
- if (!groupOptions) {
5112
- continue;
5113
- }
5114
- for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5115
- this.addParticle(undefined, groupOptions, group);
5116
- }
5117
- }
5118
- for (let i = this.count; i < particlesOptions.number.value; i++) {
5119
- this.addParticle();
5165
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5166
+ this.#applyLimits(container.actualOptions.particles);
5167
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5168
+ for (const group in groups) {
5169
+ const groupData = groups[group];
5170
+ if (groupData) {
5171
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5120
5172
  }
5121
5173
  }
5174
+ await this.#initPlugins();
5122
5175
  }
5123
5176
  push(nb, position, overrideOptions, group) {
5124
5177
  for (let i = 0; i < nb; i++) {
@@ -5133,13 +5186,13 @@
5133
5186
  remove(particle, group, override) {
5134
5187
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5135
5188
  }
5136
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5189
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5137
5190
  if (index < minIndex || index > this.count) {
5138
5191
  return;
5139
5192
  }
5140
5193
  let deleted = 0;
5141
5194
  for (let i = index; deleted < quantity && i < this.count; i++) {
5142
- if (this.#removeParticle(i, group, override)) {
5195
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5143
5196
  i--;
5144
5197
  deleted++;
5145
5198
  }
@@ -5170,7 +5223,7 @@
5170
5223
  this.#resizeFactor = factor;
5171
5224
  }
5172
5225
  update(delta) {
5173
- this.grid.clear();
5226
+ this.#grid.clear();
5174
5227
  for (const plugin of this.#updatePlugins) {
5175
5228
  plugin.update?.(delta);
5176
5229
  }
@@ -5181,7 +5234,7 @@
5181
5234
  this.#updateParticlesPhase2(delta, particlesToDelete);
5182
5235
  if (particlesToDelete.size) {
5183
5236
  for (const particle of particlesToDelete) {
5184
- this.remove(particle);
5237
+ this.remove(particle, particle.group);
5185
5238
  }
5186
5239
  }
5187
5240
  this.#resizeFactor = undefined;
@@ -5192,12 +5245,7 @@
5192
5245
  #applyDensity(options, pluginsCount, group, groupOptions) {
5193
5246
  const numberOptions = options.number;
5194
5247
  if (!numberOptions.density.enable) {
5195
- if (group === undefined) {
5196
- this.#limit = numberOptions.limit.value;
5197
- }
5198
- else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
5199
- this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
5200
- }
5248
+ this.#applyLimits(options, group, groupOptions);
5201
5249
  return;
5202
5250
  }
5203
5251
  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);
@@ -5205,7 +5253,10 @@
5205
5253
  this.#limit = numberOptions.limit.value * densityFactor;
5206
5254
  }
5207
5255
  else {
5208
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5256
+ this.#groupLimits.set(group, {
5257
+ limit: numberOptions.limit.value * densityFactor,
5258
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5259
+ });
5209
5260
  }
5210
5261
  if (particlesCount < particlesNumber) {
5211
5262
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5214,6 +5265,55 @@
5214
5265
  this.removeQuantity(particlesCount - particlesNumber, group);
5215
5266
  }
5216
5267
  }
5268
+ #applyLimits(options, group, groupOptions) {
5269
+ if (group === undefined) {
5270
+ this.#limit = options.number.limit.value;
5271
+ const groups = this.#container.actualOptions.particles.groups;
5272
+ for (const existingGroup of this.#groupLimits.keys()) {
5273
+ if (!(existingGroup in groups)) {
5274
+ this.#groupLimits.delete(existingGroup);
5275
+ }
5276
+ }
5277
+ }
5278
+ else {
5279
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5280
+ if (limitValue > minLimit) {
5281
+ this.#groupLimits.set(group, {
5282
+ limit: limitValue,
5283
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5284
+ });
5285
+ }
5286
+ else {
5287
+ this.#groupLimits.delete(group);
5288
+ }
5289
+ }
5290
+ }
5291
+ #checkAndApplyLimits(group, currentCount) {
5292
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5293
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5294
+ return true;
5295
+ }
5296
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5297
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5298
+ const groupCount = this.filter(t => t.group === group).length;
5299
+ if (groupCount >= groupLimitEntry.limit) {
5300
+ return true;
5301
+ }
5302
+ }
5303
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5304
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5305
+ if (groupCountToRemove > minCount) {
5306
+ this.removeQuantity(groupCountToRemove, group);
5307
+ }
5308
+ }
5309
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5310
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5311
+ if (globalCountToRemove > minCount) {
5312
+ this.#removeAny(globalCountToRemove);
5313
+ }
5314
+ }
5315
+ return false;
5316
+ }
5217
5317
  #createBuckets(zLayers) {
5218
5318
  const bucketCount = Math.max(Math.floor(zLayers), one);
5219
5319
  return Array.from({ length: bucketCount }, () => []);
@@ -5236,6 +5336,70 @@
5236
5336
  }
5237
5337
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5238
5338
  }
5339
+ async #initDrawersAndUpdaters() {
5340
+ const container = this.#container;
5341
+ await container.initDrawersAndUpdaters();
5342
+ for (const drawer of container.effectDrawers.values()) {
5343
+ await drawer.init?.(container);
5344
+ }
5345
+ for (const drawer of container.shapeDrawers.values()) {
5346
+ await drawer.init?.(container);
5347
+ }
5348
+ }
5349
+ async #initPlugins() {
5350
+ await this.#initPluginsArrays();
5351
+ await this.#initDrawersAndUpdaters();
5352
+ let handled = false;
5353
+ const container = this.#container;
5354
+ for (const plugin of container.plugins) {
5355
+ handled = plugin.particlesInitialization?.() ?? handled;
5356
+ if (handled) {
5357
+ break;
5358
+ }
5359
+ }
5360
+ if (handled) {
5361
+ return;
5362
+ }
5363
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5364
+ for (const group in groups) {
5365
+ const groupOptions = groups[group];
5366
+ if (!groupOptions) {
5367
+ continue;
5368
+ }
5369
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5370
+ this.addParticle(undefined, groupOptions, group);
5371
+ }
5372
+ }
5373
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5374
+ this.addParticle();
5375
+ }
5376
+ }
5377
+ async #initPluginsArrays() {
5378
+ const container = this.#container;
5379
+ for (const plugin of container.plugins) {
5380
+ if (plugin.redrawInit) {
5381
+ await plugin.redrawInit();
5382
+ }
5383
+ if (plugin.checkParticlePosition) {
5384
+ this.checkParticlePositionPlugins.push(plugin);
5385
+ }
5386
+ if (plugin.update) {
5387
+ this.#updatePlugins.push(plugin);
5388
+ }
5389
+ if (plugin.particleUpdate) {
5390
+ this.#particleUpdatePlugins.push(plugin);
5391
+ }
5392
+ if (plugin.postUpdate) {
5393
+ this.#postUpdatePlugins.push(plugin);
5394
+ }
5395
+ if (plugin.particleReset) {
5396
+ this.#particleResetPlugins.push(plugin);
5397
+ }
5398
+ if (plugin.postParticleUpdate) {
5399
+ this.#postParticleUpdatePlugins.push(plugin);
5400
+ }
5401
+ }
5402
+ }
5239
5403
  #insertParticleIntoBucket(particle) {
5240
5404
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5241
5405
  if (!bucket) {
@@ -5244,12 +5408,15 @@
5244
5408
  bucket.push(particle);
5245
5409
  this.#particleBuckets.set(particle.id, bucketIndex);
5246
5410
  }
5247
- #removeParticle(index, group, override) {
5411
+ #removeAny(quantity) {
5412
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5413
+ }
5414
+ #removeParticle(index, group, override, matchGroup = true) {
5248
5415
  const particle = this.#array[index];
5249
5416
  if (!particle) {
5250
5417
  return false;
5251
5418
  }
5252
- if (particle.group !== group) {
5419
+ if (matchGroup && particle.group !== group) {
5253
5420
  return false;
5254
5421
  }
5255
5422
  this.#array.splice(index, deleteCount);
@@ -5336,7 +5503,7 @@
5336
5503
  particlesToDelete.add(particle);
5337
5504
  continue;
5338
5505
  }
5339
- this.grid.insert(particle);
5506
+ this.#grid.insert(particle);
5340
5507
  }
5341
5508
  return particlesToDelete;
5342
5509
  }
@@ -5423,6 +5590,7 @@
5423
5590
  started;
5424
5591
  zLayers;
5425
5592
  #delay;
5593
+ #delayResolve;
5426
5594
  #delayTimeout;
5427
5595
  #delta = { value: 0, factor: 0 };
5428
5596
  #dispatchCallback;
@@ -5512,7 +5680,6 @@
5512
5680
  this.shapeDrawers = new Map();
5513
5681
  this.particleUpdaters = [];
5514
5682
  this.plugins.length = 0;
5515
- this.#pluginManager.clearPlugins(this);
5516
5683
  this.destroyed = true;
5517
5684
  this.#onDestroy(remove);
5518
5685
  this.dispatchEvent(EventType.containerDestroyed);
@@ -5565,31 +5732,14 @@
5565
5732
  await this.initDrawersAndUpdaters();
5566
5733
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
5567
5734
  this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
5568
- this.plugins.length = 0;
5569
- this.particleDestroyedPlugins.length = 0;
5570
- this.particleCreatedPlugins.length = 0;
5571
- this.particlePositionPlugins.length = 0;
5572
- for (const [plugin, containerPlugin] of allContainerPlugins) {
5573
- if (plugin.needsPlugin(this.actualOptions)) {
5574
- this.plugins.push(containerPlugin);
5575
- if (containerPlugin.particleCreated) {
5576
- this.particleCreatedPlugins.push(containerPlugin);
5577
- }
5578
- if (containerPlugin.particleDestroyed) {
5579
- this.particleDestroyedPlugins.push(containerPlugin);
5580
- }
5581
- if (containerPlugin.particlePosition) {
5582
- this.particlePositionPlugins.push(containerPlugin);
5583
- }
5584
- }
5585
- }
5735
+ this.#initPlugins(allContainerPlugins);
5586
5736
  this.retina.init();
5587
5737
  this.canvas.init();
5588
5738
  this.updateActualOptions();
5589
5739
  this.canvas.initBackground();
5590
5740
  this.canvas.resize();
5591
5741
  const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
5592
- this.hdrMode = hdrOptions.mode;
5742
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
5593
5743
  this.peakNits = hdrOptions.peakNits;
5594
5744
  this.zLayers = zLayers;
5595
5745
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
@@ -5610,9 +5760,9 @@
5610
5760
  }
5611
5761
  async initDrawersAndUpdaters() {
5612
5762
  const pluginManager = this.#pluginManager;
5613
- this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
5614
- this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
5615
- this.particleUpdaters = await pluginManager.getUpdaters(this, true);
5763
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
5764
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
5765
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
5616
5766
  }
5617
5767
  pause() {
5618
5768
  if (!guardCheck(this)) {
@@ -5679,7 +5829,10 @@
5679
5829
  await this.init();
5680
5830
  this.started = true;
5681
5831
  await new Promise(resolve => {
5832
+ this.#delayResolve = resolve;
5682
5833
  const start = async () => {
5834
+ this.#delayTimeout = undefined;
5835
+ this.#delayResolve = undefined;
5683
5836
  this.#eventListeners.addListeners();
5684
5837
  for (const plugin of this.plugins) {
5685
5838
  await plugin.start?.();
@@ -5695,9 +5848,11 @@
5695
5848
  if (!guardCheck(this) || !this.started) {
5696
5849
  return;
5697
5850
  }
5698
- if (this.#delayTimeout) {
5851
+ if (this.#delayTimeout !== undefined) {
5699
5852
  clearTimeout(this.#delayTimeout);
5700
5853
  this.#delayTimeout = undefined;
5854
+ this.#delayResolve?.();
5855
+ this.#delayResolve = undefined;
5701
5856
  }
5702
5857
  this.#firstStart = true;
5703
5858
  this.started = false;
@@ -5723,6 +5878,27 @@
5723
5878
  }
5724
5879
  return refresh;
5725
5880
  }
5881
+ #initPlugins(allContainerPlugins) {
5882
+ this.plugins.length = 0;
5883
+ this.particleDestroyedPlugins.length = 0;
5884
+ this.particleCreatedPlugins.length = 0;
5885
+ this.particlePositionPlugins.length = 0;
5886
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
5887
+ if (!plugin.needsPlugin(this.actualOptions)) {
5888
+ continue;
5889
+ }
5890
+ this.plugins.push(containerPlugin);
5891
+ if (containerPlugin.particleCreated) {
5892
+ this.particleCreatedPlugins.push(containerPlugin);
5893
+ }
5894
+ if (containerPlugin.particleDestroyed) {
5895
+ this.particleDestroyedPlugins.push(containerPlugin);
5896
+ }
5897
+ if (containerPlugin.particlePosition) {
5898
+ this.particlePositionPlugins.push(containerPlugin);
5899
+ }
5900
+ }
5901
+ }
5726
5902
  #nextFrame(timestamp) {
5727
5903
  try {
5728
5904
  if (!this.#smooth &&
@@ -5999,11 +6175,11 @@
5999
6175
  }
6000
6176
  }
6001
6177
  async #init() {
6002
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6003
- if (!availablePathGenerators) {
6178
+ const pluginManager = this.#pluginManager;
6179
+ if (!pluginManager.initializers.pathGenerators) {
6004
6180
  return;
6005
6181
  }
6006
- this.availablePathGenerators = availablePathGenerators;
6182
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6007
6183
  this.pathGenerators = new Map();
6008
6184
  for (const pathGenerator of this.pathGenerators.values()) {
6009
6185
  pathGenerator.init();
@@ -6019,6 +6195,7 @@
6019
6195
  exports.loadSnowPreset = loadSnowPreset;
6020
6196
  exports.tsParticles = tsParticles;
6021
6197
 
6022
- }));
6198
+ })(g.__tsParticlesInternals.presets.snow = g.__tsParticlesInternals.presets.snow || {});
6199
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
6200
+
6023
6201
  Object.assign(globalThis.window || globalThis, { loadSnowPreset: (globalThis.__tsParticlesInternals.presets.snow || {}).loadSnowPreset });
6024
- delete (globalThis.window || globalThis).tsparticlesInternalExports;