@tsparticles/preset-bubbles 4.2.0 → 4.3.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,5 +1,5 @@
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);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this);
2
- /* Preset v4.2.0 */
2
+ /* Preset v4.3.0 */
3
3
  (function (global, factory) {
4
4
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
5
5
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -371,29 +371,27 @@
371
371
  }
372
372
  }
373
373
  else if (!isObject(destination) || Array.isArray(destination)) {
374
- destination = {};
374
+ destination = Object.create(null);
375
375
  }
376
- const sourceKeys = Object.keys(source), dangerousKeys = new Set(["__proto__", "constructor", "prototype"]), hasNested = sourceKeys.some(k => {
376
+ const sourceKeys = Object.keys(source), hasNested = sourceKeys.some(k => {
377
377
  const v = source[k];
378
378
  return isObject(v) || Array.isArray(v);
379
379
  });
380
380
  if (!hasNested) {
381
381
  const sourceDict = source, destDict = destination;
382
382
  for (const key of sourceKeys) {
383
- if (dangerousKeys.has(key)) {
383
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
384
384
  continue;
385
385
  }
386
- if (key in sourceDict) {
387
- const v = sourceDict[key];
388
- if (v !== undefined) {
389
- destDict[key] = v;
390
- }
386
+ const v = sourceDict[key];
387
+ if (v !== undefined) {
388
+ destDict[key] = v;
391
389
  }
392
390
  }
393
391
  continue;
394
392
  }
395
393
  for (const key of sourceKeys) {
396
- if (dangerousKeys.has(key)) {
394
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
397
395
  continue;
398
396
  }
399
397
  const sourceDict = source, destDict = destination, value = sourceDict[key];
@@ -809,7 +807,7 @@
809
807
  return this.#domArray;
810
808
  }
811
809
  get version() {
812
- return "4.2.0";
810
+ return "4.3.0";
813
811
  }
814
812
  addEventListener(type, listener) {
815
813
  this.#eventDispatcher.addEventListener(type, listener);
@@ -1042,6 +1040,18 @@
1042
1040
  AnimationStatus["decreasing"] = "decreasing";
1043
1041
  })(AnimationStatus || (AnimationStatus = {}));
1044
1042
 
1043
+ var DrawLayer;
1044
+ (function (DrawLayer) {
1045
+ DrawLayer[DrawLayer["BackgroundElement"] = 0] = "BackgroundElement";
1046
+ DrawLayer[DrawLayer["BackgroundDraw"] = 1] = "BackgroundDraw";
1047
+ DrawLayer[DrawLayer["BackgroundMask"] = 2] = "BackgroundMask";
1048
+ DrawLayer[DrawLayer["CanvasSetup"] = 3] = "CanvasSetup";
1049
+ DrawLayer[DrawLayer["PluginContent"] = 4] = "PluginContent";
1050
+ DrawLayer[DrawLayer["Particles"] = 5] = "Particles";
1051
+ DrawLayer[DrawLayer["CanvasCleanup"] = 6] = "CanvasCleanup";
1052
+ DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
1053
+ })(DrawLayer || (DrawLayer = {}));
1054
+
1045
1055
  class OptionLoader {
1046
1056
  load(data) {
1047
1057
  if (isNull(data)) {
@@ -1195,6 +1205,8 @@
1195
1205
 
1196
1206
  class Background extends OptionLoader {
1197
1207
  color;
1208
+ draw;
1209
+ element;
1198
1210
  image = "";
1199
1211
  opacity = 1;
1200
1212
  position = "";
@@ -1209,6 +1221,8 @@
1209
1221
  if (data.color !== undefined) {
1210
1222
  this.color = OptionsColor.create(this.color, data.color);
1211
1223
  }
1224
+ loadProperty(this, "element", data.element);
1225
+ loadProperty(this, "draw", data.draw);
1212
1226
  loadProperty(this, "image", data.image);
1213
1227
  loadProperty(this, "position", data.position);
1214
1228
  loadProperty(this, "repeat", data.repeat);
@@ -1754,7 +1768,7 @@
1754
1768
  }
1755
1769
  }
1756
1770
 
1757
- const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, sdrReferenceWhiteNits = 203;
1771
+ const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, hdrRgbFixedPrecision = 4, hdrHslFixedPrecision = 4, sdrReferenceWhiteNits = 203, hdrAnimationScale = sdrReferenceWhiteNits / maxNits;
1758
1772
  function getCachedStyle(key, generator) {
1759
1773
  let cached = styleCache.get(key);
1760
1774
  if (!cached) {
@@ -1894,6 +1908,17 @@
1894
1908
  : 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));
1895
1909
  return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
1896
1910
  }
1911
+ function hslToRgbFloat(hsl) {
1912
+ const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
1913
+ if (s === sMin) {
1914
+ const grayscaleValue = lNormalized * rgbMax;
1915
+ return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
1916
+ }
1917
+ const temp1 = lNormalized < half
1918
+ ? lNormalized * (sNormalizedOffset + sNormalized)
1919
+ : 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));
1920
+ return { r: red, g: green, b: blue };
1921
+ }
1897
1922
  function hslaToRgba(hsla) {
1898
1923
  const rgbResult = hslToRgb(hsla);
1899
1924
  return {
@@ -1903,8 +1928,9 @@
1903
1928
  r: rgbResult.r,
1904
1929
  };
1905
1930
  }
1906
- function getRandomRgbColor(min) {
1907
- const fixedMin = defaultRgbMin, fixedMax = rgbMax + identity, getRgbInRangeValue = () => Math.floor(getRandomInRange(fixedMin, fixedMax));
1931
+ function getRandomRgbColor(min, hdr) {
1932
+ const fixedMin = defaultRgbMin;
1933
+ const fixedMax = rgbMax + identity, getRgbInRangeValue = () => Math.floor(getRandomInRange(fixedMin, fixedMax));
1908
1934
  return {
1909
1935
  b: getRgbInRangeValue(),
1910
1936
  g: getRgbInRangeValue(),
@@ -1912,7 +1938,7 @@
1912
1938
  };
1913
1939
  }
1914
1940
  function getStyleFromRgb(color, hdr, opacity) {
1915
- const op = opacity ?? defaultOpacity$1, key = `rgb-${color.r.toFixed(rgbFixedPrecision)}-${color.g.toFixed(rgbFixedPrecision)}-${color.b.toFixed(rgbFixedPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
1941
+ 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()}`;
1916
1942
  return getCachedStyle(key, () => (hdr ? getHdrStyleFromRgb(color, opacity) : getSdrStyleFromRgb(color, opacity)));
1917
1943
  }
1918
1944
  function getHdrStyleFromRgb(color, opacity, peakNits = maxNits) {
@@ -1923,9 +1949,9 @@
1923
1949
  return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity$1).toString()})`;
1924
1950
  }
1925
1951
  function getStyleFromHsl(color, hdr, opacity) {
1926
- const op = opacity ?? defaultOpacity$1, key = `hsl-${color.h.toFixed(hslFixedPrecision)}-${color.s.toFixed(hslFixedPrecision)}-${color.l.toFixed(hslFixedPrecision)}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
1952
+ 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()}`;
1927
1953
  return getCachedStyle(key, () => hdr
1928
- ? getStyleFromRgb(hslToRgb(color), true, opacity)
1954
+ ? getStyleFromRgb(hslToRgbFloat(color), true, opacity)
1929
1955
  : `hsla(${color.h.toString()}, ${color.s.toString()}%, ${color.l.toString()}%, ${op.toString()})`);
1930
1956
  }
1931
1957
  function getHslFromAnimation(animation) {
@@ -1988,7 +2014,7 @@
1988
2014
  colorValue.velocity = defaultVelocity;
1989
2015
  }
1990
2016
  }
1991
- function updateColorValue(data, decrease, delta) {
2017
+ function updateColorValue(data, decrease, delta, hdr) {
1992
2018
  const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
1993
2019
  if (!data.enable ||
1994
2020
  ((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
@@ -2001,7 +2027,7 @@
2001
2027
  if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
2002
2028
  return;
2003
2029
  }
2004
- 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;
2030
+ 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;
2005
2031
  if (!decrease || data.status === AnimationStatus.increasing) {
2006
2032
  data.value += velocity;
2007
2033
  if (data.value > max) {
@@ -2028,14 +2054,14 @@
2028
2054
  }
2029
2055
  data.value = clamp(data.value, min, max);
2030
2056
  }
2031
- function updateColor(color, delta) {
2057
+ function updateColor(color, delta, hdr) {
2032
2058
  if (!color) {
2033
2059
  return;
2034
2060
  }
2035
2061
  const { h, s, l } = color;
2036
- updateColorValue(h, false, delta);
2037
- updateColorValue(s, true, delta);
2038
- updateColorValue(l, true, delta);
2062
+ updateColorValue(h, false, delta, hdr);
2063
+ updateColorValue(s, true, delta, hdr);
2064
+ updateColorValue(l, true, delta, hdr);
2039
2065
  }
2040
2066
  function alterHsl(color, type, value) {
2041
2067
  return {
@@ -2085,7 +2111,7 @@
2085
2111
  }
2086
2112
 
2087
2113
  async function loadBlendPlugin(engine) {
2088
- engine.checkVersion("4.2.0");
2114
+ engine.checkVersion("4.3.0");
2089
2115
  await engine.pluginManager.register(e => {
2090
2116
  e.pluginManager.addPlugin(new BlendPlugin());
2091
2117
  });
@@ -2122,7 +2148,7 @@
2122
2148
  }
2123
2149
 
2124
2150
  async function loadCircleShape(engine) {
2125
- engine.checkVersion("4.2.0");
2151
+ engine.checkVersion("4.3.0");
2126
2152
  await engine.pluginManager.register(e => {
2127
2153
  e.pluginManager.addShape(["circle"], () => {
2128
2154
  return Promise.resolve(new CircleDrawer());
@@ -2170,7 +2196,7 @@
2170
2196
  }
2171
2197
 
2172
2198
  async function loadHexColorPlugin(engine) {
2173
- engine.checkVersion("4.2.0");
2199
+ engine.checkVersion("4.3.0");
2174
2200
  await engine.pluginManager.register(e => {
2175
2201
  e.pluginManager.addColorManager("hex", new HexColorManager());
2176
2202
  });
@@ -2223,7 +2249,7 @@
2223
2249
  }
2224
2250
 
2225
2251
  async function loadHslColorPlugin(engine) {
2226
- engine.checkVersion("4.2.0");
2252
+ engine.checkVersion("4.3.0");
2227
2253
  await engine.pluginManager.register(e => {
2228
2254
  e.pluginManager.addColorManager("hsl", new HslColorManager());
2229
2255
  });
@@ -2247,7 +2273,7 @@
2247
2273
  }
2248
2274
 
2249
2275
  async function loadMovePlugin(engine) {
2250
- engine.checkVersion("4.2.0");
2276
+ engine.checkVersion("4.3.0");
2251
2277
  await engine.pluginManager.register(e => {
2252
2278
  const moveEngine = e, movePluginManager = moveEngine.pluginManager;
2253
2279
  movePluginManager.initializers.pathGenerators ??= new Map();
@@ -2457,7 +2483,7 @@
2457
2483
  }
2458
2484
 
2459
2485
  async function loadOpacityUpdater(engine) {
2460
- engine.checkVersion("4.2.0");
2486
+ engine.checkVersion("4.3.0");
2461
2487
  await engine.pluginManager.register(e => {
2462
2488
  e.pluginManager.addParticleUpdater("opacity", container => {
2463
2489
  return Promise.resolve(new OpacityUpdater(container));
@@ -2802,7 +2828,7 @@
2802
2828
  }
2803
2829
 
2804
2830
  async function loadOutModesUpdater(engine) {
2805
- engine.checkVersion("4.2.0");
2831
+ engine.checkVersion("4.3.0");
2806
2832
  await engine.pluginManager.register(e => {
2807
2833
  e.pluginManager.addParticleUpdater("outModes", container => {
2808
2834
  return Promise.resolve(new OutOfCanvasUpdater(container));
@@ -2867,13 +2893,13 @@
2867
2893
  if (!this.isEnabled(particle)) {
2868
2894
  return;
2869
2895
  }
2870
- updateColor(particle.fillColor, delta);
2871
- updateColor(particle.strokeColor, delta);
2896
+ updateColor(particle.fillColor, delta, this.#container.hdr);
2897
+ updateColor(particle.strokeColor, delta, this.#container.hdr);
2872
2898
  }
2873
2899
  }
2874
2900
 
2875
2901
  async function loadPaintUpdater(engine) {
2876
- engine.checkVersion("4.2.0");
2902
+ engine.checkVersion("4.3.0");
2877
2903
  await engine.pluginManager.register(e => {
2878
2904
  e.pluginManager.addParticleUpdater("paint", container => {
2879
2905
  return Promise.resolve(new PaintUpdater(e.pluginManager, container));
@@ -2928,7 +2954,7 @@
2928
2954
  }
2929
2955
 
2930
2956
  async function loadRgbColorPlugin(engine) {
2931
- engine.checkVersion("4.2.0");
2957
+ engine.checkVersion("4.3.0");
2932
2958
  await engine.pluginManager.register(e => {
2933
2959
  e.pluginManager.addColorManager("rgb", new RgbColorManager());
2934
2960
  });
@@ -3013,7 +3039,7 @@
3013
3039
  }
3014
3040
 
3015
3041
  async function loadSizeUpdater(engine) {
3016
- engine.checkVersion("4.2.0");
3042
+ engine.checkVersion("4.3.0");
3017
3043
  await engine.pluginManager.register(e => {
3018
3044
  e.pluginManager.addParticleUpdater("size", container => {
3019
3045
  return Promise.resolve(new SizeUpdater(container));
@@ -3022,7 +3048,7 @@
3022
3048
  }
3023
3049
 
3024
3050
  async function loadBasic(engine) {
3025
- engine.checkVersion("4.2.0");
3051
+ engine.checkVersion("4.3.0");
3026
3052
  await engine.pluginManager.register(async (e) => {
3027
3053
  await Promise.all([
3028
3054
  loadBlendPlugin(e),
@@ -3245,7 +3271,7 @@
3245
3271
  })(EmitterClickMode || (EmitterClickMode = {}));
3246
3272
 
3247
3273
  async function loadEmittersPluginSimple(engine) {
3248
- engine.checkVersion("4.2.0");
3274
+ engine.checkVersion("4.3.0");
3249
3275
  await engine.pluginManager.register(async (e) => {
3250
3276
  const instancesManager = await getEmittersInstancesManager(e);
3251
3277
  await addEmittersShapesManager(e);
@@ -3330,10 +3356,10 @@
3330
3356
  }
3331
3357
  }
3332
3358
  class RenderManager {
3359
+ #backgroundElement;
3360
+ #backgroundWarnings;
3333
3361
  #canvasClearPlugins;
3334
3362
  #canvasManager;
3335
- #canvasPaintPlugins;
3336
- #clearDrawPlugins;
3337
3363
  #colorPlugins;
3338
3364
  #container;
3339
3365
  #context;
@@ -3341,9 +3367,7 @@
3341
3367
  #drawParticlePlugins;
3342
3368
  #drawParticlesCleanupPlugins;
3343
3369
  #drawParticlesSetupPlugins;
3344
- #drawPlugins;
3345
- #drawSettingsCleanupPlugins;
3346
- #drawSettingsSetupPlugins;
3370
+ #layers;
3347
3371
  #pluginManager;
3348
3372
  #postDrawUpdaters;
3349
3373
  #preDrawUpdaters;
@@ -3355,18 +3379,25 @@
3355
3379
  this.#container = container;
3356
3380
  this.#canvasManager = canvasManager;
3357
3381
  this.#context = null;
3382
+ this.#backgroundElement = null;
3383
+ this.#backgroundWarnings = new Set();
3358
3384
  this.#preDrawUpdaters = [];
3359
3385
  this.#postDrawUpdaters = [];
3360
- this.#colorPlugins = [];
3361
3386
  this.#canvasClearPlugins = [];
3362
- this.#canvasPaintPlugins = [];
3363
- this.#clearDrawPlugins = [];
3387
+ this.#colorPlugins = [];
3364
3388
  this.#drawParticlePlugins = [];
3365
3389
  this.#drawParticlesCleanupPlugins = [];
3366
3390
  this.#drawParticlesSetupPlugins = [];
3367
- this.#drawPlugins = [];
3368
- this.#drawSettingsSetupPlugins = [];
3369
- this.#drawSettingsCleanupPlugins = [];
3391
+ this.#layers = {
3392
+ 0: [],
3393
+ 1: [],
3394
+ 2: [],
3395
+ 3: [],
3396
+ 4: [],
3397
+ 5: [],
3398
+ 6: [],
3399
+ 7: [],
3400
+ };
3370
3401
  }
3371
3402
  get settings() {
3372
3403
  return this.#contextSettings;
@@ -3380,32 +3411,38 @@
3380
3411
  });
3381
3412
  }
3382
3413
  clear() {
3383
- let pluginHandled = false;
3384
3414
  for (const plugin of this.#canvasClearPlugins) {
3385
- pluginHandled = plugin.canvasClear?.() ?? false;
3386
- if (pluginHandled) {
3387
- break;
3415
+ if (plugin.canvasClear?.() ?? false) {
3416
+ return;
3388
3417
  }
3389
3418
  }
3390
- if (pluginHandled) {
3391
- return;
3419
+ for (const layer of Object.values(DrawLayer)) {
3420
+ if (typeof layer === "number") {
3421
+ for (const plugin of this.#getLayerPlugins(layer)) {
3422
+ if (plugin.canvasClear?.() ?? false) {
3423
+ return;
3424
+ }
3425
+ }
3426
+ }
3392
3427
  }
3393
3428
  this.canvasClear();
3394
3429
  }
3395
3430
  destroy() {
3396
3431
  this.stop();
3432
+ this.#backgroundElement = null;
3433
+ this.#backgroundWarnings.clear();
3397
3434
  this.#preDrawUpdaters = [];
3398
3435
  this.#postDrawUpdaters = [];
3399
- this.#colorPlugins = [];
3400
3436
  this.#canvasClearPlugins = [];
3401
- this.#canvasPaintPlugins = [];
3402
- this.#clearDrawPlugins = [];
3437
+ this.#colorPlugins = [];
3403
3438
  this.#drawParticlePlugins = [];
3404
3439
  this.#drawParticlesCleanupPlugins = [];
3405
3440
  this.#drawParticlesSetupPlugins = [];
3406
- this.#drawPlugins = [];
3407
- this.#drawSettingsSetupPlugins = [];
3408
- this.#drawSettingsCleanupPlugins = [];
3441
+ for (const layer of Object.values(DrawLayer)) {
3442
+ if (typeof layer === "number") {
3443
+ this.#layers[layer] = [];
3444
+ }
3445
+ }
3409
3446
  }
3410
3447
  draw(cb) {
3411
3448
  const ctx = this.#context;
@@ -3462,21 +3499,40 @@
3462
3499
  });
3463
3500
  }
3464
3501
  drawParticles(delta) {
3465
- const { particles } = this.#container;
3502
+ const { particles, actualOptions } = this.#container;
3466
3503
  this.clear();
3467
3504
  particles.update(delta);
3468
3505
  this.draw(ctx => {
3469
- for (const plugin of this.#drawSettingsSetupPlugins) {
3506
+ const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
3507
+ if (this.#backgroundElement) {
3508
+ try {
3509
+ ctx.drawImage(this.#backgroundElement, originPoint.x, originPoint.y, width, height);
3510
+ }
3511
+ catch {
3512
+ this.#warnOnce("background-element-draw-error", "Error drawing background element onto canvas");
3513
+ }
3514
+ }
3515
+ const background = actualOptions.background;
3516
+ if (background.draw) {
3517
+ try {
3518
+ background.draw(ctx, delta);
3519
+ }
3520
+ catch {
3521
+ this.#warnOnce("background-draw-error", "Error in background.draw callback");
3522
+ }
3523
+ }
3524
+ for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3525
+ plugin.canvasPaint?.();
3526
+ }
3527
+ for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasSetup)) {
3470
3528
  plugin.drawSettingsSetup?.(ctx, delta);
3471
3529
  }
3472
- for (const plugin of this.#drawPlugins) {
3530
+ for (const plugin of this.#getLayerPlugins(DrawLayer.PluginContent)) {
3473
3531
  plugin.draw?.(ctx, delta);
3474
3532
  }
3475
3533
  particles.drawParticles(delta);
3476
- for (const plugin of this.#clearDrawPlugins) {
3534
+ for (const plugin of this.#getLayerPlugins(DrawLayer.CanvasCleanup)) {
3477
3535
  plugin.clearDraw?.(ctx, delta);
3478
- }
3479
- for (const plugin of this.#drawSettingsCleanupPlugins) {
3480
3536
  plugin.drawSettingsCleanup?.(ctx, delta);
3481
3537
  }
3482
3538
  });
@@ -3484,29 +3540,24 @@
3484
3540
  init() {
3485
3541
  this.initUpdaters();
3486
3542
  this.initPlugins();
3543
+ this.#resolveBackgroundElement();
3487
3544
  this.paint();
3488
3545
  }
3489
3546
  initPlugins() {
3490
- this.#colorPlugins = [];
3491
3547
  this.#canvasClearPlugins = [];
3492
- this.#canvasPaintPlugins = [];
3493
- this.#clearDrawPlugins = [];
3548
+ this.#colorPlugins = [];
3494
3549
  this.#drawParticlePlugins = [];
3495
3550
  this.#drawParticlesSetupPlugins = [];
3496
3551
  this.#drawParticlesCleanupPlugins = [];
3497
- this.#drawPlugins = [];
3498
- this.#drawSettingsSetupPlugins = [];
3499
- this.#drawSettingsCleanupPlugins = [];
3552
+ for (const layer of Object.values(DrawLayer)) {
3553
+ if (typeof layer === "number") {
3554
+ this.#layers[layer] = [];
3555
+ }
3556
+ }
3500
3557
  for (const plugin of this.#container.plugins) {
3501
3558
  if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
3502
3559
  this.#colorPlugins.push(plugin);
3503
3560
  }
3504
- if (plugin.canvasClear) {
3505
- this.#canvasClearPlugins.push(plugin);
3506
- }
3507
- if (plugin.canvasPaint) {
3508
- this.#canvasPaintPlugins.push(plugin);
3509
- }
3510
3561
  if (plugin.drawParticle) {
3511
3562
  this.#drawParticlePlugins.push(plugin);
3512
3563
  }
@@ -3516,17 +3567,20 @@
3516
3567
  if (plugin.drawParticleCleanup) {
3517
3568
  this.#drawParticlesCleanupPlugins.push(plugin);
3518
3569
  }
3519
- if (plugin.draw) {
3520
- this.#drawPlugins.push(plugin);
3570
+ if (plugin.canvasClear) {
3571
+ this.#canvasClearPlugins.push(plugin);
3572
+ }
3573
+ if (plugin.canvasPaint) {
3574
+ this.#getLayerPlugins(DrawLayer.BackgroundMask).push(plugin);
3521
3575
  }
3522
3576
  if (plugin.drawSettingsSetup) {
3523
- this.#drawSettingsSetupPlugins.push(plugin);
3577
+ this.#getLayerPlugins(DrawLayer.CanvasSetup).push(plugin);
3524
3578
  }
3525
- if (plugin.drawSettingsCleanup) {
3526
- this.#drawSettingsCleanupPlugins.push(plugin);
3579
+ if (plugin.draw) {
3580
+ this.#getLayerPlugins(DrawLayer.PluginContent).push(plugin);
3527
3581
  }
3528
- if (plugin.clearDraw) {
3529
- this.#clearDrawPlugins.push(plugin);
3582
+ if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
3583
+ this.#getLayerPlugins(DrawLayer.CanvasCleanup).push(plugin);
3530
3584
  }
3531
3585
  }
3532
3586
  }
@@ -3544,7 +3598,7 @@
3544
3598
  }
3545
3599
  paint() {
3546
3600
  let handled = false;
3547
- for (const plugin of this.#canvasPaintPlugins) {
3601
+ for (const plugin of this.#getLayerPlugins(DrawLayer.BackgroundMask)) {
3548
3602
  handled = plugin.canvasPaint?.() ?? false;
3549
3603
  if (handled) {
3550
3604
  break;
@@ -3717,6 +3771,9 @@
3717
3771
  }
3718
3772
  drawer.beforeDraw(data);
3719
3773
  }
3774
+ #getLayerPlugins(layer) {
3775
+ return this.#layers[layer];
3776
+ }
3720
3777
  #getPluginParticleColors(particle) {
3721
3778
  let fColor, sColor;
3722
3779
  for (const plugin of this.#colorPlugins) {
@@ -3734,6 +3791,39 @@
3734
3791
  this.#reusablePluginColors[sColorIndex] = sColor;
3735
3792
  return this.#reusablePluginColors;
3736
3793
  }
3794
+ #resolveBackgroundElement() {
3795
+ const background = this.#container.actualOptions.background;
3796
+ this.#backgroundElement = null;
3797
+ if (!background.element) {
3798
+ return;
3799
+ }
3800
+ if (typeof background.element === "string") {
3801
+ if (typeof document !== "undefined") {
3802
+ const node = document.querySelector(background.element);
3803
+ if (node instanceof HTMLCanvasElement || node instanceof HTMLVideoElement || node instanceof HTMLImageElement) {
3804
+ this.#backgroundElement = node;
3805
+ }
3806
+ else if (node) {
3807
+ this.#warnOnce("background-element-not-supported", `Background element "${background.element}" is not a supported drawable element (canvas, video, or img)`);
3808
+ }
3809
+ else {
3810
+ this.#warnOnce("background-element-not-found", `Background element selector "${background.element}" not found`);
3811
+ }
3812
+ }
3813
+ }
3814
+ else if (background.element instanceof HTMLCanvasElement ||
3815
+ background.element instanceof OffscreenCanvas ||
3816
+ background.element instanceof HTMLVideoElement ||
3817
+ background.element instanceof HTMLImageElement) {
3818
+ this.#backgroundElement = background.element;
3819
+ }
3820
+ }
3821
+ #warnOnce(key, message) {
3822
+ if (!this.#backgroundWarnings.has(key)) {
3823
+ this.#backgroundWarnings.add(key);
3824
+ getLogger().warning(message);
3825
+ }
3826
+ }
3737
3827
  }
3738
3828
 
3739
3829
  const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
@@ -4310,7 +4400,6 @@
4310
4400
  }
4311
4401
  class Particle {
4312
4402
  backColor;
4313
- bubble;
4314
4403
  destroyed;
4315
4404
  direction;
4316
4405
  effect;
@@ -4344,7 +4433,6 @@
4344
4433
  shapeData;
4345
4434
  sides;
4346
4435
  size;
4347
- slow;
4348
4436
  spawning;
4349
4437
  strokeColor;
4350
4438
  strokeOpacity;
@@ -4366,18 +4454,25 @@
4366
4454
  d: 1,
4367
4455
  };
4368
4456
  #container;
4457
+ #modifiers = [];
4369
4458
  #pluginManager;
4370
4459
  constructor(pluginManager, container) {
4371
4460
  this.#pluginManager = pluginManager;
4372
4461
  this.#container = container;
4373
4462
  }
4463
+ addModifier(modifier) {
4464
+ this.#modifiers.push(modifier);
4465
+ this.#modifiers.sort((a, b) => a.priority - b.priority);
4466
+ }
4467
+ clearModifiers() {
4468
+ this.#modifiers.length = 0;
4469
+ }
4374
4470
  destroy(override) {
4375
4471
  if (this.unbreakable || this.destroyed) {
4376
4472
  return;
4377
4473
  }
4378
4474
  this.destroyed = true;
4379
- this.bubble.inRange = false;
4380
- this.slow.inRange = false;
4475
+ this.clearModifiers();
4381
4476
  const container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
4382
4477
  shapeDrawer?.particleDestroy?.(this);
4383
4478
  for (const plugin of container.particleDestroyedPlugins) {
@@ -4399,13 +4494,16 @@
4399
4494
  return this.rotation + (this.pathRotation ? this.velocity.angle : defaultAngle);
4400
4495
  }
4401
4496
  getFillColor() {
4402
- return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.fillColor));
4497
+ return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.fillColor), m => m.fillColor));
4403
4498
  }
4404
4499
  getMass() {
4405
4500
  return this.getRadius() ** squareExp * Math.PI * half;
4406
4501
  }
4502
+ getModifier(id) {
4503
+ return this.#modifiers.find(m => m.id === id);
4504
+ }
4407
4505
  getOpacity() {
4408
- const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, opacity = this.bubble.opacity ?? getRangeValue(this.opacity?.value ?? defaultOpacity$1), fillOpacity = this.fillOpacity ?? defaultOpacity$1, strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
4506
+ 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;
4409
4507
  this.#cachedOpacityData.fillOpacity = opacity * fillOpacity * zOpacityFactor;
4410
4508
  this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
4411
4509
  this.#cachedOpacityData.strokeOpacity = opacity * strokeOpacity * zOpacityFactor;
@@ -4418,7 +4516,7 @@
4418
4516
  return this.#cachedPosition;
4419
4517
  }
4420
4518
  getRadius() {
4421
- return this.bubble.radius ?? this.size.value;
4519
+ return this.#applyModifiers(this.size.value, m => m.radius);
4422
4520
  }
4423
4521
  getRotateData() {
4424
4522
  const angle = this.getAngle();
@@ -4427,7 +4525,7 @@
4427
4525
  return this.#cachedRotateData;
4428
4526
  }
4429
4527
  getStrokeColor() {
4430
- return this.#getRollColor(this.bubble.color ?? getHslFromAnimation(this.strokeColor));
4528
+ return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.strokeColor), m => m.strokeColor));
4431
4529
  }
4432
4530
  getTransformData(externalTransform) {
4433
4531
  const rotateData = this.getRotateData(), rotating = this.isRotating;
@@ -4447,13 +4545,6 @@
4447
4545
  this.options = resolveParticleOptions(this, container, this.#pluginManager, overrideOptions);
4448
4546
  container.retina.initParticle(this);
4449
4547
  runUpdaterPreInit(container.particleUpdaters, this);
4450
- this.bubble = {
4451
- inRange: false,
4452
- };
4453
- this.slow = {
4454
- inRange: false,
4455
- factor: 1,
4456
- };
4457
4548
  this.#initPosition(position);
4458
4549
  this.initialVelocity = this.#calculateVelocity();
4459
4550
  this.velocity = this.initialVelocity.copy();
@@ -4493,11 +4584,29 @@
4493
4584
  isVisible() {
4494
4585
  return !this.destroyed && !this.spawning && this.isInsideCanvas();
4495
4586
  }
4587
+ removeModifier(id) {
4588
+ const idx = this.#modifiers.findIndex(m => m.id === id);
4589
+ if (idx >= defaultAngle) {
4590
+ this.#modifiers.splice(idx, identity);
4591
+ }
4592
+ }
4496
4593
  reset() {
4497
4594
  for (const updater of this.#container.particleUpdaters) {
4498
4595
  updater.reset?.(this);
4499
4596
  }
4500
4597
  }
4598
+ #applyModifiers(base, getter) {
4599
+ let value = base;
4600
+ for (const mod of this.#modifiers) {
4601
+ if (mod.enabled) {
4602
+ const override = getter(mod);
4603
+ if (override !== undefined) {
4604
+ value = override;
4605
+ }
4606
+ }
4607
+ }
4608
+ return value;
4609
+ }
4501
4610
  #calcPosition(position, zIndex) {
4502
4611
  let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
4503
4612
  const container = this.#container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size;
@@ -5599,6 +5708,7 @@
5599
5708
  });
5600
5709
 
5601
5710
  class BlendPluginInstance {
5711
+ layer = DrawLayer.CanvasSetup;
5602
5712
  #container;
5603
5713
  #defaultCompositeValue;
5604
5714
  constructor(container) {
@@ -5744,7 +5854,8 @@
5744
5854
  particle.lastPathTime -= pathDelay;
5745
5855
  }
5746
5856
  function getProximitySpeedFactor(particle) {
5747
- return particle.slow.inRange ? particle.slow.factor : identity;
5857
+ const mod = particle.getModifier("slow");
5858
+ return mod?.enabled ? (mod.speedFactor ?? identity) : identity;
5748
5859
  }
5749
5860
  function initSpin(container, particle) {
5750
5861
  const options = particle.options, spinOptions = options.move.spin;