@tsparticles/preset-firefly 4.3.2 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,35 @@
1
- (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.3.2 */
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.firefly = global.__tsParticlesInternals.presets.firefly || {})));
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, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity$1 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
1
+ (function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
+ (function (g) {
3
+ /* Preset v4.5.0 */
4
+ g.__tsParticlesInternals = g.__tsParticlesInternals || {};
5
+ g.__tsParticlesInternals.presets = g.__tsParticlesInternals.presets || {};
6
+ (function (exports) {
7
+ 'use strict';
8
+
9
+ const errorPrefix = "tsParticles - Error", codedErrorPattern = /^\[tsParticles Error (TSP-\d+)\]\s*/, fullMatchIndex = 0, codeIndex = 1;
10
+ const wrap = (fn) => (...args) => {
11
+ fn(...args);
12
+ }, _logger = {
13
+ debug: wrap(console.debug),
14
+ error: (message, ...optionalParams) => {
15
+ if (typeof message === "string") {
16
+ const match = codedErrorPattern.exec(message);
17
+ if (match?.[codeIndex]) {
18
+ console.error(`tsParticles - ${match[codeIndex]} - ${message.slice(match[fullMatchIndex].length)}`, ...optionalParams);
19
+ return;
20
+ }
21
+ }
22
+ console.error(`${errorPrefix} - ${message}`, ...optionalParams);
23
+ },
24
+ info: wrap(console.info),
25
+ log: wrap(console.log),
26
+ trace: wrap(console.trace),
27
+ verbose: wrap(console.log),
28
+ warning: wrap(console.warn),
29
+ };
30
+ function getLogger() {
31
+ return _logger;
32
+ }
19
33
 
20
34
  var MoveDirection;
21
35
  (function (MoveDirection) {
@@ -32,6 +46,17 @@
32
46
  MoveDirection["inside"] = "inside";
33
47
  })(MoveDirection || (MoveDirection = {}));
34
48
 
49
+ const generatedAttribute = "generated", fullPercent = "100%", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = {
50
+ x: 0,
51
+ y: 0,
52
+ z: 0,
53
+ }, defaultTransform = {
54
+ a: 1,
55
+ b: 0,
56
+ c: 0,
57
+ d: 1,
58
+ }, randomColorValue = "random", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance = 0, minRadius = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity$1 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
59
+
35
60
  function getZ(source) {
36
61
  return "z" in source ? source.z : originPoint.z;
37
62
  }
@@ -147,6 +172,12 @@
147
172
  function isNumber(arg) {
148
173
  return typeof arg === "number";
149
174
  }
175
+ function isEnumValue(enumObject, arg) {
176
+ return Object.values(enumObject).some(enumValue => enumValue === arg);
177
+ }
178
+ function toEnumValue(enumObject, arg, defaultValue) {
179
+ return isEnumValue(enumObject, arg) ? arg : defaultValue;
180
+ }
150
181
  function isObject(arg) {
151
182
  return typeof arg === "object" && arg !== null;
152
183
  }
@@ -270,11 +301,61 @@
270
301
  };
271
302
  }
272
303
  function parseAlpha(input) {
273
- const defaultAlpha = 1;
274
304
  if (!input) {
275
305
  return defaultAlpha;
276
306
  }
277
- return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
307
+ return input.endsWith("%") ? Number.parseFloat(input) / percentDenominator : Number.parseFloat(input);
308
+ }
309
+
310
+ const ErrorCodes$2 = {
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$2.engineVersionMismatch]: "The tsParticles version is different from the loaded plugins version. Engine version: {0}. Plugin version: {1}",
323
+ [ErrorCodes$2.particlePositionNotFound]: "a valid position cannot be found for particle",
324
+ [ErrorCodes$2.pluginRegisterAfterLoad]: "Register plugins can only be done before calling tsParticles.load()",
325
+ [ErrorCodes$2.offscreenCanvasUnsupported]: "OffscreenCanvas is required but not supported by this browser",
326
+ [ErrorCodes$2.offscreenCanvasTransferFailed]: "OffscreenCanvas transfer failed",
327
+ [ErrorCodes$2.hdrEnableInvalid]: 'Invalid HDR "enable" value: expected a boolean, got "{0}"',
328
+ [ErrorCodes$2.hdrModeInvalid]: 'Invalid HDR "mode" value: expected one of {0}, got "{1}"',
329
+ [ErrorCodes$2.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;
581
+ }
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;
599
+ }
600
+ function isSourceOptionsValue(value) {
601
+ if (isArray(value)) {
602
+ return value.every((entry) => isObject(entry) && !isArray(entry));
495
603
  }
496
- return res;
604
+ return isObject(value) && !isArray(value);
497
605
  }
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);
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
+ }
504
618
  }
505
- return res;
619
+ else {
620
+ const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
621
+ if (foundCanvas) {
622
+ canvasEl = foundCanvas;
623
+ canvasEl.dataset[generatedAttribute] = generatedFalse;
624
+ }
625
+ else {
626
+ canvasEl = documentSafe.createElement(canvasTag);
627
+ canvasEl.dataset[generatedAttribute] = generatedTrue;
628
+ domContainer.appendChild(canvasEl);
629
+ }
630
+ canvasEl.style.width ||= fullPercent;
631
+ canvasEl.style.height ||= fullPercent;
632
+ canvasEl.style.pointerEvents = "none";
633
+ canvasEl.style.setProperty("pointer-events", "none");
634
+ }
635
+ return canvasEl;
636
+ }
637
+ function getDomContainer(id, source) {
638
+ const documentSafe = safeDocument();
639
+ let domContainer = source ?? documentSafe.getElementById(id);
640
+ if (domContainer) {
641
+ return domContainer;
642
+ }
643
+ domContainer = documentSafe.createElement("canvas");
644
+ domContainer.id = id;
645
+ domContainer.dataset[generatedAttribute] = generatedTrue;
646
+ documentSafe.body.append(domContainer);
647
+ return domContainer;
506
648
  }
507
649
 
508
650
  class EventDispatcher {
@@ -573,7 +715,6 @@
573
715
  class PluginManager {
574
716
  colorManagers = new Map();
575
717
  easingFunctions = new Map();
576
- effectDrawers = new Map();
577
718
  initializers = {
578
719
  effects: new Map(),
579
720
  shapes: new Map(),
@@ -582,8 +723,6 @@
582
723
  palettes = new Map();
583
724
  plugins = [];
584
725
  presets = new Map();
585
- shapeDrawers = new Map();
586
- updaters = new Map();
587
726
  #allLoadersSet = new Set();
588
727
  #configs = new Map();
589
728
  #engine;
@@ -601,57 +740,79 @@
601
740
  }
602
741
  return res;
603
742
  }
743
+ get initialized() {
744
+ return this.#initialized;
745
+ }
604
746
  addColorManager(name, manager) {
747
+ if (this.#initialized) {
748
+ return;
749
+ }
605
750
  this.colorManagers.set(name, manager);
606
751
  }
607
752
  addConfig(config) {
753
+ if (this.#initialized) {
754
+ return;
755
+ }
608
756
  const key = config.key ?? config.name ?? "default";
609
757
  this.#configs.set(key, config);
610
758
  this.#engine.dispatchEvent(EventType.configAdded, { data: { name: key, config } });
611
759
  }
612
760
  addEasing(name, easing) {
761
+ if (this.#initialized) {
762
+ return;
763
+ }
613
764
  if (this.easingFunctions.get(name)) {
614
765
  return;
615
766
  }
616
767
  this.easingFunctions.set(name, easing);
617
768
  }
618
769
  addEffect(effect, drawer) {
770
+ if (this.#initialized) {
771
+ return;
772
+ }
619
773
  this.initializers.effects.set(effect, drawer);
620
774
  }
621
775
  addPalette(name, palette) {
776
+ if (this.#initialized) {
777
+ return;
778
+ }
622
779
  this.palettes.set(name, palette);
623
780
  }
624
781
  addParticleUpdater(name, updaterInitializer) {
782
+ if (this.#initialized) {
783
+ return;
784
+ }
625
785
  this.initializers.updaters.set(name, updaterInitializer);
626
786
  }
627
787
  addPlugin(plugin) {
788
+ if (this.#initialized) {
789
+ return;
790
+ }
628
791
  if (this.getPlugin(plugin.id)) {
629
792
  return;
630
793
  }
631
794
  this.plugins.push(plugin);
632
795
  }
633
796
  addPreset(preset, options, override = false) {
797
+ if (this.#initialized) {
798
+ return;
799
+ }
634
800
  if (!(override || !this.getPreset(preset))) {
635
801
  return;
636
802
  }
637
803
  this.presets.set(preset, options);
638
804
  }
639
805
  addShape(shapes, drawer) {
806
+ if (this.#initialized) {
807
+ return;
808
+ }
640
809
  for (const shape of shapes) {
641
810
  this.initializers.shapes.set(shape, drawer);
642
811
  }
643
812
  }
644
- clearPlugins(container) {
645
- this.effectDrawers.delete(container);
646
- this.shapeDrawers.delete(container);
647
- this.updaters.delete(container);
648
- }
649
813
  getEasing(name) {
650
814
  return this.easingFunctions.get(name) ?? ((value) => value);
651
815
  }
652
- getEffectDrawers(container, force = false) {
653
- return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force);
654
- }
655
816
  getPalette(name) {
656
817
  return this.palettes.get(name);
657
818
  }
@@ -661,12 +822,6 @@
661
822
  getPreset(preset) {
662
823
  return this.presets.get(preset);
663
824
  }
664
- async getShapeDrawers(container, force = false) {
665
- return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force);
666
- }
667
- async getUpdaters(container, force = false) {
668
- return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force);
669
- }
670
825
  async init() {
671
826
  if (this.#initialized || this.#isRunningLoaders) {
672
827
  return;
@@ -685,16 +840,9 @@
685
840
  this.#initialized = true;
686
841
  }
687
842
  }
688
- loadParticlesOptions(container, options, ...sourceOptions) {
689
- const updaters = this.updaters.get(container);
690
- if (!updaters) {
691
- return;
692
- }
693
- updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions));
694
- }
695
843
  async register(...loaders) {
696
844
  if (this.#initialized) {
697
- throw new Error("Register plugins can only be done before calling tsParticles.load()");
845
+ throw new Error(getErrorMessage(ErrorCodes$2.pluginRegisterAfterLoad));
698
846
  }
699
847
  for (const loader of loaders) {
700
848
  if (this.#isRunningLoaders) {
@@ -706,88 +854,15 @@
706
854
  }
707
855
  }
708
856
  async #runLoader(loader, executed, allLoaders) {
709
- if (executed.has(loader))
857
+ if (executed.has(loader)) {
710
858
  return;
859
+ }
711
860
  executed.add(loader);
712
861
  allLoaders.add(loader);
713
862
  await loader(this.#engine);
714
863
  }
715
864
  }
716
865
 
717
- const errorPrefix = "tsParticles - Error";
718
- const wrap = (fn) => (...args) => {
719
- fn(...args);
720
- }, _logger = {
721
- debug: wrap(console.debug),
722
- error: (message, ...optionalParams) => {
723
- console.error(`${errorPrefix} - ${message}`, ...optionalParams);
724
- },
725
- info: wrap(console.info),
726
- log: wrap(console.log),
727
- trace: wrap(console.trace),
728
- verbose: wrap(console.log),
729
- warning: wrap(console.warn),
730
- };
731
- function getLogger() {
732
- return _logger;
733
- }
734
-
735
- const fullPercent = "100%";
736
- async function getDataFromUrl(data) {
737
- const url = itemFromSingleOrMultiple(data.url, data.index);
738
- if (!url) {
739
- return data.fallback;
740
- }
741
- const response = await fetch(url);
742
- if (response.ok) {
743
- return (await response.json());
744
- }
745
- getLogger().error(`${response.status.toString()} while retrieving config file`);
746
- return data.fallback;
747
- }
748
- const getCanvasFromContainer = (domContainer) => {
749
- const documentSafe = safeDocument();
750
- let canvasEl;
751
- const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag;
752
- if (isCanvas) {
753
- canvasEl = domContainer;
754
- canvasEl.dataset[generatedAttribute] ??= generatedFalse;
755
- if (canvasEl.dataset[generatedAttribute] === generatedTrue) {
756
- canvasEl.style.width ||= fullPercent;
757
- canvasEl.style.height ||= fullPercent;
758
- canvasEl.style.pointerEvents = "none";
759
- canvasEl.style.setProperty("pointer-events", "none");
760
- }
761
- }
762
- else {
763
- const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex);
764
- if (foundCanvas) {
765
- canvasEl = foundCanvas;
766
- canvasEl.dataset[generatedAttribute] = generatedFalse;
767
- }
768
- else {
769
- canvasEl = documentSafe.createElement(canvasTag);
770
- canvasEl.dataset[generatedAttribute] = generatedTrue;
771
- domContainer.appendChild(canvasEl);
772
- }
773
- canvasEl.style.width ||= fullPercent;
774
- canvasEl.style.height ||= fullPercent;
775
- canvasEl.style.pointerEvents = "none";
776
- canvasEl.style.setProperty("pointer-events", "none");
777
- }
778
- return canvasEl;
779
- }, getDomContainer = (id, source) => {
780
- const documentSafe = safeDocument();
781
- let domContainer = source ?? documentSafe.getElementById(id);
782
- if (domContainer) {
783
- return domContainer;
784
- }
785
- domContainer = documentSafe.createElement("canvas");
786
- domContainer.id = id;
787
- domContainer.dataset[generatedAttribute] = generatedTrue;
788
- documentSafe.body.append(domContainer);
789
- return domContainer;
790
- };
791
866
  class Engine {
792
867
  pluginManager = new PluginManager(this);
793
868
  #domArray = [];
@@ -797,7 +872,7 @@
797
872
  return this.#domArray;
798
873
  }
799
874
  get version() {
800
- return "4.3.2";
875
+ return "4.5.0";
801
876
  }
802
877
  addEventListener(type, listener) {
803
878
  this.#eventDispatcher.addEventListener(type, listener);
@@ -806,7 +881,7 @@
806
881
  if (this.version === pluginVersion) {
807
882
  return;
808
883
  }
809
- throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`);
884
+ throw new Error(getErrorMessage(ErrorCodes$2.engineVersionMismatch, this.version, pluginVersion));
810
885
  }
811
886
  dispatchEvent(type, args) {
812
887
  this.#eventDispatcher.dispatchEvent(type, args);
@@ -878,6 +953,10 @@
878
953
  }
879
954
 
880
955
  function initEngine() {
956
+ const existing = globalThis.tsParticles;
957
+ if (existing?.pluginManager) {
958
+ return existing;
959
+ }
881
960
  return new Engine();
882
961
  }
883
962
 
@@ -975,6 +1054,15 @@
975
1054
  AnimationMode["random"] = "random";
976
1055
  })(AnimationMode || (AnimationMode = {}));
977
1056
 
1057
+ var HdrMode;
1058
+ (function (HdrMode) {
1059
+ HdrMode["standard"] = "standard";
1060
+ HdrMode["natural"] = "natural";
1061
+ HdrMode["vivid"] = "vivid";
1062
+ HdrMode["cinematic"] = "cinematic";
1063
+ HdrMode["dynamic"] = "dynamic";
1064
+ })(HdrMode || (HdrMode = {}));
1065
+
978
1066
  var LimitMode;
979
1067
  (function (LimitMode) {
980
1068
  LimitMode["delete"] = "delete";
@@ -1042,20 +1130,6 @@
1042
1130
  DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1043
1131
  })(DrawLayer || (DrawLayer = {}));
1044
1132
 
1045
- class OptionLoader {
1046
- load(data) {
1047
- if (isNull(data)) {
1048
- return;
1049
- }
1050
- this.doLoad(data);
1051
- }
1052
- }
1053
- function loadOptions(options, ...sourceOptionsArr) {
1054
- for (const sourceOptions of sourceOptionsArr) {
1055
- options.load(sourceOptions);
1056
- }
1057
- }
1058
-
1059
1133
  function loadProperty(obj, key, value) {
1060
1134
  if (value !== undefined) {
1061
1135
  obj[key] = value;
@@ -1087,6 +1161,20 @@
1087
1161
  }
1088
1162
  }
1089
1163
 
1164
+ class OptionLoader {
1165
+ load(data) {
1166
+ if (isNull(data)) {
1167
+ return;
1168
+ }
1169
+ this.doLoad(data);
1170
+ }
1171
+ }
1172
+ function loadOptions(options, ...sourceOptionsArr) {
1173
+ for (const sourceOptions of sourceOptionsArr) {
1174
+ options.load(sourceOptions);
1175
+ }
1176
+ }
1177
+
1090
1178
  class AnimationOptions extends OptionLoader {
1091
1179
  count = 0;
1092
1180
  decay = 0;
@@ -1230,6 +1318,28 @@
1230
1318
  }
1231
1319
  }
1232
1320
 
1321
+ const defaultPeakNits = 400, minPeakNits = 0, toStringValue = (value) => String(value);
1322
+ class HDROptions extends OptionLoader {
1323
+ enable = true;
1324
+ mode = HdrMode.standard;
1325
+ peakNits = defaultPeakNits;
1326
+ doLoad(data) {
1327
+ if (data.enable !== undefined && !isBoolean(data.enable)) {
1328
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrEnableInvalid, String(data.enable)));
1329
+ }
1330
+ if (data.mode !== undefined && !isEnumValue(HdrMode, data.mode)) {
1331
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrModeInvalid, Object.values(HdrMode).join(", "), toStringValue(data.mode)));
1332
+ }
1333
+ if (data.peakNits !== undefined &&
1334
+ (!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
1335
+ throw new Error(getErrorMessage(ErrorCodes$2.hdrPeakNitsInvalid, String(data.peakNits)));
1336
+ }
1337
+ loadProperty(this, "enable", data.enable);
1338
+ loadProperty(this, "mode", data.mode);
1339
+ loadProperty(this, "peakNits", data.peakNits);
1340
+ }
1341
+ }
1342
+
1233
1343
  class ResizeEvent extends OptionLoader {
1234
1344
  delay = 0.5;
1235
1345
  enable = true;
@@ -1600,12 +1710,10 @@
1600
1710
  plugin.loadParticlesOptions(this.#container, this, data);
1601
1711
  }
1602
1712
  }
1603
- const updaters = this.#pluginManager.updaters.get(this.#container);
1604
- if (updaters) {
1605
- for (const updater of updaters) {
1606
- if (updater.loadOptions) {
1607
- updater.loadOptions(this, data);
1608
- }
1713
+ const updaters = this.#container.particleUpdaters;
1714
+ for (const updater of updaters) {
1715
+ if (updater.loadOptions) {
1716
+ updater.loadOptions(this, data);
1609
1717
  }
1610
1718
  }
1611
1719
  }
@@ -1673,7 +1781,7 @@
1673
1781
  duration = 0;
1674
1782
  fpsLimit = 120;
1675
1783
  fullScreen;
1676
- hdr = true;
1784
+ hdr;
1677
1785
  key;
1678
1786
  name;
1679
1787
  palette;
@@ -1693,6 +1801,7 @@
1693
1801
  this.#container = container;
1694
1802
  this.background = new Background();
1695
1803
  this.fullScreen = new FullScreen();
1804
+ this.hdr = new HDROptions();
1696
1805
  this.particles = loadParticlesOptions(this.#pluginManager, this.#container);
1697
1806
  this.resize = new ResizeEvent();
1698
1807
  }
@@ -1715,7 +1824,15 @@
1715
1824
  loadProperty(this, "detectRetina", data.detectRetina);
1716
1825
  loadRangeProperty(this, "duration", data.duration);
1717
1826
  loadProperty(this, "fpsLimit", data.fpsLimit);
1718
- loadProperty(this, "hdr", data.hdr);
1827
+ const hdrData = data.hdr;
1828
+ if (isBoolean(hdrData)) {
1829
+ this.hdr.enable = hdrData;
1830
+ this.hdr.mode = HdrMode.standard;
1831
+ this.hdr.peakNits = 400;
1832
+ }
1833
+ else {
1834
+ this.hdr.load(hdrData);
1835
+ }
1719
1836
  loadProperty(this, "pauseOnBlur", data.pauseOnBlur);
1720
1837
  loadProperty(this, "pauseOnOutsideViewport", data.pauseOnOutsideViewport);
1721
1838
  loadProperty(this, "zLayers", data.zLayers);
@@ -1758,7 +1875,7 @@
1758
1875
  }
1759
1876
  }
1760
1877
 
1761
- const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, hdrRgbFixedPrecision = 4, hdrHslFixedPrecision = 4, sdrReferenceWhiteNits = 203, hdrAnimationScale = sdrReferenceWhiteNits / maxNits;
1878
+ const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, hdrRgbFixedPrecision = 4, hdrHslFixedPrecision = 4, sdrReferenceWhiteNits = 203, acesA = 2.51, acesB = 0.03, acesC = 2.43, acesD = 0.59, acesE = 0.14, saturationBoost = 1.15, lightnessBoost = 1.05, temperatureR = 1.05, temperatureB = 0.95, contrastFactor = 1.1, luminanceR = 0.2126, luminanceG = 0.7152, luminanceB = 0.0722, dynamicLuminanceFactor = 2, channelCount = 3;
1762
1879
  function getCachedStyle(key, generator) {
1763
1880
  let cached = styleCache.get(key);
1764
1881
  if (!cached) {
@@ -1770,6 +1887,42 @@
1770
1887
  }
1771
1888
  return cached;
1772
1889
  }
1890
+ function acesFilmic(x) {
1891
+ return clamp((x * (acesA * x + acesB)) / (x * (acesC * x + acesD) + acesE), none, one);
1892
+ }
1893
+ function applyHdrModeAdjustments(r, g, b, mode, maxChannel) {
1894
+ switch (mode) {
1895
+ case HdrMode.vivid: {
1896
+ const avg = (r + g + b) / channelCount;
1897
+ return {
1898
+ b: clamp(clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
1899
+ g: clamp(clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
1900
+ r: clamp(clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
1901
+ };
1902
+ }
1903
+ case HdrMode.cinematic: {
1904
+ const tempR = r * temperatureR, tempB = b * temperatureB, avg = (tempR + g + tempB) / channelCount;
1905
+ return {
1906
+ b: clamp(avg + (tempB - avg) * contrastFactor, none, maxChannel),
1907
+ g: clamp(avg + (g - avg) * contrastFactor, none, maxChannel),
1908
+ r: clamp(avg + (tempR - avg) * contrastFactor, none, maxChannel),
1909
+ };
1910
+ }
1911
+ case HdrMode.dynamic: {
1912
+ const luminance = luminanceR * r + luminanceG * g + luminanceB * b, vividWeight = Math.min(one, luminance * dynamicLuminanceFactor), naturalWeight = one - vividWeight, avg = (r + g + b) / channelCount, vividR = clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividG = clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividB = clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost;
1913
+ return {
1914
+ b: clamp(b * naturalWeight + vividB * vividWeight, none, maxChannel),
1915
+ g: clamp(g * naturalWeight + vividG * vividWeight, none, maxChannel),
1916
+ r: clamp(r * naturalWeight + vividR * vividWeight, none, maxChannel),
1917
+ };
1918
+ }
1919
+ case HdrMode.standard:
1920
+ return { b, g, r };
1921
+ case HdrMode.natural:
1922
+ default:
1923
+ return { b, g, r };
1924
+ }
1925
+ }
1773
1926
  function stringToRgba(pluginManager, input) {
1774
1927
  if (!input) {
1775
1928
  return;
@@ -1893,9 +2046,9 @@
1893
2046
  const grayscaleValue = Math.round(lNormalized * rgbMax);
1894
2047
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1895
2048
  }
1896
- const temp1 = lNormalized < half
2049
+ const temp2 = lNormalized < half
1897
2050
  ? lNormalized * (sNormalizedOffset + sNormalized)
1898
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2051
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1899
2052
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1900
2053
  }
1901
2054
  function hslToRgbFloat(hsl) {
@@ -1904,9 +2057,9 @@
1904
2057
  const grayscaleValue = lNormalized * rgbMax;
1905
2058
  return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1906
2059
  }
1907
- const temp1 = lNormalized < half
2060
+ const temp2 = lNormalized < half
1908
2061
  ? lNormalized * (sNormalizedOffset + sNormalized)
1909
- : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
2062
+ : lNormalized + sNormalized - lNormalized * sNormalized, temp1 = double * lNormalized - temp2, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp1, temp2, hNormalized - phaseThird));
1910
2063
  return { r: red, g: green, b: blue };
1911
2064
  }
1912
2065
  function hslaToRgba(hsla) {
@@ -1927,21 +2080,40 @@
1927
2080
  r: getRgbInRangeValue(),
1928
2081
  };
1929
2082
  }
1930
- function getStyleFromRgb(color, hdr, opacity) {
1931
- const rgbPrecision = hdr ? hdrRgbFixedPrecision : rgbFixedPrecision, op = opacity ?? defaultOpacity$1, key = `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
1932
- return getCachedStyle(key, () => (hdr ? getHdrStyleFromRgb(color, opacity) : getSdrStyleFromRgb(color, opacity)));
2083
+ function getStyleFromRgb(color, hdr, opacity, peakNits, mode) {
2084
+ const rgbPrecision = hdr ? hdrRgbFixedPrecision : rgbFixedPrecision, op = opacity ?? defaultOpacity$1, key = hdr
2085
+ ? `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? HdrMode.standard}`
2086
+ : `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-sdr-${op.toString()}`;
2087
+ return getCachedStyle(key, () => hdr ? getHdrStyleFromRgb(color, opacity, peakNits, mode) : getSdrStyleFromRgb(color, opacity));
2088
+ }
2089
+ function getHdrStyleFromRgb(color, opacity, peakNits = maxNits, mode = HdrMode.standard) {
2090
+ const headroom = peakNits / sdrReferenceWhiteNits, middleGray = 0.18, mapped = hdrToneMapColor(color, headroom, middleGray, mode);
2091
+ return `color(display-p3 ${mapped.r.toString()} ${mapped.g.toString()} ${mapped.b.toString()} / ${(opacity ?? defaultOpacity$1).toString()})`;
1933
2092
  }
1934
- function getHdrStyleFromRgb(color, opacity, peakNits = maxNits) {
1935
- const headroom = peakNits / sdrReferenceWhiteNits;
1936
- return `color(display-p3 ${((color.r / rgbMax) * headroom).toString()} ${((color.g / rgbMax) * headroom).toString()} ${((color.b / rgbMax) * headroom).toString()} / ${(opacity ?? defaultOpacity$1).toString()})`;
2093
+ function hdrToneMapColor(color, headroom, middleGray, mode) {
2094
+ const rNorm = color.r / rgbMax, gNorm = color.g / rgbMax, bNorm = color.b / rgbMax;
2095
+ if (mode !== HdrMode.natural && mode !== HdrMode.vivid && mode !== HdrMode.cinematic && mode !== HdrMode.dynamic) {
2096
+ return { b: bNorm, g: gNorm, r: rNorm };
2097
+ }
2098
+ const maxChannel = Math.max(one, headroom), luminance = luminanceR * rNorm + luminanceG * gNorm + luminanceB * bNorm, mappedLuminance = hdrToneMap(luminance, headroom, middleGray), scale = luminance > none ? mappedLuminance / luminance : one;
2099
+ return applyHdrModeAdjustments(clamp(rNorm * scale, none, maxChannel), clamp(gNorm * scale, none, maxChannel), clamp(bNorm * scale, none, maxChannel), mode, maxChannel);
2100
+ }
2101
+ function hdrToneMap(normalized, headroom, middleGray) {
2102
+ if (normalized <= middleGray) {
2103
+ return acesFilmic(normalized);
2104
+ }
2105
+ const expanded = normalized + (normalized - middleGray) * (headroom - one);
2106
+ return acesFilmic(expanded);
1937
2107
  }
1938
2108
  function getSdrStyleFromRgb(color, opacity) {
1939
2109
  return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity$1).toString()})`;
1940
2110
  }
1941
- function getStyleFromHsl(color, hdr, opacity) {
1942
- const hslPrecision = hdr ? hdrHslFixedPrecision : hslFixedPrecision, op = opacity ?? defaultOpacity$1, key = `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
2111
+ function getStyleFromHsl(color, hdr, opacity, peakNits, mode) {
2112
+ const hslPrecision = hdr ? hdrHslFixedPrecision : hslFixedPrecision, op = opacity ?? defaultOpacity$1, key = hdr
2113
+ ? `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? HdrMode.standard}`
2114
+ : `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-sdr-${op.toString()}`;
1943
2115
  return getCachedStyle(key, () => hdr
1944
- ? getStyleFromRgb(hslToRgbFloat(color), true, opacity)
2116
+ ? getStyleFromRgb(hslToRgbFloat(color), true, opacity, peakNits, mode)
1945
2117
  : `hsla(${color.h.toString()}, ${color.s.toString()}%, ${color.l.toString()}%, ${op.toString()})`);
1946
2118
  }
1947
2119
  function getHslFromAnimation(animation) {
@@ -2004,7 +2176,7 @@
2004
2176
  colorValue.velocity = defaultVelocity;
2005
2177
  }
2006
2178
  }
2007
- function updateColorValue(data, decrease, delta, hdr) {
2179
+ function updateColorValue(data, decrease, delta, _hdr) {
2008
2180
  const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
2009
2181
  if (!data.enable ||
2010
2182
  ((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
@@ -2017,7 +2189,7 @@
2017
2189
  if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
2018
2190
  return;
2019
2191
  }
2020
- const offset = data.offset ? randomInRangeValue(data.offset) : minOffset, velocity = ((data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor) * (hdr ? hdrAnimationScale : identity), decay = data.decay ?? identity, max = data.max, min = data.min;
2192
+ const offset = data.offset ? randomInRangeValue(data.offset) : minOffset, velocity = (data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor, decay = data.decay ?? identity, max = data.max, min = data.min;
2021
2193
  if (!decrease || data.status === AnimationStatus.increasing) {
2022
2194
  data.value += velocity;
2023
2195
  if (data.value > max) {
@@ -2049,9 +2221,9 @@
2049
2221
  return;
2050
2222
  }
2051
2223
  const { h, s, l } = color;
2052
- updateColorValue(h, false, delta, hdr);
2053
- updateColorValue(s, true, delta, hdr);
2054
- updateColorValue(l, true, delta, hdr);
2224
+ updateColorValue(h, false, delta);
2225
+ updateColorValue(s, true, delta);
2226
+ updateColorValue(l, true, delta);
2055
2227
  }
2056
2228
  function alterHsl(color, type, value) {
2057
2229
  return {
@@ -2101,7 +2273,7 @@
2101
2273
  }
2102
2274
 
2103
2275
  async function loadBlendPlugin(engine) {
2104
- engine.checkVersion("4.3.2");
2276
+ engine.checkVersion("4.5.0");
2105
2277
  await engine.pluginManager.register(e => {
2106
2278
  e.pluginManager.addPlugin(new BlendPlugin());
2107
2279
  });
@@ -2138,7 +2310,7 @@
2138
2310
  }
2139
2311
 
2140
2312
  async function loadCircleShape(engine) {
2141
- engine.checkVersion("4.3.2");
2313
+ engine.checkVersion("4.5.0");
2142
2314
  await engine.pluginManager.register(e => {
2143
2315
  e.pluginManager.addShape(["circle"], () => {
2144
2316
  return Promise.resolve(new CircleDrawer());
@@ -2153,7 +2325,7 @@
2153
2325
  RgbIndexes[RgbIndexes["b"] = 3] = "b";
2154
2326
  RgbIndexes[RgbIndexes["a"] = 4] = "a";
2155
2327
  })(RgbIndexes$1 || (RgbIndexes$1 = {}));
2156
- const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
2328
+ const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, alphaFactor = 0xff;
2157
2329
  class HexColorManager {
2158
2330
  accepts(input) {
2159
2331
  return input.startsWith("#");
@@ -2186,7 +2358,7 @@
2186
2358
  }
2187
2359
 
2188
2360
  async function loadHexColorPlugin(engine) {
2189
- engine.checkVersion("4.3.2");
2361
+ engine.checkVersion("4.5.0");
2190
2362
  await engine.pluginManager.register(e => {
2191
2363
  e.pluginManager.addColorManager("hex", new HexColorManager());
2192
2364
  });
@@ -2226,7 +2398,7 @@
2226
2398
  if (!this.accepts(input)) {
2227
2399
  return;
2228
2400
  }
2229
- const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
2401
+ const result = hslRegex.exec(input), minLength = 4, radix = 10;
2230
2402
  return result
2231
2403
  ? hslaToRgba({
2232
2404
  a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
@@ -2239,12 +2411,20 @@
2239
2411
  }
2240
2412
 
2241
2413
  async function loadHslColorPlugin(engine) {
2242
- engine.checkVersion("4.3.2");
2414
+ engine.checkVersion("4.5.0");
2243
2415
  await engine.pluginManager.register(e => {
2244
2416
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2245
2417
  });
2246
2418
  }
2247
2419
 
2420
+ const ErrorCodes$1 = {
2421
+ baseMoverNotLoaded: "TSP-3203",
2422
+ };
2423
+
2424
+ const ErrorMessages$1 = {
2425
+ [ErrorCodes$1.baseMoverNotLoaded]: "tsParticles Base Mover is not loaded",
2426
+ };
2427
+
2248
2428
  class MovePlugin {
2249
2429
  id = "move";
2250
2430
  #pluginManager;
@@ -2263,20 +2443,20 @@
2263
2443
  }
2264
2444
 
2265
2445
  async function loadMovePlugin(engine) {
2266
- engine.checkVersion("4.3.2");
2446
+ engine.checkVersion("4.5.0");
2447
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
2448
+ addErrorMessages(ErrorMessages$1);
2449
+ }
2267
2450
  await engine.pluginManager.register(e => {
2268
2451
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2269
2452
  movePluginManager.initializers.pathGenerators ??= new Map();
2270
- movePluginManager.pathGenerators ??= new Map();
2271
2453
  movePluginManager.addPathGenerator = (name, generator) => {
2454
+ if (movePluginManager.initialized) {
2455
+ return;
2456
+ }
2272
2457
  movePluginManager.initializers.pathGenerators ??= new Map();
2273
2458
  movePluginManager.initializers.pathGenerators.set(name, generator);
2274
2459
  };
2275
- movePluginManager.getPathGenerators = async (container, force = false) => {
2276
- movePluginManager.initializers.pathGenerators ??= new Map();
2277
- movePluginManager.pathGenerators ??= new Map();
2278
- return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
2279
- };
2280
2460
  e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
2281
2461
  });
2282
2462
  }
@@ -2473,7 +2653,7 @@
2473
2653
  }
2474
2654
 
2475
2655
  async function loadOpacityUpdater(engine) {
2476
- engine.checkVersion("4.3.2");
2656
+ engine.checkVersion("4.5.0");
2477
2657
  await engine.pluginManager.register(e => {
2478
2658
  e.pluginManager.addParticleUpdater("opacity", container => {
2479
2659
  return Promise.resolve(new OpacityUpdater(container));
@@ -2482,70 +2662,60 @@
2482
2662
  }
2483
2663
 
2484
2664
  const boundsMin = 0;
2485
- function bounceHorizontal(data) {
2486
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2487
- (data.direction !== OutModeDirection.left && data.direction !== OutModeDirection.right)) {
2488
- return;
2489
- }
2490
- if (data.bounds.right < boundsMin && data.direction === OutModeDirection.left) {
2491
- data.particle.position.x = data.size + data.offset.x;
2492
- }
2493
- else if (data.bounds.left > data.canvasSize.width && data.direction === OutModeDirection.right) {
2494
- data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
2495
- }
2496
- const velocity = data.particle.velocity.x;
2497
- let bounced = false;
2498
- if (data.outOfCanvas &&
2499
- ((data.direction === OutModeDirection.right && velocity > minVelocity) ||
2500
- (data.direction === OutModeDirection.left && velocity < minVelocity))) {
2501
- const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
2502
- data.particle.velocity.x *= -newVelocity;
2503
- bounced = true;
2504
- }
2505
- if (!bounced) {
2506
- return;
2507
- }
2508
- const minPos = data.offset.x + data.size;
2509
- if (data.outOfCanvas && data.direction === OutModeDirection.right) {
2510
- data.particle.position.x = data.canvasSize.width - minPos;
2511
- }
2512
- else if (data.outOfCanvas && data.direction === OutModeDirection.left) {
2513
- data.particle.position.x = minPos;
2514
- }
2515
- if (data.outMode === OutMode.split) {
2516
- data.particle.destroy();
2517
- }
2518
- }
2519
- function bounceVertical(data) {
2520
- if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) ||
2521
- (data.direction !== OutModeDirection.bottom && data.direction !== OutModeDirection.top)) {
2665
+ const bounceAxisConfigs = {
2666
+ [OutModeDirection.left]: {
2667
+ axis: "x",
2668
+ canvas: "width",
2669
+ bounce: "horizontal",
2670
+ negativeEdge: "right",
2671
+ positiveEdge: "left",
2672
+ negative: true,
2673
+ },
2674
+ [OutModeDirection.right]: {
2675
+ axis: "x",
2676
+ canvas: "width",
2677
+ bounce: "horizontal",
2678
+ negativeEdge: "right",
2679
+ positiveEdge: "left",
2680
+ negative: false,
2681
+ },
2682
+ [OutModeDirection.top]: {
2683
+ axis: "y",
2684
+ canvas: "height",
2685
+ bounce: "vertical",
2686
+ negativeEdge: "bottom",
2687
+ positiveEdge: "top",
2688
+ negative: true,
2689
+ },
2690
+ [OutModeDirection.bottom]: {
2691
+ axis: "y",
2692
+ canvas: "height",
2693
+ bounce: "vertical",
2694
+ negativeEdge: "bottom",
2695
+ positiveEdge: "top",
2696
+ negative: false,
2697
+ },
2698
+ };
2699
+ function bounce(data) {
2700
+ if ((data.outMode !== OutMode.bounce && data.outMode !== OutMode.split) || !(data.direction in bounceAxisConfigs)) {
2522
2701
  return;
2523
2702
  }
2524
- if (data.bounds.bottom < boundsMin && data.direction === OutModeDirection.top) {
2525
- data.particle.position.y = data.size + data.offset.y;
2703
+ const config = bounceAxisConfigs[data.direction], bounds = data.bounds, canvasDim = data.canvasSize[config.canvas], position = data.particle.position, velocity = data.particle.velocity[config.axis];
2704
+ if (config.negative && bounds[config.negativeEdge] < boundsMin) {
2705
+ position[config.axis] = data.size + data.offset[config.axis];
2526
2706
  }
2527
- else if (data.bounds.top > data.canvasSize.height && data.direction === OutModeDirection.bottom) {
2528
- data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
2707
+ else if (!config.negative && bounds[config.positiveEdge] > canvasDim) {
2708
+ position[config.axis] = canvasDim - data.size - data.offset[config.axis];
2529
2709
  }
2530
- const velocity = data.particle.velocity.y;
2531
- let bounced = false;
2532
2710
  if (data.outOfCanvas &&
2533
- ((data.direction === OutModeDirection.bottom && velocity > minVelocity) ||
2534
- (data.direction === OutModeDirection.top && velocity < minVelocity))) {
2535
- const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
2536
- data.particle.velocity.y *= -newVelocity;
2537
- bounced = true;
2711
+ ((config.negative && velocity < minVelocity) || (!config.negative && velocity > minVelocity))) {
2712
+ data.particle.velocity[config.axis] *= -getRangeValue(data.particle.options.bounce[config.bounce].value);
2538
2713
  }
2539
- if (!bounced) {
2714
+ else {
2540
2715
  return;
2541
2716
  }
2542
- const minPos = data.offset.y + data.size;
2543
- if (data.outOfCanvas && data.direction === OutModeDirection.bottom) {
2544
- data.particle.position.y = data.canvasSize.height - minPos;
2545
- }
2546
- else if (data.outOfCanvas && data.direction === OutModeDirection.top) {
2547
- data.particle.position.y = minPos;
2548
- }
2717
+ const minPos = data.offset[config.axis] + data.size;
2718
+ position[config.axis] = config.negative ? minPos : canvasDim - minPos;
2549
2719
  if (data.outMode === OutMode.split) {
2550
2720
  data.particle.destroy();
2551
2721
  }
@@ -2561,26 +2731,19 @@
2561
2731
  OutMode.bounce,
2562
2732
  OutMode.split,
2563
2733
  ];
2564
- this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
2734
+ this.#particleBouncePlugins = container.plugins.filter(p => !!p.particleBounce);
2565
2735
  }
2566
2736
  update(particle, direction, delta, outMode) {
2567
2737
  if (!this.modes.includes(outMode)) {
2568
2738
  return;
2569
2739
  }
2570
- const container = this.#container;
2571
- let handled = false;
2572
2740
  for (const plugin of this.#particleBouncePlugins) {
2573
- handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
2574
- if (handled) {
2575
- break;
2741
+ if (plugin.particleBounce?.(particle, delta, direction)) {
2742
+ return;
2576
2743
  }
2577
2744
  }
2578
- if (handled) {
2579
- return;
2580
- }
2581
- const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2582
- bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2583
- bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2745
+ const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = this.#container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
2746
+ bounce({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
2584
2747
  }
2585
2748
  }
2586
2749
 
@@ -2669,107 +2832,108 @@
2669
2832
  return;
2670
2833
  }
2671
2834
  const container = this.#container;
2672
- switch (particle.outType) {
2673
- case ParticleOutType.inside: {
2674
- const { x: vx, y: vy } = particle.velocity;
2675
- updateVector.setTo(originPoint);
2676
- updateVector.length = particle.moveCenter.radius;
2677
- updateVector.angle = particle.velocity.angle + Math.PI;
2678
- updateVector.addTo(particle.moveCenter);
2679
- const { dx, dy } = getDistances(particle.position, updateVector);
2680
- if ((vx <= minVelocity && dx >= minDistance) ||
2681
- (vy <= minVelocity && dy >= minDistance) ||
2682
- (vx >= minVelocity && dx <= minDistance) ||
2683
- (vy >= minVelocity && dy <= minDistance)) {
2684
- return;
2685
- }
2686
- particle.position.x = Math.floor(randomInRangeValue({
2687
- min: 0,
2688
- max: container.canvas.size.width,
2689
- }));
2690
- particle.position.y = Math.floor(randomInRangeValue({
2691
- min: 0,
2692
- max: container.canvas.size.height,
2693
- }));
2694
- const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2695
- particle.direction = Math.atan2(-newDy, -newDx);
2696
- particle.velocity.angle = particle.direction;
2697
- particle.justWarped = true;
2698
- break;
2835
+ if (particle.outType === ParticleOutType.inside) {
2836
+ this.#applyOutModeInside(particle, container);
2837
+ }
2838
+ else {
2839
+ if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2840
+ return;
2699
2841
  }
2700
- default: {
2701
- if (particle.isInsideCanvasForOutMode(outMode, direction)) {
2702
- return;
2703
- }
2704
- switch (particle.outType) {
2705
- case ParticleOutType.outside: {
2706
- particle.position.x =
2707
- Math.floor(randomInRangeValue({
2708
- min: -particle.moveCenter.radius,
2709
- max: particle.moveCenter.radius,
2710
- })) + particle.moveCenter.x;
2711
- particle.position.y =
2712
- Math.floor(randomInRangeValue({
2713
- min: -particle.moveCenter.radius,
2714
- max: particle.moveCenter.radius,
2715
- })) + particle.moveCenter.y;
2716
- const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2717
- if (particle.moveCenter.radius) {
2718
- particle.direction = Math.atan2(dy, dx);
2719
- particle.velocity.angle = particle.direction;
2720
- }
2721
- particle.justWarped = true;
2722
- break;
2723
- }
2724
- case ParticleOutType.normal: {
2725
- const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
2726
- bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
2727
- left: -particle.getRadius() - particle.offset.x,
2728
- right: canvasSize.width + particle.getRadius() + particle.offset.x,
2729
- top: -particle.getRadius() - particle.offset.y,
2730
- }, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
2731
- if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2732
- particle.position.x = newPos.left;
2733
- particle.initialPosition.x = particle.position.x;
2734
- if (!warp) {
2735
- particle.position.y = getRandom() * canvasSize.height;
2736
- particle.initialPosition.y = particle.position.y;
2737
- }
2738
- particle.justWarped = true;
2739
- }
2740
- else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2741
- particle.position.x = newPos.right;
2742
- particle.initialPosition.x = particle.position.x;
2743
- if (!warp) {
2744
- particle.position.y = getRandom() * canvasSize.height;
2745
- particle.initialPosition.y = particle.position.y;
2746
- }
2747
- particle.justWarped = true;
2748
- }
2749
- if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2750
- if (!warp) {
2751
- particle.position.x = getRandom() * canvasSize.width;
2752
- particle.initialPosition.x = particle.position.x;
2753
- }
2754
- particle.position.y = newPos.top;
2755
- particle.initialPosition.y = particle.position.y;
2756
- particle.justWarped = true;
2757
- }
2758
- else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2759
- if (!warp) {
2760
- particle.position.x = getRandom() * canvasSize.width;
2761
- particle.initialPosition.x = particle.position.x;
2762
- }
2763
- particle.position.y = newPos.bottom;
2764
- particle.initialPosition.y = particle.position.y;
2765
- particle.justWarped = true;
2766
- }
2767
- break;
2768
- }
2769
- }
2770
- break;
2842
+ if (particle.outType === ParticleOutType.outside) {
2843
+ this.#applyParticleModeOutside(particle);
2771
2844
  }
2772
- }
2845
+ else {
2846
+ this.#applyOutModeNormal(particle, container, direction);
2847
+ }
2848
+ }
2849
+ }
2850
+ #applyOutModeInside(particle, container) {
2851
+ const { x: vx, y: vy } = particle.velocity;
2852
+ updateVector.setTo(originPoint);
2853
+ updateVector.length = particle.moveCenter.radius;
2854
+ updateVector.angle = particle.velocity.angle + Math.PI;
2855
+ updateVector.addTo(particle.moveCenter);
2856
+ const { dx, dy } = getDistances(particle.position, updateVector);
2857
+ if ((vx <= minVelocity && dx >= minDistance) ||
2858
+ (vy <= minVelocity && dy >= minDistance) ||
2859
+ (vx >= minVelocity && dx <= minDistance) ||
2860
+ (vy >= minVelocity && dy <= minDistance)) {
2861
+ return;
2862
+ }
2863
+ particle.position.x = Math.floor(randomInRangeValue({
2864
+ min: 0,
2865
+ max: container.canvas.size.width,
2866
+ }));
2867
+ particle.position.y = Math.floor(randomInRangeValue({
2868
+ min: 0,
2869
+ max: container.canvas.size.height,
2870
+ }));
2871
+ const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
2872
+ particle.direction = Math.atan2(-newDy, -newDx);
2873
+ particle.velocity.angle = particle.direction;
2874
+ particle.justWarped = true;
2875
+ }
2876
+ #applyOutModeNormal(particle, container, direction) {
2877
+ const warp = particle.options.move.warp, canvasSize = container.canvas.size, sizeValue = particle.getRadius(), newPos = {
2878
+ bottom: canvasSize.height + sizeValue + particle.offset.y,
2879
+ left: -sizeValue - particle.offset.x,
2880
+ right: canvasSize.width + sizeValue + particle.offset.x,
2881
+ top: -sizeValue - particle.offset.y,
2882
+ }, nextBounds = calculateBounds(particle.position, sizeValue);
2883
+ if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
2884
+ particle.position.x = newPos.left;
2885
+ particle.initialPosition.x = particle.position.x;
2886
+ if (!warp) {
2887
+ particle.position.y = getRandom() * canvasSize.height;
2888
+ particle.initialPosition.y = particle.position.y;
2889
+ }
2890
+ particle.justWarped = true;
2891
+ }
2892
+ else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
2893
+ particle.position.x = newPos.right;
2894
+ particle.initialPosition.x = particle.position.x;
2895
+ if (!warp) {
2896
+ particle.position.y = getRandom() * canvasSize.height;
2897
+ particle.initialPosition.y = particle.position.y;
2898
+ }
2899
+ particle.justWarped = true;
2900
+ }
2901
+ if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
2902
+ if (!warp) {
2903
+ particle.position.x = getRandom() * canvasSize.width;
2904
+ particle.initialPosition.x = particle.position.x;
2905
+ }
2906
+ particle.position.y = newPos.top;
2907
+ particle.initialPosition.y = particle.position.y;
2908
+ particle.justWarped = true;
2909
+ }
2910
+ else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
2911
+ if (!warp) {
2912
+ particle.position.x = getRandom() * canvasSize.width;
2913
+ particle.initialPosition.x = particle.position.x;
2914
+ }
2915
+ particle.position.y = newPos.bottom;
2916
+ particle.initialPosition.y = particle.position.y;
2917
+ particle.justWarped = true;
2918
+ }
2919
+ }
2920
+ #applyParticleModeOutside(particle) {
2921
+ particle.position.x =
2922
+ Math.floor(randomInRangeValue({
2923
+ min: -particle.moveCenter.radius,
2924
+ max: particle.moveCenter.radius,
2925
+ })) + particle.moveCenter.x;
2926
+ particle.position.y =
2927
+ Math.floor(randomInRangeValue({
2928
+ min: -particle.moveCenter.radius,
2929
+ max: particle.moveCenter.radius,
2930
+ })) + particle.moveCenter.y;
2931
+ const { dx, dy } = getDistances(particle.position, particle.moveCenter);
2932
+ if (particle.moveCenter.radius) {
2933
+ particle.direction = Math.atan2(dy, dx);
2934
+ particle.velocity.angle = particle.direction;
2935
+ }
2936
+ particle.justWarped = true;
2773
2937
  }
2774
2938
  }
2775
2939
 
@@ -2818,7 +2982,7 @@
2818
2982
  }
2819
2983
 
2820
2984
  async function loadOutModesUpdater(engine) {
2821
- engine.checkVersion("4.3.2");
2985
+ engine.checkVersion("4.5.0");
2822
2986
  await engine.pluginManager.register(e => {
2823
2987
  e.pluginManager.addParticleUpdater("outModes", container => {
2824
2988
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2835,39 +2999,9 @@
2835
2999
  this.#pluginManager = pluginManager;
2836
3000
  }
2837
3001
  init(particle) {
2838
- 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;
2839
- if (fill) {
2840
- const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
2841
- particle.fillEnabled = fill.enable;
2842
- particle.fillOpacity = getRangeValue(fill.opacity);
2843
- particle.fillAnimation = fillColor.animation;
2844
- const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
2845
- if (fillHslColor) {
2846
- particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
2847
- }
2848
- }
2849
- else {
2850
- particle.fillEnabled = false;
2851
- particle.fillAnimation = undefined;
2852
- particle.fillColor = undefined;
2853
- particle.fillOpacity = defaultOpacity;
2854
- }
2855
- if (stroke) {
2856
- const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
2857
- particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
2858
- particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
2859
- particle.strokeAnimation = strokeColor.animation;
2860
- const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
2861
- if (strokeHslColor) {
2862
- particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
2863
- }
2864
- }
2865
- else {
2866
- particle.strokeAnimation = undefined;
2867
- particle.strokeColor = undefined;
2868
- particle.strokeOpacity = defaultOpacity;
2869
- particle.strokeWidth = 0;
2870
- }
3002
+ const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates);
3003
+ this.#initFill(paint, particle, container);
3004
+ this.#initStroke(paint, particle, container);
2871
3005
  }
2872
3006
  isEnabled(particle) {
2873
3007
  const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
@@ -2886,10 +3020,48 @@
2886
3020
  updateColor(particle.fillColor, delta, this.#container.hdr);
2887
3021
  updateColor(particle.strokeColor, delta, this.#container.hdr);
2888
3022
  }
3023
+ #initFill(paint, particle, container) {
3024
+ const fill = paint?.fill, color = paint?.color, paintColor = color ?? undefined;
3025
+ if (!fill) {
3026
+ particle.fillEnabled = false;
3027
+ particle.fillAnimation = undefined;
3028
+ particle.fillColor = undefined;
3029
+ particle.fillOpacity = defaultOpacity;
3030
+ return;
3031
+ }
3032
+ const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
3033
+ particle.fillEnabled = fill.enable;
3034
+ particle.fillOpacity = getRangeValue(fill.opacity);
3035
+ particle.fillAnimation = fillColor.animation;
3036
+ const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
3037
+ if (!fillHslColor) {
3038
+ return;
3039
+ }
3040
+ particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
3041
+ }
3042
+ #initStroke(paint, particle, container) {
3043
+ const stroke = paint?.stroke, color = paint?.color, paintColor = color ?? undefined;
3044
+ if (!stroke) {
3045
+ particle.strokeAnimation = undefined;
3046
+ particle.strokeColor = undefined;
3047
+ particle.strokeOpacity = defaultOpacity;
3048
+ particle.strokeWidth = 0;
3049
+ return;
3050
+ }
3051
+ const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
3052
+ particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
3053
+ particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
3054
+ particle.strokeAnimation = strokeColor.animation;
3055
+ const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
3056
+ if (!strokeHslColor) {
3057
+ return;
3058
+ }
3059
+ particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
3060
+ }
2889
3061
  }
2890
3062
 
2891
3063
  async function loadPaintUpdater(engine) {
2892
- engine.checkVersion("4.3.2");
3064
+ engine.checkVersion("4.5.0");
2893
3065
  await engine.pluginManager.register(e => {
2894
3066
  e.pluginManager.addParticleUpdater("paint", container => {
2895
3067
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -2931,7 +3103,7 @@
2931
3103
  if (!this.accepts(input)) {
2932
3104
  return;
2933
3105
  }
2934
- const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
3106
+ const result = rgbRegex.exec(input), radix = 10, minLength = 4;
2935
3107
  return result
2936
3108
  ? {
2937
3109
  a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
@@ -2944,7 +3116,7 @@
2944
3116
  }
2945
3117
 
2946
3118
  async function loadRgbColorPlugin(engine) {
2947
- engine.checkVersion("4.3.2");
3119
+ engine.checkVersion("4.5.0");
2948
3120
  await engine.pluginManager.register(e => {
2949
3121
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
2950
3122
  });
@@ -3029,7 +3201,7 @@
3029
3201
  }
3030
3202
 
3031
3203
  async function loadSizeUpdater(engine) {
3032
- engine.checkVersion("4.3.2");
3204
+ engine.checkVersion("4.5.0");
3033
3205
  await engine.pluginManager.register(e => {
3034
3206
  e.pluginManager.addParticleUpdater("size", container => {
3035
3207
  return Promise.resolve(new SizeUpdater(container));
@@ -3038,7 +3210,7 @@
3038
3210
  }
3039
3211
 
3040
3212
  async function loadBasic(engine) {
3041
- engine.checkVersion("4.3.2");
3213
+ engine.checkVersion("4.5.0");
3042
3214
  await engine.pluginManager.register(async (e) => {
3043
3215
  await Promise.all([
3044
3216
  loadBlendPlugin(e),
@@ -3055,6 +3227,16 @@
3055
3227
  });
3056
3228
  }
3057
3229
 
3230
+ const ErrorCodes = {
3231
+ interactivityPluginNotLoaded: "TSP-3201",
3232
+ interactivityClickHandlerNotSet: "TSP-3202",
3233
+ };
3234
+
3235
+ const ErrorMessages = {
3236
+ [ErrorCodes.interactivityPluginNotLoaded]: "tsParticles Interactivity Plugin is not loaded",
3237
+ [ErrorCodes.interactivityClickHandlerNotSet]: "Click handlers can only be set after calling tsParticles.load()",
3238
+ };
3239
+
3058
3240
  class ClickEvent {
3059
3241
  enable = false;
3060
3242
  mode = [];
@@ -3131,9 +3313,7 @@
3131
3313
 
3132
3314
  class Modes {
3133
3315
  #container;
3134
- #pluginManager;
3135
- constructor(pluginManager, container) {
3136
- this.#pluginManager = pluginManager;
3316
+ constructor(container) {
3137
3317
  this.#container = container;
3138
3318
  }
3139
3319
  load(data) {
@@ -3143,10 +3323,7 @@
3143
3323
  if (!this.#container) {
3144
3324
  return;
3145
3325
  }
3146
- const interactors = this.#pluginManager.interactors?.get(this.#container);
3147
- if (!interactors) {
3148
- return;
3149
- }
3326
+ const interactors = this.#container.interactionManager?.interactors ?? [];
3150
3327
  for (const interactor of interactors) {
3151
3328
  if (!interactor.loadModeOptions) {
3152
3329
  continue;
@@ -3160,8 +3337,8 @@
3160
3337
  detectsOn = InteractivityDetect.window;
3161
3338
  events = new Events();
3162
3339
  modes;
3163
- constructor(pluginManager, container) {
3164
- this.modes = new Modes(pluginManager, container);
3340
+ constructor(container) {
3341
+ this.modes = new Modes(container);
3165
3342
  }
3166
3343
  load(data) {
3167
3344
  if (isNull(data)) {
@@ -3192,10 +3369,10 @@
3192
3369
  }
3193
3370
  let interactivityOptions = options.interactivity;
3194
3371
  if (!interactivityOptions?.load) {
3195
- options.interactivity = interactivityOptions = new Interactivity(this.#pluginManager, container);
3372
+ options.interactivity = interactivityOptions = new Interactivity(container);
3196
3373
  }
3197
3374
  interactivityOptions.load(source?.interactivity);
3198
- const interactors = this.#pluginManager.interactors?.get(container);
3375
+ const interactors = container.interactionManager?.interactors;
3199
3376
  if (!interactors) {
3200
3377
  return;
3201
3378
  }
@@ -3209,7 +3386,7 @@
3209
3386
  if (source?.interactivity) {
3210
3387
  options.interactivity = deepExtend({}, source.interactivity);
3211
3388
  }
3212
- const interactors = this.#pluginManager.interactors?.get(container);
3389
+ const interactors = container.interactionManager?.interactors;
3213
3390
  if (!interactors) {
3214
3391
  return;
3215
3392
  }
@@ -3239,25 +3416,25 @@
3239
3416
  const clickEvent = "click", mouseDownEvent = "pointerdown", mouseUpEvent = "pointerup", mouseLeaveEvent = "pointerleave", mouseMoveEvent = "pointermove", touchStartEvent = "touchstart", touchEndEvent = "touchend", touchMoveEvent = "touchmove", touchCancelEvent = "touchcancel";
3240
3417
 
3241
3418
  async function loadInteractivityPlugin(engine) {
3242
- engine.checkVersion("4.3.2");
3419
+ engine.checkVersion("4.5.0");
3420
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
3421
+ addErrorMessages(ErrorMessages);
3422
+ }
3243
3423
  await engine.pluginManager.register(e => {
3244
3424
  const interactivityEngine = e, interactivityPluginManager = interactivityEngine.pluginManager;
3245
3425
  interactivityPluginManager.addPlugin(new InteractivityPlugin(interactivityPluginManager));
3246
3426
  interactivityPluginManager.initializers.interactors ??= new Map();
3247
- interactivityPluginManager.interactors ??= new Map();
3248
3427
  interactivityPluginManager.addInteractor = (name, interactorInitializer) => {
3428
+ if (interactivityPluginManager.initialized) {
3429
+ return;
3430
+ }
3249
3431
  interactivityPluginManager.initializers.interactors ??= new Map();
3250
3432
  interactivityPluginManager.initializers.interactors.set(name, interactorInitializer);
3251
3433
  };
3252
- interactivityPluginManager.getInteractors = async (container, force = false) => {
3253
- interactivityPluginManager.interactors ??= new Map();
3254
- interactivityPluginManager.initializers.interactors ??= new Map();
3255
- return getItemsFromInitializer(container, interactivityPluginManager.interactors, interactivityPluginManager.initializers.interactors, force);
3256
- };
3257
3434
  interactivityPluginManager.setOnClickHandler = (callback) => {
3258
3435
  const { items } = interactivityEngine;
3259
3436
  if (!items.length) {
3260
- throw new Error("Click handlers can only be set after calling tsParticles.load()");
3437
+ throw new Error(getErrorMessage(ErrorCodes.interactivityClickHandlerNotSet));
3261
3438
  }
3262
3439
  items.forEach(item => {
3263
3440
  const interactivityContainer = item;
@@ -3268,7 +3445,7 @@
3268
3445
  }
3269
3446
  function ensureInteractivityPluginLoaded(e) {
3270
3447
  if (!e.pluginManager.addInteractor) {
3271
- throw new Error("tsParticles Interactivity Plugin is not loaded");
3448
+ throw new Error(getErrorMessage(ErrorCodes.interactivityPluginNotLoaded));
3272
3449
  }
3273
3450
  }
3274
3451
 
@@ -3436,7 +3613,7 @@
3436
3613
  }
3437
3614
 
3438
3615
  async function loadExternalTrailInteraction(engine) {
3439
- engine.checkVersion("4.3.2");
3616
+ engine.checkVersion("4.5.0");
3440
3617
  await engine.pluginManager.register((e) => {
3441
3618
  ensureInteractivityPluginLoaded(e);
3442
3619
  e.pluginManager.addInteractor?.("externalTrail", container => {
@@ -3582,7 +3759,7 @@
3582
3759
  }
3583
3760
 
3584
3761
  async function loadLifeUpdater(engine) {
3585
- engine.checkVersion("4.3.2");
3762
+ engine.checkVersion("4.5.0");
3586
3763
  await engine.pluginManager.register(e => {
3587
3764
  e.pluginManager.addParticleUpdater("life", container => {
3588
3765
  return Promise.resolve(new LifeUpdater(container));
@@ -3736,21 +3913,28 @@
3736
3913
  });
3737
3914
  }
3738
3915
  clear() {
3916
+ const visited = new Set();
3739
3917
  for (const plugin of this.#canvasClearPlugins) {
3918
+ visited.add(plugin);
3740
3919
  if (plugin.canvasClear?.() ?? false) {
3741
- return;
3920
+ return plugin;
3742
3921
  }
3743
3922
  }
3744
3923
  for (const layer of Object.values(DrawLayer)) {
3745
3924
  if (typeof layer === "number") {
3746
3925
  for (const plugin of this.#getLayerPlugins(layer)) {
3926
+ if (visited.has(plugin)) {
3927
+ continue;
3928
+ }
3929
+ visited.add(plugin);
3747
3930
  if (plugin.canvasClear?.() ?? false) {
3748
- return;
3931
+ return plugin;
3749
3932
  }
3750
3933
  }
3751
3934
  }
3752
3935
  }
3753
3936
  this.canvasClear();
3937
+ return undefined;
3754
3938
  }
3755
3939
  destroy() {
3756
3940
  this.stop();
@@ -3791,7 +3975,11 @@
3791
3975
  if (!fColor && !sColor) {
3792
3976
  return;
3793
3977
  }
3794
- const container = this.#container, zIndexOptions = particle.options.zIndex, zIndexFactor = zIndexFactorOffset - particle.zIndexFactor, { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(), transform = this.#reusableTransform, colorStyles = this.#reusableColorStyles, fill = fColor ? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity) : undefined, stroke = sColor ? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity) : fill;
3978
+ const container = this.#container, zIndexOptions = particle.options.zIndex, zIndexFactor = zIndexFactorOffset - particle.zIndexFactor, { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(), transform = this.#reusableTransform, colorStyles = this.#reusableColorStyles, fill = fColor
3979
+ ? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity, container.peakNits, container.hdrMode)
3980
+ : undefined, stroke = sColor
3981
+ ? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity, container.peakNits, container.hdrMode)
3982
+ : fill;
3795
3983
  transform.a = transform.b = transform.c = transform.d = undefined;
3796
3984
  colorStyles.fill = fill;
3797
3985
  colorStyles.stroke = stroke;
@@ -3824,8 +4012,7 @@
3824
4012
  });
3825
4013
  }
3826
4014
  drawParticles(delta) {
3827
- const { particles, actualOptions } = this.#container;
3828
- this.clear();
4015
+ const { particles, actualOptions } = this.#container, clearedPlugin = this.clear();
3829
4016
  particles.update(delta);
3830
4017
  this.draw(ctx => {
3831
4018
  const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
@@ -3847,7 +4034,9 @@
3847
4034
  }
3848
4035
  }
3849
4036
  for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3850
- plugin.canvasPaint?.();
4037
+ if (plugin !== clearedPlugin) {
4038
+ plugin.canvasPaint?.();
4039
+ }
3851
4040
  }
3852
4041
  for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3853
4042
  plugin.drawSettingsSetup?.(ctx, delta);
@@ -3864,50 +4053,22 @@
3864
4053
  }
3865
4054
  init() {
3866
4055
  this.initUpdaters();
3867
- this.initPlugins();
3868
- this.#resolveBackgroundElement();
3869
- this.paint();
3870
- }
3871
- initPlugins() {
3872
- this.#canvasClearPlugins = [];
3873
- this.#colorPlugins = [];
3874
- this.#drawParticlePlugins = [];
3875
- this.#drawParticlesSetupPlugins = [];
3876
- this.#drawParticlesCleanupPlugins = [];
3877
- for (const layer of Object.values(DrawLayer)) {
3878
- if (typeof layer === "number") {
3879
- this.#layers[layer] = [];
3880
- }
3881
- }
3882
- for (const plugin of this.#container.plugins) {
3883
- if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3884
- this.#colorPlugins.push(plugin);
3885
- }
3886
- if (plugin.drawParticle) {
3887
- this.#drawParticlePlugins.push(plugin);
3888
- }
3889
- if (plugin.drawParticleSetup) {
3890
- this.#drawParticlesSetupPlugins.push(plugin);
3891
- }
3892
- if (plugin.drawParticleCleanup) {
3893
- this.#drawParticlesCleanupPlugins.push(plugin);
3894
- }
3895
- if (plugin.canvasClear) {
3896
- this.#canvasClearPlugins.push(plugin);
3897
- }
3898
- if (plugin.canvasPaint) {
3899
- this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
3900
- }
3901
- if (plugin.drawSettingsSetup) {
3902
- this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
3903
- }
3904
- if (plugin.draw) {
3905
- this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
3906
- }
3907
- if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
3908
- this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
4056
+ this.initPlugins();
4057
+ this.#resolveBackgroundElement();
4058
+ this.paint();
4059
+ }
4060
+ initPlugins() {
4061
+ this.#canvasClearPlugins = [];
4062
+ this.#colorPlugins = [];
4063
+ this.#drawParticlePlugins = [];
4064
+ this.#drawParticlesSetupPlugins = [];
4065
+ this.#drawParticlesCleanupPlugins = [];
4066
+ for (const layer of Object.values(DrawLayer)) {
4067
+ if (typeof layer === "number") {
4068
+ this.#layers[layer] = [];
3909
4069
  }
3910
4070
  }
4071
+ this.#initPluginsArray();
3911
4072
  }
3912
4073
  initUpdaters() {
3913
4074
  this.#preDrawUpdaters = [];
@@ -4116,6 +4277,45 @@
4116
4277
  this.#reusablePluginColors[sColorIndex] = sColor;
4117
4278
  return this.#reusablePluginColors;
4118
4279
  }
4280
+ #initLayerPlugin(plugin) {
4281
+ const layerMap = [
4282
+ [DrawLayer.BackgroundMask, !!plugin.canvasPaint],
4283
+ [DrawLayer.CanvasSetup, !!plugin.drawSettingsSetup],
4284
+ [DrawLayer.PluginContent, !!plugin.draw],
4285
+ [DrawLayer.CanvasCleanup, !!(plugin.clearDraw ?? plugin.drawSettingsCleanup)],
4286
+ ];
4287
+ for (const [layer, active] of layerMap) {
4288
+ if (active) {
4289
+ this.#layers[layer].push(plugin);
4290
+ }
4291
+ }
4292
+ }
4293
+ #initPluginsArray() {
4294
+ for (const plugin of this.#container.plugins) {
4295
+ if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
4296
+ this.#colorPlugins.push(plugin);
4297
+ }
4298
+ if (plugin.drawParticle) {
4299
+ this.#drawParticlePlugins.push(plugin);
4300
+ }
4301
+ if (plugin.drawParticleSetup) {
4302
+ this.#drawParticlesSetupPlugins.push(plugin);
4303
+ }
4304
+ if (plugin.drawParticleCleanup) {
4305
+ this.#drawParticlesCleanupPlugins.push(plugin);
4306
+ }
4307
+ if (plugin.canvasClear) {
4308
+ this.#canvasClearPlugins.push(plugin);
4309
+ }
4310
+ this.#initLayerPlugin(plugin);
4311
+ }
4312
+ }
4313
+ #isSupportedElement(element) {
4314
+ return ((typeof HTMLCanvasElement !== "undefined" && element instanceof HTMLCanvasElement) ||
4315
+ (typeof OffscreenCanvas !== "undefined" && element instanceof OffscreenCanvas) ||
4316
+ (typeof HTMLVideoElement !== "undefined" && element instanceof HTMLVideoElement) ||
4317
+ (typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement));
4318
+ }
4119
4319
  #resolveBackgroundElement() {
4120
4320
  const background = this.#container.actualOptions.background;
4121
4321
  this.#backgroundElement = null;
@@ -4125,7 +4325,7 @@
4125
4325
  if (typeof background.element === "string") {
4126
4326
  if (typeof document !== "undefined") {
4127
4327
  const node = document.querySelector(background.element);
4128
- if (node instanceof HTMLCanvasElement || node instanceof HTMLVideoElement || node instanceof HTMLImageElement) {
4328
+ if (node && this.#isSupportedElement(node)) {
4129
4329
  this.#backgroundElement = node;
4130
4330
  }
4131
4331
  else if (node) {
@@ -4136,10 +4336,7 @@
4136
4336
  }
4137
4337
  }
4138
4338
  }
4139
- else if (background.element instanceof HTMLCanvasElement ||
4140
- background.element instanceof OffscreenCanvas ||
4141
- background.element instanceof HTMLVideoElement ||
4142
- background.element instanceof HTMLImageElement) {
4339
+ else if (this.#isSupportedElement(background.element)) {
4143
4340
  this.#backgroundElement = background.element;
4144
4341
  }
4145
4342
  }
@@ -4157,7 +4354,7 @@
4157
4354
  return transferredCanvas;
4158
4355
  }
4159
4356
  if (typeof canvas.transferControlToOffscreen !== "function") {
4160
- throw new TypeError("OffscreenCanvas is required but not supported by this browser");
4357
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasUnsupported));
4161
4358
  }
4162
4359
  try {
4163
4360
  const offscreenCanvas = canvas.transferControlToOffscreen();
@@ -4165,10 +4362,16 @@
4165
4362
  return offscreenCanvas;
4166
4363
  }
4167
4364
  catch {
4168
- throw new TypeError("OffscreenCanvas transfer failed");
4365
+ throw new TypeError(getErrorMessage(ErrorCodes$2.offscreenCanvasTransferFailed));
4169
4366
  }
4170
4367
  }, isHtmlCanvasElement = (canvas) => {
4171
4368
  return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
4369
+ }, getEffectiveColorSpace = (context) => {
4370
+ if (!context) {
4371
+ return undefined;
4372
+ }
4373
+ const colorInfo = context;
4374
+ return colorInfo.getContextAttributes?.().colorSpace ?? undefined;
4172
4375
  };
4173
4376
  function setStyle(canvas, style, important = false) {
4174
4377
  if (!style) {
@@ -4207,6 +4410,7 @@
4207
4410
  zoom = defaultZoom;
4208
4411
  #container;
4209
4412
  #generated;
4413
+ #hdrMediaListeners;
4210
4414
  #mutationObserver;
4211
4415
  #originalStyle;
4212
4416
  #pluginManager;
@@ -4271,7 +4475,6 @@
4271
4475
  });
4272
4476
  this.resize();
4273
4477
  this.#initStyle();
4274
- this.initBackground();
4275
4478
  this.#safeMutationObserver(obs => {
4276
4479
  const element = this.domElement;
4277
4480
  if (!element || !(element instanceof Node)) {
@@ -4281,6 +4484,8 @@
4281
4484
  });
4282
4485
  this.initPlugins();
4283
4486
  this.#initContext();
4487
+ this.initBackground();
4488
+ this.#initHdrListeners();
4284
4489
  this.render.init();
4285
4490
  }
4286
4491
  initBackground() {
@@ -4290,7 +4495,8 @@
4290
4495
  }
4291
4496
  const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
4292
4497
  if (color) {
4293
- elementStyle.backgroundColor = getStyleFromRgb(color, container.actualOptions.hdr, background.opacity);
4498
+ const hdrOptions = container.actualOptions.hdr;
4499
+ elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard));
4294
4500
  }
4295
4501
  else {
4296
4502
  elementStyle.backgroundColor = "";
@@ -4387,6 +4593,7 @@
4387
4593
  obs.disconnect();
4388
4594
  });
4389
4595
  this.#mutationObserver = undefined;
4596
+ this.#removeHdrListeners();
4390
4597
  this.render.stop();
4391
4598
  }
4392
4599
  async windowResize() {
@@ -4406,9 +4613,16 @@
4406
4613
  }
4407
4614
  }
4408
4615
  #initContext() {
4409
- const container = this.#container, canSupportHdr = container.actualOptions.hdr &&
4616
+ const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
4410
4617
  safeMatchMedia("(color-gamut: p3)")?.matches &&
4411
4618
  safeMatchMedia("(dynamic-range: high)")?.matches;
4619
+ container.hdrMode = toEnumValue(HdrMode, container.actualOptions.hdr.mode, HdrMode.standard);
4620
+ container.peakNits = container.actualOptions.hdr.peakNits;
4621
+ const renderCanvas = this.renderCanvas;
4622
+ if (!renderCanvas) {
4623
+ container.hdr = canSupportHdr ?? false;
4624
+ return;
4625
+ }
4412
4626
  this.render.setContextSettings({
4413
4627
  alpha: true,
4414
4628
  desynchronized: true,
@@ -4417,11 +4631,51 @@
4417
4631
  ? { colorSpace: "display-p3", colorType: "float16" }
4418
4632
  : { colorSpace: "srgb" }),
4419
4633
  });
4420
- const renderCanvas = this.renderCanvas;
4421
- if (!renderCanvas) {
4422
- return;
4634
+ let context;
4635
+ try {
4636
+ context = renderCanvas.getContext("2d", this.render.settings);
4423
4637
  }
4424
- this.render.setContext(renderCanvas.getContext("2d", this.render.settings));
4638
+ catch {
4639
+ context = null;
4640
+ }
4641
+ if (canSupportHdr && !context) {
4642
+ const sdrSettings = {
4643
+ alpha: true,
4644
+ desynchronized: true,
4645
+ willReadFrequently: false,
4646
+ colorSpace: "srgb",
4647
+ };
4648
+ this.render.setContextSettings(sdrSettings);
4649
+ try {
4650
+ context = renderCanvas.getContext("2d", sdrSettings);
4651
+ }
4652
+ catch {
4653
+ context = null;
4654
+ }
4655
+ }
4656
+ this.render.setContext(context);
4657
+ if (context) {
4658
+ const effectiveColorSpace = getEffectiveColorSpace(context);
4659
+ if (effectiveColorSpace !== undefined) {
4660
+ container.hdr = effectiveColorSpace === "display-p3";
4661
+ }
4662
+ }
4663
+ }
4664
+ #initHdrListeners() {
4665
+ this.#removeHdrListeners();
4666
+ const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
4667
+ this.#initContext();
4668
+ this.initBackground();
4669
+ }, listeners = [];
4670
+ if (p3Query) {
4671
+ p3Query.addEventListener("change", handleChange);
4672
+ listeners.push({ handler: handleChange, mql: p3Query });
4673
+ }
4674
+ if (hdrQuery) {
4675
+ hdrQuery.addEventListener("change", handleChange);
4676
+ listeners.push({ handler: handleChange, mql: hdrQuery });
4677
+ }
4678
+ this.#hdrMediaListeners = listeners;
4425
4679
  }
4426
4680
  #initStyle() {
4427
4681
  const element = this.domElement, options = this.#container.actualOptions;
@@ -4445,6 +4699,15 @@
4445
4699
  element.style.setProperty(key, value, "important");
4446
4700
  }
4447
4701
  }
4702
+ #removeHdrListeners() {
4703
+ if (!this.#hdrMediaListeners) {
4704
+ return;
4705
+ }
4706
+ for (const { handler, mql } of this.#hdrMediaListeners) {
4707
+ mql.removeEventListener("change", handler);
4708
+ }
4709
+ this.#hdrMediaListeners = undefined;
4710
+ }
4448
4711
  #repairStyle() {
4449
4712
  const element = this.domElement;
4450
4713
  if (!element) {
@@ -4534,8 +4797,7 @@
4534
4797
  this.#resizeTimeout = undefined;
4535
4798
  }
4536
4799
  const handleResize = async () => {
4537
- const canvas = this.#container.canvas;
4538
- await canvas.windowResize();
4800
+ await this.#container.canvas.windowResize();
4539
4801
  };
4540
4802
  this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
4541
4803
  }
@@ -4563,8 +4825,8 @@
4563
4825
  }
4564
4826
  else if (!this.#resizeObserver && add && canvasEl) {
4565
4827
  this.#resizeObserver = new ResizeObserver((entries) => {
4566
- const entry = entries.find(e => e.target === canvasEl);
4567
- if (!entry) {
4828
+ const res = entries.some(e => e.target === canvasEl);
4829
+ if (!res) {
4568
4830
  return;
4569
4831
  }
4570
4832
  this.#handleWindowResize();
@@ -4828,10 +5090,10 @@
4828
5090
  return this.#modifiers.find(m => m.id === id);
4829
5091
  }
4830
5092
  getOpacity() {
4831
- 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;
4832
- this.#cachedOpacityData.fillOpacity = opacity * fillOpacity * zOpacityFactor;
5093
+ 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;
5094
+ this.#cachedOpacityData.fillOpacity = this.fillOpacity ?? defaultOpacity$1;
4833
5095
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
4834
- this.#cachedOpacityData.strokeOpacity = opacity * strokeOpacity * zOpacityFactor;
5096
+ this.#cachedOpacityData.strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
4835
5097
  return this.#cachedOpacityData;
4836
5098
  }
4837
5099
  getPosition() {
@@ -5078,7 +5340,7 @@
5078
5340
  #initPosition(position) {
5079
5341
  const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
5080
5342
  if (!initialPosition) {
5081
- throw new Error("a valid position cannot be found for particle");
5343
+ throw new Error(getErrorMessage(ErrorCodes$2.particlePositionNotFound));
5082
5344
  }
5083
5345
  this.position = initialPosition;
5084
5346
  this.initialPosition = this.position.copy();
@@ -5127,6 +5389,12 @@
5127
5389
  this.#circlePoolIdx = 0;
5128
5390
  this.#rectanglePoolIdx = 0;
5129
5391
  }
5392
+ get cellSize() {
5393
+ return this.#cellSize;
5394
+ }
5395
+ set cellSize(value) {
5396
+ this.#pendingCellSize = value;
5397
+ }
5130
5398
  clear() {
5131
5399
  this.#cells.clear();
5132
5400
  const pendingCellSize = this.#pendingCellSize;
@@ -5150,18 +5418,7 @@
5150
5418
  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);
5151
5419
  for (let cx = minCellX; cx <= maxCellX; cx++) {
5152
5420
  for (let cy = minCellY; cy <= maxCellY; cy++) {
5153
- const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
5154
- if (!cellParticles) {
5155
- continue;
5156
- }
5157
- for (const p of cellParticles) {
5158
- if (check && !check(p)) {
5159
- continue;
5160
- }
5161
- if (range.contains(p.getPosition())) {
5162
- out.push(p);
5163
- }
5164
- }
5421
+ this.#queryCell(cx, cy, range, check, out);
5165
5422
  }
5166
5423
  }
5167
5424
  return out;
@@ -5176,14 +5433,23 @@
5176
5433
  this.#releaseShapes();
5177
5434
  return result;
5178
5435
  }
5179
- setCellSize(cellSize) {
5180
- this.#pendingCellSize = cellSize;
5181
- }
5182
5436
  #acquireCircle(x, y, r) {
5183
- return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
5437
+ const idx = this.#circlePoolIdx++;
5438
+ let circle = this.#circlePool[idx];
5439
+ if (!circle) {
5440
+ circle = new Circle(x, y, r);
5441
+ this.#circlePool[idx] = circle;
5442
+ }
5443
+ return circle.reset(x, y, r);
5184
5444
  }
5185
5445
  #acquireRectangle(x, y, w, h) {
5186
- return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
5446
+ const idx = this.#rectanglePoolIdx++;
5447
+ let rectangle = this.#rectanglePool[idx];
5448
+ if (!rectangle) {
5449
+ rectangle = new Rectangle(x, y, w, h);
5450
+ this.#rectanglePool[idx] = rectangle;
5451
+ }
5452
+ return rectangle.reset(x, y, w, h);
5187
5453
  }
5188
5454
  #cellKeyFromCoords(x, y) {
5189
5455
  const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
@@ -5210,6 +5476,20 @@
5210
5476
  }
5211
5477
  return null;
5212
5478
  }
5479
+ #queryCell(cx, cy, range, check, out) {
5480
+ const cellParticles = this.#cells.get(`${cx}_${cy}`);
5481
+ if (!cellParticles) {
5482
+ return;
5483
+ }
5484
+ for (const p of cellParticles) {
5485
+ if (check && !check(p)) {
5486
+ continue;
5487
+ }
5488
+ if (range.contains(p.getPosition())) {
5489
+ out.push(p);
5490
+ }
5491
+ }
5492
+ }
5213
5493
  #releaseShapes() {
5214
5494
  this.#circlePoolIdx = 0;
5215
5495
  this.#rectanglePoolIdx = 0;
@@ -5218,9 +5498,9 @@
5218
5498
 
5219
5499
  class ParticlesManager {
5220
5500
  checkParticlePositionPlugins;
5221
- grid;
5222
5501
  #array;
5223
5502
  #container;
5503
+ #grid;
5224
5504
  #groupLimits;
5225
5505
  #limit;
5226
5506
  #nextId;
@@ -5244,7 +5524,7 @@
5244
5524
  this.#groupLimits = new Map();
5245
5525
  this.#particleBuckets = new Map();
5246
5526
  this.#zBuckets = this.#createBuckets(this.#container.zLayers);
5247
- this.grid = new SpatialHashGrid(spatialHashGridCellSize);
5527
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize);
5248
5528
  this.checkParticlePositionPlugins = [];
5249
5529
  this.#particleResetPlugins = [];
5250
5530
  this.#particleUpdatePlugins = [];
@@ -5255,23 +5535,12 @@
5255
5535
  get count() {
5256
5536
  return this.#array.length;
5257
5537
  }
5538
+ get grid() {
5539
+ return this.#grid;
5540
+ }
5258
5541
  addParticle(position, overrideOptions, group, initializer) {
5259
- const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
5260
- if (limit > minLimit) {
5261
- switch (limitMode) {
5262
- case LimitMode.delete: {
5263
- const countToRemove = currentCount + countOffset - limit;
5264
- if (countToRemove > minCount) {
5265
- this.removeQuantity(countToRemove);
5266
- }
5267
- break;
5268
- }
5269
- case LimitMode.wait:
5270
- if (currentCount >= limit) {
5271
- return;
5272
- }
5273
- break;
5274
- }
5542
+ if (this.#checkAndApplyLimits(group, this.count)) {
5543
+ return;
5275
5544
  }
5276
5545
  try {
5277
5546
  const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
@@ -5335,7 +5604,7 @@
5335
5604
  return this.#array[index];
5336
5605
  }
5337
5606
  async init() {
5338
- const container = this.#container, options = container.actualOptions;
5607
+ const container = this.#container;
5339
5608
  this.checkParticlePositionPlugins = [];
5340
5609
  this.#updatePlugins = [];
5341
5610
  this.#particleUpdatePlugins = [];
@@ -5344,59 +5613,16 @@
5344
5613
  this.#postParticleUpdatePlugins = [];
5345
5614
  this.#particleBuckets.clear();
5346
5615
  this.#resetBuckets(container.zLayers);
5347
- this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5348
- for (const plugin of container.plugins) {
5349
- if (plugin.redrawInit) {
5350
- await plugin.redrawInit();
5351
- }
5352
- if (plugin.checkParticlePosition) {
5353
- this.checkParticlePositionPlugins.push(plugin);
5354
- }
5355
- if (plugin.update) {
5356
- this.#updatePlugins.push(plugin);
5357
- }
5358
- if (plugin.particleUpdate) {
5359
- this.#particleUpdatePlugins.push(plugin);
5360
- }
5361
- if (plugin.postUpdate) {
5362
- this.#postUpdatePlugins.push(plugin);
5363
- }
5364
- if (plugin.particleReset) {
5365
- this.#particleResetPlugins.push(plugin);
5366
- }
5367
- if (plugin.postParticleUpdate) {
5368
- this.#postParticleUpdatePlugins.push(plugin);
5369
- }
5370
- }
5371
- await this.#container.initDrawersAndUpdaters();
5372
- for (const drawer of this.#container.effectDrawers.values()) {
5373
- await drawer.init?.(container);
5374
- }
5375
- for (const drawer of this.#container.shapeDrawers.values()) {
5376
- await drawer.init?.(container);
5377
- }
5378
- let handled = false;
5379
- for (const plugin of container.plugins) {
5380
- handled = plugin.particlesInitialization?.() ?? handled;
5381
- if (handled) {
5382
- break;
5383
- }
5384
- }
5385
- if (!handled) {
5386
- const particlesOptions = options.particles, groups = particlesOptions.groups;
5387
- for (const group in groups) {
5388
- const groupOptions = groups[group];
5389
- if (!groupOptions) {
5390
- continue;
5391
- }
5392
- for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5393
- this.addParticle(undefined, groupOptions, group);
5394
- }
5395
- }
5396
- for (let i = this.count; i < particlesOptions.number.value; i++) {
5397
- this.addParticle();
5616
+ this.#grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
5617
+ this.#applyLimits(container.actualOptions.particles);
5618
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5619
+ for (const group in groups) {
5620
+ const groupData = groups[group];
5621
+ if (groupData) {
5622
+ this.#applyLimits(loadParticlesOptions(this.#pluginManager, this.#container, groupData), group, groupData);
5398
5623
  }
5399
5624
  }
5625
+ await this.#initPlugins();
5400
5626
  }
5401
5627
  push(nb, position, overrideOptions, group) {
5402
5628
  for (let i = 0; i < nb; i++) {
@@ -5411,13 +5637,13 @@
5411
5637
  remove(particle, group, override) {
5412
5638
  this.removeAt(this.#array.indexOf(particle), undefined, group, override);
5413
5639
  }
5414
- removeAt(index, quantity = defaultRemoveQuantity, group, override) {
5640
+ removeAt(index, quantity = defaultRemoveQuantity, group, override, matchGroup = true) {
5415
5641
  if (index < minIndex || index > this.count) {
5416
5642
  return;
5417
5643
  }
5418
5644
  let deleted = 0;
5419
5645
  for (let i = index; deleted < quantity && i < this.count; i++) {
5420
- if (this.#removeParticle(i, group, override)) {
5646
+ if (this.#removeParticle(i, group, override, matchGroup)) {
5421
5647
  i--;
5422
5648
  deleted++;
5423
5649
  }
@@ -5448,7 +5674,7 @@
5448
5674
  this.#resizeFactor = factor;
5449
5675
  }
5450
5676
  update(delta) {
5451
- this.grid.clear();
5677
+ this.#grid.clear();
5452
5678
  for (const plugin of this.#updatePlugins) {
5453
5679
  plugin.update?.(delta);
5454
5680
  }
@@ -5459,7 +5685,7 @@
5459
5685
  this.#updateParticlesPhase2(delta, particlesToDelete);
5460
5686
  if (particlesToDelete.size) {
5461
5687
  for (const particle of particlesToDelete) {
5462
- this.remove(particle);
5688
+ this.remove(particle, particle.group);
5463
5689
  }
5464
5690
  }
5465
5691
  this.#resizeFactor = undefined;
@@ -5470,12 +5696,7 @@
5470
5696
  #applyDensity(options, pluginsCount, group, groupOptions) {
5471
5697
  const numberOptions = options.number;
5472
5698
  if (!numberOptions.density.enable) {
5473
- if (group === undefined) {
5474
- this.#limit = numberOptions.limit.value;
5475
- }
5476
- else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
5477
- this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
5478
- }
5699
+ this.#applyLimits(options, group, groupOptions);
5479
5700
  return;
5480
5701
  }
5481
5702
  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);
@@ -5483,7 +5704,10 @@
5483
5704
  this.#limit = numberOptions.limit.value * densityFactor;
5484
5705
  }
5485
5706
  else {
5486
- this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
5707
+ this.#groupLimits.set(group, {
5708
+ limit: numberOptions.limit.value * densityFactor,
5709
+ mode: toEnumValue(LimitMode, numberOptions.limit.mode, LimitMode.delete),
5710
+ });
5487
5711
  }
5488
5712
  if (particlesCount < particlesNumber) {
5489
5713
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
@@ -5492,6 +5716,55 @@
5492
5716
  this.removeQuantity(particlesCount - particlesNumber, group);
5493
5717
  }
5494
5718
  }
5719
+ #applyLimits(options, group, groupOptions) {
5720
+ if (group === undefined) {
5721
+ this.#limit = options.number.limit.value;
5722
+ const groups = this.#container.actualOptions.particles.groups;
5723
+ for (const existingGroup of this.#groupLimits.keys()) {
5724
+ if (!(existingGroup in groups)) {
5725
+ this.#groupLimits.delete(existingGroup);
5726
+ }
5727
+ }
5728
+ }
5729
+ else {
5730
+ const limitValue = groupOptions?.number?.limit?.value ?? options.number.limit.value;
5731
+ if (limitValue > minLimit) {
5732
+ this.#groupLimits.set(group, {
5733
+ limit: limitValue,
5734
+ mode: toEnumValue(LimitMode, groupOptions?.number?.limit?.mode ?? options.number.limit.mode, LimitMode.delete),
5735
+ });
5736
+ }
5737
+ else {
5738
+ this.#groupLimits.delete(group);
5739
+ }
5740
+ }
5741
+ }
5742
+ #checkAndApplyLimits(group, currentCount) {
5743
+ const globalLimit = this.#limit, globalMode = this.#container.actualOptions.particles.number.limit.mode;
5744
+ if (globalLimit > minLimit && globalMode === LimitMode.wait && currentCount >= globalLimit) {
5745
+ return true;
5746
+ }
5747
+ const groupLimitEntry = group === undefined ? undefined : this.#groupLimits.get(group);
5748
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.wait) {
5749
+ const groupCount = this.filter(t => t.group === group).length;
5750
+ if (groupCount >= groupLimitEntry.limit) {
5751
+ return true;
5752
+ }
5753
+ }
5754
+ if (groupLimitEntry && groupLimitEntry.limit > minLimit && groupLimitEntry.mode === LimitMode.delete) {
5755
+ const groupCountToRemove = this.filter(t => t.group === group).length + countOffset - groupLimitEntry.limit;
5756
+ if (groupCountToRemove > minCount) {
5757
+ this.removeQuantity(groupCountToRemove, group);
5758
+ }
5759
+ }
5760
+ if (globalLimit > minLimit && globalMode === LimitMode.delete) {
5761
+ const globalCountToRemove = this.count + countOffset - globalLimit;
5762
+ if (globalCountToRemove > minCount) {
5763
+ this.#removeAny(globalCountToRemove);
5764
+ }
5765
+ }
5766
+ return false;
5767
+ }
5495
5768
  #createBuckets(zLayers) {
5496
5769
  const bucketCount = Math.max(Math.floor(zLayers), one);
5497
5770
  return Array.from({ length: bucketCount }, () => []);
@@ -5514,6 +5787,70 @@
5514
5787
  }
5515
5788
  return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
5516
5789
  }
5790
+ async #initDrawersAndUpdaters() {
5791
+ const container = this.#container;
5792
+ await container.initDrawersAndUpdaters();
5793
+ for (const drawer of container.effectDrawers.values()) {
5794
+ await drawer.init?.(container);
5795
+ }
5796
+ for (const drawer of container.shapeDrawers.values()) {
5797
+ await drawer.init?.(container);
5798
+ }
5799
+ }
5800
+ async #initPlugins() {
5801
+ await this.#initPluginsArrays();
5802
+ await this.#initDrawersAndUpdaters();
5803
+ let handled = false;
5804
+ const container = this.#container;
5805
+ for (const plugin of container.plugins) {
5806
+ handled = plugin.particlesInitialization?.() ?? handled;
5807
+ if (handled) {
5808
+ break;
5809
+ }
5810
+ }
5811
+ if (handled) {
5812
+ return;
5813
+ }
5814
+ const particlesOptions = container.actualOptions.particles, groups = particlesOptions.groups;
5815
+ for (const group in groups) {
5816
+ const groupOptions = groups[group];
5817
+ if (!groupOptions) {
5818
+ continue;
5819
+ }
5820
+ for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
5821
+ this.addParticle(undefined, groupOptions, group);
5822
+ }
5823
+ }
5824
+ for (let i = this.count; i < particlesOptions.number.value; i++) {
5825
+ this.addParticle();
5826
+ }
5827
+ }
5828
+ async #initPluginsArrays() {
5829
+ const container = this.#container;
5830
+ for (const plugin of container.plugins) {
5831
+ if (plugin.redrawInit) {
5832
+ await plugin.redrawInit();
5833
+ }
5834
+ if (plugin.checkParticlePosition) {
5835
+ this.checkParticlePositionPlugins.push(plugin);
5836
+ }
5837
+ if (plugin.update) {
5838
+ this.#updatePlugins.push(plugin);
5839
+ }
5840
+ if (plugin.particleUpdate) {
5841
+ this.#particleUpdatePlugins.push(plugin);
5842
+ }
5843
+ if (plugin.postUpdate) {
5844
+ this.#postUpdatePlugins.push(plugin);
5845
+ }
5846
+ if (plugin.particleReset) {
5847
+ this.#particleResetPlugins.push(plugin);
5848
+ }
5849
+ if (plugin.postParticleUpdate) {
5850
+ this.#postParticleUpdatePlugins.push(plugin);
5851
+ }
5852
+ }
5853
+ }
5517
5854
  #insertParticleIntoBucket(particle) {
5518
5855
  const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
5519
5856
  if (!bucket) {
@@ -5522,12 +5859,15 @@
5522
5859
  bucket.push(particle);
5523
5860
  this.#particleBuckets.set(particle.id, bucketIndex);
5524
5861
  }
5525
- #removeParticle(index, group, override) {
5862
+ #removeAny(quantity) {
5863
+ this.removeAt(minIndex, quantity, undefined, undefined, false);
5864
+ }
5865
+ #removeParticle(index, group, override, matchGroup = true) {
5526
5866
  const particle = this.#array[index];
5527
5867
  if (!particle) {
5528
5868
  return false;
5529
5869
  }
5530
- if (particle.group !== group) {
5870
+ if (matchGroup && particle.group !== group) {
5531
5871
  return false;
5532
5872
  }
5533
5873
  this.#array.splice(index, deleteCount);
@@ -5614,7 +5954,7 @@
5614
5954
  particlesToDelete.add(particle);
5615
5955
  continue;
5616
5956
  }
5617
- this.grid.insert(particle);
5957
+ this.#grid.insert(particle);
5618
5958
  }
5619
5959
  return particlesToDelete;
5620
5960
  }
@@ -5686,6 +6026,7 @@
5686
6026
  effectDrawers;
5687
6027
  fpsLimit;
5688
6028
  hdr;
6029
+ hdrMode;
5689
6030
  id;
5690
6031
  pageHidden;
5691
6032
  particleCreatedPlugins;
@@ -5693,6 +6034,7 @@
5693
6034
  particlePositionPlugins;
5694
6035
  particleUpdaters;
5695
6036
  particles;
6037
+ peakNits;
5696
6038
  plugins;
5697
6039
  retina;
5698
6040
  shapeDrawers;
@@ -5723,6 +6065,8 @@
5723
6065
  this.id = Symbol(id);
5724
6066
  this.fpsLimit = 120;
5725
6067
  this.hdr = false;
6068
+ this.hdrMode = HdrMode.standard;
6069
+ this.peakNits = 400;
5726
6070
  this.#smooth = false;
5727
6071
  this.#delay = 0;
5728
6072
  this.#duration = 0;
@@ -5786,7 +6130,6 @@
5786
6130
  this.shapeDrawers = new Map();
5787
6131
  this.particleUpdaters = [];
5788
6132
  this.plugins.length = 0;
5789
- this.#pluginManager.clearPlugins(this);
5790
6133
  this.destroyed = true;
5791
6134
  this.#onDestroy(remove);
5792
6135
  this.dispatchEvent(EventType.containerDestroyed);
@@ -5839,31 +6182,15 @@
5839
6182
  await this.initDrawersAndUpdaters();
5840
6183
  this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
5841
6184
  this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
5842
- this.plugins.length = 0;
5843
- this.particleDestroyedPlugins.length = 0;
5844
- this.particleCreatedPlugins.length = 0;
5845
- this.particlePositionPlugins.length = 0;
5846
- for (const [plugin, containerPlugin] of allContainerPlugins) {
5847
- if (plugin.needsPlugin(this.actualOptions)) {
5848
- this.plugins.push(containerPlugin);
5849
- if (containerPlugin.particleCreated) {
5850
- this.particleCreatedPlugins.push(containerPlugin);
5851
- }
5852
- if (containerPlugin.particleDestroyed) {
5853
- this.particleDestroyedPlugins.push(containerPlugin);
5854
- }
5855
- if (containerPlugin.particlePosition) {
5856
- this.particlePositionPlugins.push(containerPlugin);
5857
- }
5858
- }
5859
- }
6185
+ this.#initPlugins(allContainerPlugins);
5860
6186
  this.retina.init();
5861
6187
  this.canvas.init();
5862
6188
  this.updateActualOptions();
5863
6189
  this.canvas.initBackground();
5864
6190
  this.canvas.resize();
5865
- const { delay, duration, fpsLimit, hdr, smooth, zLayers } = this.actualOptions;
5866
- this.hdr = hdr;
6191
+ const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
6192
+ this.hdrMode = toEnumValue(HdrMode, hdrOptions.mode, HdrMode.standard);
6193
+ this.peakNits = hdrOptions.peakNits;
5867
6194
  this.zLayers = zLayers;
5868
6195
  this.#duration = getRangeValue(duration) * millisecondsToSeconds;
5869
6196
  this.#delay = getRangeValue(delay) * millisecondsToSeconds;
@@ -5883,9 +6210,9 @@
5883
6210
  }
5884
6211
  async initDrawersAndUpdaters() {
5885
6212
  const pluginManager = this.#pluginManager;
5886
- this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
5887
- this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
5888
- this.particleUpdaters = await pluginManager.getUpdaters(this, true);
6213
+ this.effectDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.effects);
6214
+ this.shapeDrawers = await getItemMapFromInitializer(this, pluginManager.initializers.shapes);
6215
+ this.particleUpdaters = await getItemsFromInitializer(this, pluginManager.initializers.updaters);
5889
6216
  }
5890
6217
  pause() {
5891
6218
  if (!guardCheck(this)) {
@@ -5996,6 +6323,27 @@
5996
6323
  }
5997
6324
  return refresh;
5998
6325
  }
6326
+ #initPlugins(allContainerPlugins) {
6327
+ this.plugins.length = 0;
6328
+ this.particleDestroyedPlugins.length = 0;
6329
+ this.particleCreatedPlugins.length = 0;
6330
+ this.particlePositionPlugins.length = 0;
6331
+ for (const [plugin, containerPlugin] of allContainerPlugins) {
6332
+ if (!plugin.needsPlugin(this.actualOptions)) {
6333
+ continue;
6334
+ }
6335
+ this.plugins.push(containerPlugin);
6336
+ if (containerPlugin.particleCreated) {
6337
+ this.particleCreatedPlugins.push(containerPlugin);
6338
+ }
6339
+ if (containerPlugin.particleDestroyed) {
6340
+ this.particleDestroyedPlugins.push(containerPlugin);
6341
+ }
6342
+ if (containerPlugin.particlePosition) {
6343
+ this.particlePositionPlugins.push(containerPlugin);
6344
+ }
6345
+ }
6346
+ }
5999
6347
  #nextFrame(timestamp) {
6000
6348
  try {
6001
6349
  if (!this.#smooth &&
@@ -6272,11 +6620,11 @@
6272
6620
  }
6273
6621
  }
6274
6622
  async #init() {
6275
- const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
6276
- if (!availablePathGenerators) {
6623
+ const pluginManager = this.#pluginManager;
6624
+ if (!pluginManager.initializers.pathGenerators) {
6277
6625
  return;
6278
6626
  }
6279
- this.availablePathGenerators = availablePathGenerators;
6627
+ this.availablePathGenerators = await getItemMapFromInitializer(this.#container, pluginManager.initializers.pathGenerators);
6280
6628
  this.pathGenerators = new Map();
6281
6629
  for (const pathGenerator of this.pathGenerators.values()) {
6282
6630
  pathGenerator.init();
@@ -6380,28 +6728,12 @@
6380
6728
  }
6381
6729
  const html = interactivityEl, canvas = container.canvas;
6382
6730
  canvas.setPointerEvents(html === canvas.domElement ? "initial" : "none");
6383
- if (add && !(options.interactivity?.events.onHover.enable || options.interactivity?.events.onClick.enable)) {
6384
- return;
6385
- }
6386
6731
  manageListener(interactivityEl, mouseMoveEvent, handlers.mouseMove, add);
6387
6732
  manageListener(interactivityEl, touchStartEvent, handlers.touchStart, add);
6388
6733
  manageListener(interactivityEl, touchMoveEvent, handlers.touchMove, add);
6389
- if (add) {
6390
- if (options.interactivity?.events.onClick.enable) {
6391
- manageListener(interactivityEl, touchEndEvent, handlers.touchEndClick, add);
6392
- manageListener(interactivityEl, mouseUpEvent, handlers.mouseUp, add);
6393
- manageListener(interactivityEl, mouseDownEvent, handlers.mouseDown, add);
6394
- }
6395
- else {
6396
- manageListener(interactivityEl, touchEndEvent, handlers.touchEnd, add);
6397
- }
6398
- }
6399
- else {
6400
- manageListener(interactivityEl, touchEndEvent, handlers.touchEndClick, add);
6401
- manageListener(interactivityEl, mouseUpEvent, handlers.mouseUp, add);
6402
- manageListener(interactivityEl, mouseDownEvent, handlers.mouseDown, add);
6403
- manageListener(interactivityEl, touchEndEvent, handlers.touchEnd, add);
6404
- }
6734
+ manageListener(interactivityEl, touchEndEvent, options.interactivity?.events.onClick.enable ? handlers.touchEndClick : handlers.touchEnd, add);
6735
+ manageListener(interactivityEl, mouseUpEvent, handlers.mouseUp, add);
6736
+ manageListener(interactivityEl, mouseDownEvent, handlers.mouseDown, add);
6405
6737
  manageListener(interactivityEl, mouseLeaveEvent, handlers.mouseLeave, add);
6406
6738
  manageListener(interactivityEl, touchCancelEvent, handlers.touchCancel, add);
6407
6739
  }
@@ -6427,19 +6759,18 @@
6427
6759
  #mouseTouchClick(e) {
6428
6760
  const container = this.#container, interactionManager = this.#interactionManager, options = container.actualOptions, { mouse } = interactionManager.interactivityData;
6429
6761
  mouse.inside = true;
6430
- let handled = false;
6431
6762
  const mousePosition = mouse.position;
6432
- if (!mousePosition || !options.interactivity?.events.onClick.enable) {
6433
- return;
6434
- }
6435
- for (const plugin of this.#clickPositionPlugins) {
6436
- handled = plugin.clickPositionValid?.(mousePosition) ?? false;
6437
- if (handled) {
6438
- break;
6763
+ if (mousePosition && options.interactivity?.events.onClick.enable) {
6764
+ let handled = false;
6765
+ for (const plugin of this.#clickPositionPlugins) {
6766
+ handled = plugin.clickPositionValid?.(mousePosition) ?? false;
6767
+ if (handled) {
6768
+ break;
6769
+ }
6770
+ }
6771
+ if (!handled) {
6772
+ this.#doMouseTouchClick(e);
6439
6773
  }
6440
- }
6441
- if (!handled) {
6442
- this.#doMouseTouchClick(e);
6443
6774
  }
6444
6775
  mouse.clicking = false;
6445
6776
  }
@@ -6535,6 +6866,9 @@
6535
6866
  this.#touches.set(touch.identifier, performance.now());
6536
6867
  }
6537
6868
  this.#mouseTouchMove(e);
6869
+ const { mouse } = this.#interactionManager.interactivityData;
6870
+ mouse.clicking = true;
6871
+ mouse.downPosition = mouse.position;
6538
6872
  }
6539
6873
  }
6540
6874
 
@@ -6547,18 +6881,18 @@
6547
6881
  }
6548
6882
  class InteractionManager {
6549
6883
  interactivityData;
6884
+ interactors;
6550
6885
  #clickHandlers;
6551
6886
  #container;
6552
6887
  #eventListeners;
6553
6888
  #externalInteractors;
6554
- #interactors;
6555
6889
  #intersectionObserver;
6556
6890
  #particleInteractors;
6557
6891
  #pluginManager;
6558
6892
  constructor(pluginManager, container) {
6559
6893
  this.#container = container;
6560
6894
  this.#pluginManager = pluginManager;
6561
- this.#interactors = [];
6895
+ this.interactors = [];
6562
6896
  this.#externalInteractors = [];
6563
6897
  this.#particleInteractors = [];
6564
6898
  this.#clickHandlers = new Map();
@@ -6677,7 +7011,7 @@
6677
7011
  }
6678
7012
  init() {
6679
7013
  this.#eventListeners.init();
6680
- for (const interactor of this.#interactors) {
7014
+ for (const interactor of this.interactors) {
6681
7015
  switch (interactor.type) {
6682
7016
  case InteractorType.external:
6683
7017
  this.#externalInteractors.push(interactor);
@@ -6690,11 +7024,11 @@
6690
7024
  }
6691
7025
  }
6692
7026
  async initInteractors() {
6693
- const interactors = await this.#pluginManager.getInteractors?.(this.#container, true);
6694
- if (!interactors) {
7027
+ const pluginManager = this.#pluginManager;
7028
+ if (!pluginManager.initializers.interactors) {
6695
7029
  return;
6696
7030
  }
6697
- this.#interactors = interactors;
7031
+ this.interactors = await getItemsFromInitializer(this.#container, pluginManager.initializers.interactors);
6698
7032
  this.#externalInteractors = [];
6699
7033
  this.#particleInteractors = [];
6700
7034
  }
@@ -6739,13 +7073,13 @@
6739
7073
  }
6740
7074
  updateMaxDistance() {
6741
7075
  let maxTotalDistance = 0;
6742
- for (const interactor of this.#interactors) {
7076
+ for (const interactor of this.interactors) {
6743
7077
  if (interactor.maxDistance > maxTotalDistance) {
6744
7078
  maxTotalDistance = interactor.maxDistance;
6745
7079
  }
6746
7080
  }
6747
7081
  const container = this.#container;
6748
- container.particles.grid.setCellSize(maxTotalDistance * container.retina.pixelRatio);
7082
+ container.particles.grid.cellSize = maxTotalDistance * container.retina.pixelRatio;
6749
7083
  }
6750
7084
  #intersectionManager(entries) {
6751
7085
  const container = this.#container;
@@ -6767,59 +7101,59 @@
6767
7101
  }
6768
7102
 
6769
7103
  class InteractivityPluginInstance {
6770
- interactionManager;
6771
7104
  #container;
6772
- #pluginManager;
6773
7105
  constructor(pluginManager, container) {
6774
- this.#container = container;
6775
- this.#pluginManager = pluginManager;
6776
- this.interactionManager = new InteractionManager(pluginManager, container);
6777
- this.#container.addClickHandler = (callback) => {
6778
- this.interactionManager.addClickHandler(callback);
7106
+ container.addClickHandler = (callback) => {
7107
+ container.interactionManager?.addClickHandler(callback);
6779
7108
  };
7109
+ container.interactionManager = new InteractionManager(pluginManager, container);
7110
+ this.#container = container;
6780
7111
  }
6781
7112
  addClickHandler(callback) {
6782
- this.interactionManager.addClickHandler(callback);
7113
+ this.#container.interactionManager?.addClickHandler(callback);
6783
7114
  }
6784
7115
  clearClickHandlers() {
6785
- this.interactionManager.clearClickHandlers();
7116
+ this.#container.interactionManager?.clearClickHandlers();
6786
7117
  }
6787
7118
  destroy() {
6788
7119
  this.clearClickHandlers();
6789
- this.#pluginManager.interactors?.delete(this.#container);
6790
7120
  }
6791
7121
  particleCreated(particle) {
6792
- const interactivityParticle = particle, interactivity = new Interactivity(this.#pluginManager, this.#container);
6793
- interactivity.load(this.#container.actualOptions.interactivity);
7122
+ const interactivityParticle = particle, container = this.#container, interactivity = new Interactivity(container);
7123
+ interactivity.load(container.actualOptions.interactivity);
6794
7124
  interactivity.load(interactivityParticle.options.interactivity);
6795
7125
  interactivityParticle.interactivity = interactivity;
6796
7126
  }
6797
7127
  particleReset(particle) {
6798
- this.interactionManager.reset(particle);
7128
+ this.#container.interactionManager?.reset(particle);
6799
7129
  }
6800
7130
  postParticleUpdate(particle, delta) {
6801
- this.interactionManager.particlesInteract(particle, delta);
7131
+ this.#container.interactionManager?.particlesInteract(particle, delta);
6802
7132
  }
6803
7133
  postUpdate(delta) {
6804
- this.interactionManager.externalInteract(delta);
6805
- this.interactionManager.updateMaxDistance();
7134
+ const container = this.#container;
7135
+ container.interactionManager?.externalInteract(delta);
7136
+ container.interactionManager?.updateMaxDistance();
6806
7137
  }
6807
7138
  async preInit() {
6808
- await this.interactionManager.initInteractors();
6809
- this.interactionManager.init();
7139
+ const container = this.#container;
7140
+ await container.interactionManager?.initInteractors();
7141
+ container.interactionManager?.init();
6810
7142
  }
6811
7143
  async redrawInit() {
6812
- await this.interactionManager.initInteractors();
6813
- this.interactionManager.init();
7144
+ const container = this.#container;
7145
+ await container.interactionManager?.initInteractors();
7146
+ container.interactionManager?.init();
6814
7147
  }
6815
7148
  start() {
6816
- this.interactionManager.addListeners();
6817
- this.interactionManager.startObserving();
7149
+ const container = this.#container;
7150
+ container.interactionManager?.addListeners();
7151
+ container.interactionManager?.startObserving();
6818
7152
  return Promise.resolve();
6819
7153
  }
6820
7154
  stop() {
6821
- this.interactionManager.removeListeners();
6822
- this.interactionManager.stopObserving();
7155
+ this.#container.interactionManager?.removeListeners();
7156
+ this.#container.interactionManager?.stopObserving();
6823
7157
  }
6824
7158
  }
6825
7159
 
@@ -6831,6 +7165,7 @@
6831
7165
  exports.loadFireflyPreset = loadFireflyPreset;
6832
7166
  exports.tsParticles = tsParticles;
6833
7167
 
6834
- }));
7168
+ })(g.__tsParticlesInternals.presets.firefly = g.__tsParticlesInternals.presets.firefly || {});
7169
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : self);
7170
+
6835
7171
  Object.assign(globalThis.window || globalThis, { loadFireflyPreset: (globalThis.__tsParticlesInternals.presets.firefly || {}).loadFireflyPreset });
6836
- delete (globalThis.window || globalThis).tsparticlesInternalExports;