@tsparticles/engine 3.0.0-beta.3 → 3.0.0-beta.5

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.
Files changed (182) hide show
  1. package/browser/Core/Canvas.js +2 -2
  2. package/browser/Core/Container.js +36 -19
  3. package/browser/Core/Engine.js +18 -5
  4. package/browser/Core/Particle.js +77 -41
  5. package/browser/Core/Particles.js +51 -26
  6. package/browser/Core/Retina.js +0 -2
  7. package/browser/Core/Utils/QuadTree.js +1 -1
  8. package/browser/Options/Classes/BackgroundMask/BackgroundMask.js +1 -2
  9. package/browser/Options/Classes/ColorAnimation.js +4 -24
  10. package/browser/Options/Classes/Interactivity/Events/Events.js +2 -7
  11. package/browser/Options/Classes/Particles/Bounce/ParticlesBounceFactor.js +0 -1
  12. package/browser/Options/Classes/Particles/Effect/Effect.js +32 -0
  13. package/browser/Options/Classes/Particles/Number/ParticlesNumber.js +3 -5
  14. package/browser/Options/Classes/Particles/Number/ParticlesNumberLimit.js +17 -0
  15. package/browser/Options/Classes/Particles/Opacity/Opacity.js +2 -3
  16. package/browser/Options/Classes/Particles/ParticlesOptions.js +12 -6
  17. package/browser/Options/Classes/Particles/Size/Size.js +2 -3
  18. package/browser/Options/Classes/ValueWithRandom.js +1 -10
  19. package/browser/Utils/CanvasUtils.js +23 -18
  20. package/browser/Utils/ColorUtils.js +24 -38
  21. package/browser/Utils/NumberUtils.js +7 -16
  22. package/browser/Utils/Utils.js +7 -7
  23. package/browser/export-types.js +4 -0
  24. package/browser/exports.js +2 -3
  25. package/cjs/Core/Canvas.js +2 -2
  26. package/cjs/Core/Container.js +36 -19
  27. package/cjs/Core/Engine.js +18 -5
  28. package/cjs/Core/Particle.js +76 -40
  29. package/cjs/Core/Particles.js +51 -26
  30. package/cjs/Core/Retina.js +0 -2
  31. package/cjs/Core/Utils/QuadTree.js +1 -1
  32. package/cjs/Options/Classes/BackgroundMask/BackgroundMask.js +1 -2
  33. package/cjs/Options/Classes/ColorAnimation.js +4 -24
  34. package/cjs/Options/Classes/Interactivity/Events/Events.js +2 -7
  35. package/cjs/Options/Classes/Particles/Bounce/ParticlesBounceFactor.js +0 -1
  36. package/cjs/Options/Classes/Particles/Effect/Effect.js +36 -0
  37. package/cjs/Options/Classes/Particles/Number/ParticlesNumber.js +3 -5
  38. package/cjs/Options/Classes/Particles/Number/ParticlesNumberLimit.js +21 -0
  39. package/cjs/Options/Classes/Particles/Opacity/Opacity.js +1 -2
  40. package/cjs/Options/Classes/Particles/ParticlesOptions.js +12 -6
  41. package/cjs/Options/Classes/Particles/Size/Size.js +2 -3
  42. package/cjs/Options/Classes/ValueWithRandom.js +1 -10
  43. package/cjs/Options/Interfaces/Particles/Number/IParticlesNumberLimit.js +2 -0
  44. package/cjs/Utils/CanvasUtils.js +26 -21
  45. package/cjs/Utils/ColorUtils.js +24 -38
  46. package/cjs/Utils/NumberUtils.js +8 -18
  47. package/cjs/Utils/Utils.js +6 -6
  48. package/cjs/export-types.js +4 -0
  49. package/cjs/exports.js +2 -3
  50. package/esm/Core/Canvas.js +2 -2
  51. package/esm/Core/Container.js +36 -19
  52. package/esm/Core/Engine.js +18 -5
  53. package/esm/Core/Particle.js +77 -41
  54. package/esm/Core/Particles.js +51 -26
  55. package/esm/Core/Retina.js +0 -2
  56. package/esm/Core/Utils/QuadTree.js +1 -1
  57. package/esm/Options/Classes/BackgroundMask/BackgroundMask.js +1 -2
  58. package/esm/Options/Classes/ColorAnimation.js +4 -24
  59. package/esm/Options/Classes/Interactivity/Events/Events.js +2 -7
  60. package/esm/Options/Classes/Particles/Bounce/ParticlesBounceFactor.js +0 -1
  61. package/esm/Options/Classes/Particles/Effect/Effect.js +32 -0
  62. package/esm/Options/Classes/Particles/Number/ParticlesNumber.js +3 -5
  63. package/esm/Options/Classes/Particles/Number/ParticlesNumberLimit.js +17 -0
  64. package/esm/Options/Classes/Particles/Opacity/Opacity.js +2 -3
  65. package/esm/Options/Classes/Particles/ParticlesOptions.js +12 -6
  66. package/esm/Options/Classes/Particles/Size/Size.js +2 -3
  67. package/esm/Options/Classes/ValueWithRandom.js +1 -10
  68. package/esm/Options/Interfaces/Particles/Effect/IEffect.js +1 -0
  69. package/esm/Options/Interfaces/Particles/Number/IParticlesNumberLimit.js +1 -0
  70. package/esm/Utils/CanvasUtils.js +23 -18
  71. package/esm/Utils/ColorUtils.js +24 -38
  72. package/esm/Utils/NumberUtils.js +7 -16
  73. package/esm/Utils/Utils.js +7 -7
  74. package/esm/export-types.js +4 -0
  75. package/esm/exports.js +2 -3
  76. package/package.json +1 -1
  77. package/report.html +4 -22
  78. package/tsparticles.engine.js +400 -295
  79. package/tsparticles.engine.min.js +1 -1
  80. package/tsparticles.engine.min.js.LICENSE.txt +1 -1
  81. package/types/Core/Canvas.d.ts +0 -1
  82. package/types/Core/Container.d.ts +8 -7
  83. package/types/Core/Engine.d.ts +7 -2
  84. package/types/Core/Interfaces/IContainerPlugin.d.ts +2 -3
  85. package/types/Core/Interfaces/IEffectDrawer.d.ts +10 -0
  86. package/types/Core/Interfaces/IExternalInteractor.d.ts +3 -4
  87. package/types/Core/Interfaces/IInteractor.d.ts +3 -3
  88. package/types/Core/Interfaces/IParticleRetinaProps.d.ts +0 -1
  89. package/types/Core/Interfaces/IParticlesInteractor.d.ts +3 -3
  90. package/types/Core/Interfaces/IShapeDrawData.d.ts +10 -0
  91. package/types/Core/Interfaces/IShapeDrawer.d.ts +3 -10
  92. package/types/Core/Particle.d.ts +6 -2
  93. package/types/Core/Particles.d.ts +10 -7
  94. package/types/Core/Retina.d.ts +0 -1
  95. package/types/Core/Utils/ExternalInteractorBase.d.ts +4 -4
  96. package/types/Core/Utils/InteractionManager.d.ts +1 -2
  97. package/types/Core/Utils/ParticlesInteractorBase.d.ts +5 -5
  98. package/types/Enums/Modes/LimitMode.d.ts +4 -0
  99. package/types/Enums/Types/EasingType.d.ts +3 -0
  100. package/types/Options/Classes/BackgroundMask/BackgroundMask.d.ts +1 -1
  101. package/types/Options/Classes/ColorAnimation.d.ts +2 -7
  102. package/types/Options/Classes/Interactivity/Events/ClickEvent.d.ts +1 -2
  103. package/types/Options/Classes/Interactivity/Events/DivEvent.d.ts +1 -2
  104. package/types/Options/Classes/Interactivity/Events/Events.d.ts +3 -3
  105. package/types/Options/Classes/Interactivity/Events/HoverEvent.d.ts +2 -3
  106. package/types/Options/Classes/Interactivity/Interactivity.d.ts +2 -2
  107. package/types/Options/Classes/Options.d.ts +6 -6
  108. package/types/Options/Classes/Particles/Bounce/ParticlesBounce.d.ts +2 -2
  109. package/types/Options/Classes/Particles/Collisions/Collisions.d.ts +3 -3
  110. package/types/Options/Classes/Particles/Effect/Effect.d.ts +13 -0
  111. package/types/Options/Classes/Particles/Move/Move.d.ts +8 -8
  112. package/types/Options/Classes/Particles/Move/MoveTrail.d.ts +1 -1
  113. package/types/Options/Classes/Particles/Number/ParticlesNumber.d.ts +3 -2
  114. package/types/Options/Classes/Particles/Number/ParticlesNumberLimit.d.ts +10 -0
  115. package/types/Options/Classes/Particles/Opacity/Opacity.d.ts +3 -3
  116. package/types/Options/Classes/Particles/ParticlesOptions.d.ts +12 -10
  117. package/types/Options/Classes/Particles/Size/Size.d.ts +3 -3
  118. package/types/Options/Classes/Theme/Theme.d.ts +1 -1
  119. package/types/Options/Classes/ValueWithRandom.d.ts +2 -4
  120. package/types/Options/Interfaces/IValueWithRandom.d.ts +0 -2
  121. package/types/Options/Interfaces/Interactivity/Events/IClickEvent.d.ts +1 -2
  122. package/types/Options/Interfaces/Interactivity/Events/IDivEvent.d.ts +1 -2
  123. package/types/Options/Interfaces/Interactivity/Events/IEvents.d.ts +1 -1
  124. package/types/Options/Interfaces/Interactivity/Events/IHoverEvent.d.ts +1 -2
  125. package/types/Options/Interfaces/Particles/Effect/IEffect.d.ts +8 -0
  126. package/types/Options/Interfaces/Particles/IParticlesOptions.d.ts +2 -0
  127. package/types/Options/Interfaces/Particles/Number/IParticlesNumber.d.ts +2 -1
  128. package/types/Options/Interfaces/Particles/Number/IParticlesNumberLimit.d.ts +5 -0
  129. package/types/Utils/CanvasUtils.d.ts +2 -2
  130. package/types/Utils/NumberUtils.d.ts +0 -2
  131. package/types/Utils/Utils.d.ts +2 -3
  132. package/types/export-types.d.ts +5 -0
  133. package/types/exports.d.ts +2 -3
  134. package/umd/Core/Canvas.js +2 -2
  135. package/umd/Core/Container.js +36 -19
  136. package/umd/Core/Engine.js +18 -5
  137. package/umd/Core/Particle.js +76 -40
  138. package/umd/Core/Particles.js +51 -26
  139. package/umd/Core/Retina.js +0 -2
  140. package/umd/Core/Utils/QuadTree.js +1 -1
  141. package/umd/Options/Classes/BackgroundMask/BackgroundMask.js +1 -2
  142. package/umd/Options/Classes/ColorAnimation.js +5 -25
  143. package/umd/Options/Classes/Interactivity/Events/Events.js +3 -8
  144. package/umd/Options/Classes/Particles/Bounce/ParticlesBounceFactor.js +0 -1
  145. package/umd/Options/Classes/Particles/Effect/Effect.js +46 -0
  146. package/umd/Options/Classes/Particles/Number/ParticlesNumber.js +4 -6
  147. package/umd/Options/Classes/{Random.js → Particles/Number/ParticlesNumberLimit.js} +9 -9
  148. package/umd/Options/Classes/Particles/Opacity/Opacity.js +1 -2
  149. package/umd/Options/Classes/Particles/ParticlesOptions.js +13 -7
  150. package/umd/Options/Classes/Particles/Size/Size.js +3 -4
  151. package/umd/Options/Classes/ValueWithRandom.js +2 -11
  152. package/umd/Options/Interfaces/Particles/Number/IParticlesNumberLimit.js +12 -0
  153. package/umd/Utils/CanvasUtils.js +26 -21
  154. package/umd/Utils/ColorUtils.js +24 -38
  155. package/umd/Utils/NumberUtils.js +9 -19
  156. package/umd/Utils/Utils.js +6 -6
  157. package/umd/export-types.js +5 -1
  158. package/umd/exports.js +3 -4
  159. package/browser/Options/Classes/Random.js +0 -17
  160. package/cjs/Options/Classes/Random.js +0 -21
  161. package/esm/Options/Classes/Random.js +0 -17
  162. package/types/Enums/Modes/ClickMode.d.ts +0 -9
  163. package/types/Enums/Modes/DivMode.d.ts +0 -5
  164. package/types/Enums/Modes/HoverMode.d.ts +0 -11
  165. package/types/Options/Classes/Random.d.ts +0 -9
  166. package/types/Options/Interfaces/IRandom.d.ts +0 -4
  167. /package/browser/{Enums/Modes/ClickMode.js → Core/Interfaces/IEffectDrawer.js} +0 -0
  168. /package/browser/{Enums/Modes/DivMode.js → Core/Interfaces/IShapeDrawData.js} +0 -0
  169. /package/browser/Enums/Modes/{HoverMode.js → LimitMode.js} +0 -0
  170. /package/browser/Options/Interfaces/{IRandom.js → Particles/Effect/IEffect.js} +0 -0
  171. /package/{esm/Enums/Modes/ClickMode.js → browser/Options/Interfaces/Particles/Number/IParticlesNumberLimit.js} +0 -0
  172. /package/cjs/{Enums/Modes/ClickMode.js → Core/Interfaces/IEffectDrawer.js} +0 -0
  173. /package/cjs/{Enums/Modes/DivMode.js → Core/Interfaces/IShapeDrawData.js} +0 -0
  174. /package/cjs/Enums/Modes/{HoverMode.js → LimitMode.js} +0 -0
  175. /package/cjs/Options/Interfaces/{IRandom.js → Particles/Effect/IEffect.js} +0 -0
  176. /package/esm/{Enums/Modes/DivMode.js → Core/Interfaces/IEffectDrawer.js} +0 -0
  177. /package/esm/{Enums/Modes/HoverMode.js → Core/Interfaces/IShapeDrawData.js} +0 -0
  178. /package/esm/{Options/Interfaces/IRandom.js → Enums/Modes/LimitMode.js} +0 -0
  179. /package/umd/{Enums/Modes/ClickMode.js → Core/Interfaces/IEffectDrawer.js} +0 -0
  180. /package/umd/{Enums/Modes/DivMode.js → Core/Interfaces/IShapeDrawData.js} +0 -0
  181. /package/umd/Enums/Modes/{HoverMode.js → LimitMode.js} +0 -0
  182. /package/umd/Options/Interfaces/{IRandom.js → Particles/Effect/IEffect.js} +0 -0
@@ -402,10 +402,10 @@ export class Canvas {
402
402
  this.element.width = size.width = this.element.offsetWidth * pxRatio;
403
403
  this.element.height = size.height = this.element.offsetHeight * pxRatio;
404
404
  if (this.container.started) {
405
- this.resizeFactor = {
405
+ container.particles.setResizeFactor({
406
406
  width: size.width / oldSize.width,
407
407
  height: size.height / oldSize.height,
408
- };
408
+ });
409
409
  }
410
410
  return true;
411
411
  }
@@ -36,16 +36,16 @@ export class Container {
36
36
  };
37
37
  this._nextFrame = async (timestamp) => {
38
38
  try {
39
- if (!this.smooth &&
40
- this.lastFrameTime !== undefined &&
41
- timestamp < this.lastFrameTime + 1000 / this.fpsLimit) {
39
+ if (!this._smooth &&
40
+ this._lastFrameTime !== undefined &&
41
+ timestamp < this._lastFrameTime + 1000 / this.fpsLimit) {
42
42
  this.draw(false);
43
43
  return;
44
44
  }
45
- this.lastFrameTime ??= timestamp;
46
- const delta = initDelta(timestamp - this.lastFrameTime, this.fpsLimit, this.smooth);
45
+ this._lastFrameTime ??= timestamp;
46
+ const delta = initDelta(timestamp - this._lastFrameTime, this.fpsLimit, this._smooth);
47
47
  this.addLifeTime(delta.value);
48
- this.lastFrameTime = timestamp;
48
+ this._lastFrameTime = timestamp;
49
49
  if (delta.value > 1000) {
50
50
  this.draw(false);
51
51
  return;
@@ -66,7 +66,7 @@ export class Container {
66
66
  this._engine = engine;
67
67
  this.id = Symbol(id);
68
68
  this.fpsLimit = 120;
69
- this.smooth = false;
69
+ this._smooth = false;
70
70
  this._delay = 0;
71
71
  this._duration = 0;
72
72
  this._lifeTime = 0;
@@ -74,7 +74,7 @@ export class Container {
74
74
  this.started = false;
75
75
  this.destroyed = false;
76
76
  this._paused = true;
77
- this.lastFrameTime = 0;
77
+ this._lastFrameTime = 0;
78
78
  this.zLayers = 100;
79
79
  this.pageHidden = false;
80
80
  this._sourceOptions = sourceOptions;
@@ -90,7 +90,8 @@ export class Container {
90
90
  },
91
91
  };
92
92
  this.plugins = new Map();
93
- this.drawers = new Map();
93
+ this.effectDrawers = new Map();
94
+ this.shapeDrawers = new Map();
94
95
  this._options = loadContainerOptions(this._engine, this);
95
96
  this.actualOptions = loadContainerOptions(this._engine, this);
96
97
  this._eventListeners = new EventListeners(this);
@@ -200,11 +201,17 @@ export class Container {
200
201
  this.stop();
201
202
  this.particles.destroy();
202
203
  this.canvas.destroy();
203
- for (const [, drawer] of this.drawers) {
204
- drawer.destroy && drawer.destroy(this);
204
+ for (const [, effectDrawer] of this.effectDrawers) {
205
+ effectDrawer.destroy && effectDrawer.destroy(this);
205
206
  }
206
- for (const key of this.drawers.keys()) {
207
- this.drawers.delete(key);
207
+ for (const [, shapeDrawer] of this.shapeDrawers) {
208
+ shapeDrawer.destroy && shapeDrawer.destroy(this);
209
+ }
210
+ for (const key of this.effectDrawers.keys()) {
211
+ this.effectDrawers.delete(key);
212
+ }
213
+ for (const key of this.shapeDrawers.keys()) {
214
+ this.shapeDrawers.delete(key);
208
215
  }
209
216
  this._engine.clearPlugins(this);
210
217
  this.destroyed = true;
@@ -221,7 +228,7 @@ export class Container {
221
228
  let refreshTime = force;
222
229
  this._drawAnimationFrame = requestAnimationFrame(async (timestamp) => {
223
230
  if (refreshTime) {
224
- this.lastFrameTime = undefined;
231
+ this._lastFrameTime = undefined;
225
232
  refreshTime = false;
226
233
  }
227
234
  await this._nextFrame(timestamp);
@@ -256,11 +263,18 @@ export class Container {
256
263
  if (!guardCheck(this)) {
257
264
  return;
258
265
  }
266
+ const effects = this._engine.getSupportedEffects();
267
+ for (const type of effects) {
268
+ const drawer = this._engine.getEffectDrawer(type);
269
+ if (drawer) {
270
+ this.effectDrawers.set(type, drawer);
271
+ }
272
+ }
259
273
  const shapes = this._engine.getSupportedShapes();
260
274
  for (const type of shapes) {
261
275
  const drawer = this._engine.getShapeDrawer(type);
262
276
  if (drawer) {
263
- this.drawers.set(type, drawer);
277
+ this.shapeDrawers.set(type, drawer);
264
278
  }
265
279
  }
266
280
  this._options = loadContainerOptions(this._engine, this, this._initialSourceOptions, this.sourceOptions);
@@ -279,8 +293,11 @@ export class Container {
279
293
  this._delay = getRangeValue(this.actualOptions.delay) * 1000;
280
294
  this._lifeTime = 0;
281
295
  this.fpsLimit = this.actualOptions.fpsLimit > 0 ? this.actualOptions.fpsLimit : 120;
282
- this.smooth = this.actualOptions.smooth;
283
- for (const [, drawer] of this.drawers) {
296
+ this._smooth = this.actualOptions.smooth;
297
+ for (const [, drawer] of this.effectDrawers) {
298
+ drawer.init && (await drawer.init(this));
299
+ }
300
+ for (const [, drawer] of this.shapeDrawers) {
284
301
  drawer.init && (await drawer.init(this));
285
302
  }
286
303
  for (const [, plugin] of this.plugins) {
@@ -409,10 +426,10 @@ export class Container {
409
426
  this.actualOptions.responsive = [];
410
427
  const newMaxWidth = this.actualOptions.setResponsive(this.canvas.size.width, this.retina.pixelRatio, this._options);
411
428
  this.actualOptions.setTheme(this._currentTheme);
412
- if (this.responsiveMaxWidth === newMaxWidth) {
429
+ if (this._responsiveMaxWidth === newMaxWidth) {
413
430
  return false;
414
431
  }
415
- this.responsiveMaxWidth = newMaxWidth;
432
+ this._responsiveMaxWidth = newMaxWidth;
416
433
  return true;
417
434
  }
418
435
  }
@@ -39,7 +39,8 @@ export class Engine {
39
39
  this.movers = new Map();
40
40
  this.updaters = new Map();
41
41
  this.presets = new Map();
42
- this.drawers = new Map();
42
+ this.effectDrawers = new Map();
43
+ this.shapeDrawers = new Map();
43
44
  this.pathGenerators = new Map();
44
45
  }
45
46
  get configs() {
@@ -50,13 +51,19 @@ export class Engine {
50
51
  return res;
51
52
  }
52
53
  get version() {
53
- return "3.0.0-beta.3";
54
+ return "3.0.0-beta.5";
54
55
  }
55
56
  addConfig(config) {
56
57
  const name = config.name ?? "default";
57
58
  this._configs.set(name, config);
58
59
  this._eventDispatcher.dispatchEvent("configAdded", { data: { name, config } });
59
60
  }
61
+ async addEffect(effect, drawer, refresh = true) {
62
+ executeOnSingleOrMultiple(effect, (type) => {
63
+ !this.getEffectDrawer(type) && this.effectDrawers.set(type, drawer);
64
+ });
65
+ await this.refresh(refresh);
66
+ }
60
67
  addEventListener(type, listener) {
61
68
  this._eventDispatcher.addEventListener(type, listener);
62
69
  }
@@ -86,7 +93,7 @@ export class Engine {
86
93
  }
87
94
  async addShape(shape, drawer, refresh = true) {
88
95
  executeOnSingleOrMultiple(shape, (type) => {
89
- !this.getShapeDrawer(type) && this.drawers.set(type, drawer);
96
+ !this.getShapeDrawer(type) && this.shapeDrawers.set(type, drawer);
90
97
  });
91
98
  await this.refresh(refresh);
92
99
  }
@@ -116,6 +123,9 @@ export class Engine {
116
123
  }
117
124
  return res;
118
125
  }
126
+ getEffectDrawer(type) {
127
+ return this.effectDrawers.get(type);
128
+ }
119
129
  getInteractors(container, force = false) {
120
130
  return getItemsFromInitializer(container, this.interactors, this._initializers.interactors, force);
121
131
  }
@@ -132,10 +142,13 @@ export class Engine {
132
142
  return this.presets.get(preset);
133
143
  }
134
144
  getShapeDrawer(type) {
135
- return this.drawers.get(type);
145
+ return this.shapeDrawers.get(type);
146
+ }
147
+ getSupportedEffects() {
148
+ return this.effectDrawers.keys();
136
149
  }
137
150
  getSupportedShapes() {
138
- return this.drawers.keys();
151
+ return this.shapeDrawers.keys();
139
152
  }
140
153
  getUpdaters(container, force = false) {
141
154
  return getItemsFromInitializer(container, this.updaters, this._initializers.updaters, force);
@@ -1,4 +1,4 @@
1
- import { calcExactPositionOrRandomFromSize, clamp, getDistance, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, getValue, randomInRange, setRangeValue, } from "../Utils/NumberUtils.js";
1
+ import { calcExactPositionOrRandomFromSize, clamp, getDistance, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRange, setRangeValue, } from "../Utils/NumberUtils.js";
2
2
  import { deepExtend, getPosition, initParticleNumericAnimationValue, isInArray, itemFromSingleOrMultiple, } from "../Utils/Utils.js";
3
3
  import { getHslFromAnimation, rangeColorToRgb } from "../Utils/ColorUtils.js";
4
4
  import { Interactivity } from "../Options/Classes/Interactivity/Interactivity.js";
@@ -7,6 +7,16 @@ import { Vector3d } from "./Utils/Vector3d.js";
7
7
  import { alterHsl } from "../Utils/CanvasUtils.js";
8
8
  import { errorPrefix } from "./Utils/Constants.js";
9
9
  import { loadParticlesOptions } from "../Utils/OptionsUtils.js";
10
+ function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
11
+ const effectData = effectOptions.options[effect];
12
+ if (!effectData) {
13
+ return;
14
+ }
15
+ return deepExtend({
16
+ close: effectOptions.close,
17
+ fill: effectOptions.fill,
18
+ }, itemFromSingleOrMultiple(effectData, id, reduceDuplicates));
19
+ }
10
20
  function loadShapeData(shape, shapeOptions, id, reduceDuplicates) {
11
21
  const shapeData = shapeOptions.options[shape];
12
22
  if (!shapeData) {
@@ -76,8 +86,8 @@ export class Particle {
76
86
  return res;
77
87
  }
78
88
  const rad = (Math.PI / 180) * getRangeValue(moveOptions.angle.value), radOffset = (Math.PI / 180) * getRangeValue(moveOptions.angle.offset), range = {
79
- left: radOffset - rad / 2,
80
- right: radOffset + rad / 2,
89
+ left: radOffset - rad * 0.5,
90
+ right: radOffset + rad * 0.5,
81
91
  };
82
92
  if (!moveOptions.straight) {
83
93
  res.angle += randomInRange(setRangeValue(range.left, range.right));
@@ -106,7 +116,7 @@ export class Particle {
106
116
  if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
107
117
  return color;
108
118
  }
109
- const backFactor = this.roll.horizontal && this.roll.vertical ? 2 : 1, backSum = this.roll.horizontal ? Math.PI / 2 : 0, rolled = Math.floor(((this.roll.angle ?? 0) + backSum) / (Math.PI / backFactor)) % 2;
119
+ const backFactor = this.roll.horizontal && this.roll.vertical ? 2 : 1, backSum = this.roll.horizontal ? Math.PI * 0.5 : 0, rolled = Math.floor(((this.roll.angle ?? 0) + backSum) / (Math.PI / backFactor)) % 2;
110
120
  if (!rolled) {
111
121
  return color;
112
122
  }
@@ -149,20 +159,15 @@ export class Particle {
149
159
  this.destroyed = true;
150
160
  this.bubble.inRange = false;
151
161
  this.slow.inRange = false;
152
- const container = this.container, pathGenerator = this.pathGenerator;
162
+ const container = this.container, pathGenerator = this.pathGenerator, shapeDrawer = container.shapeDrawers.get(this.shape);
163
+ shapeDrawer && shapeDrawer.particleDestroy && shapeDrawer.particleDestroy(this);
153
164
  for (const [, plugin] of container.plugins) {
154
- if (plugin.particleDestroyed) {
155
- plugin.particleDestroyed(this, override);
156
- }
165
+ plugin.particleDestroyed && plugin.particleDestroyed(this, override);
157
166
  }
158
167
  for (const updater of container.particles.updaters) {
159
- if (updater.particleDestroyed) {
160
- updater.particleDestroyed(this, override);
161
- }
162
- }
163
- if (pathGenerator) {
164
- pathGenerator.reset(this);
168
+ updater.particleDestroyed && updater.particleDestroyed(this, override);
165
169
  }
170
+ pathGenerator && pathGenerator.reset(this);
166
171
  }
167
172
  draw(delta) {
168
173
  const container = this.container, canvas = container.canvas;
@@ -175,7 +180,7 @@ export class Particle {
175
180
  return this._getRollColor(this.bubble.color ?? getHslFromAnimation(this.color));
176
181
  }
177
182
  getMass() {
178
- return (this.getRadius() ** 2 * Math.PI) / 2;
183
+ return this.getRadius() ** 2 * Math.PI * 0.5;
179
184
  }
180
185
  getPosition() {
181
186
  return {
@@ -194,9 +199,11 @@ export class Particle {
194
199
  const container = this.container, engine = this._engine;
195
200
  this.id = id;
196
201
  this.group = group;
197
- this.fill = true;
202
+ this.effectClose = true;
203
+ this.effectFill = true;
204
+ this.shapeClose = true;
205
+ this.shapeFill = true;
198
206
  this.pathRotation = false;
199
- this.close = true;
200
207
  this.lastPathTime = 0;
201
208
  this.destroyed = false;
202
209
  this.unbreakable = false;
@@ -207,18 +214,33 @@ export class Particle {
207
214
  };
208
215
  this.outType = "normal";
209
216
  this.ignoresResizeRatio = true;
210
- const pxRatio = container.retina.pixelRatio, mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(this._engine, container, mainOptions.particles), shapeType = particlesOptions.shape.type, { reduceDuplicates } = particlesOptions;
217
+ const pxRatio = container.retina.pixelRatio, mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(this._engine, container, mainOptions.particles), effectType = particlesOptions.effect.type, shapeType = particlesOptions.shape.type, { reduceDuplicates } = particlesOptions;
218
+ this.effect = itemFromSingleOrMultiple(effectType, this.id, reduceDuplicates);
211
219
  this.shape = itemFromSingleOrMultiple(shapeType, this.id, reduceDuplicates);
212
- const shapeOptions = particlesOptions.shape;
213
- if (overrideOptions && overrideOptions.shape && overrideOptions.shape.type) {
214
- const overrideShapeType = overrideOptions.shape.type, shape = itemFromSingleOrMultiple(overrideShapeType, this.id, reduceDuplicates);
215
- if (shape) {
216
- this.shape = shape;
217
- shapeOptions.load(overrideOptions.shape);
220
+ const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
221
+ if (overrideOptions) {
222
+ if (overrideOptions.effect && overrideOptions.effect.type) {
223
+ const overrideEffectType = overrideOptions.effect.type, effect = itemFromSingleOrMultiple(overrideEffectType, this.id, reduceDuplicates);
224
+ if (effect) {
225
+ this.effect = effect;
226
+ effectOptions.load(overrideOptions.effect);
227
+ }
228
+ }
229
+ if (overrideOptions.shape && overrideOptions.shape.type) {
230
+ const overrideShapeType = overrideOptions.shape.type, shape = itemFromSingleOrMultiple(overrideShapeType, this.id, reduceDuplicates);
231
+ if (shape) {
232
+ this.shape = shape;
233
+ shapeOptions.load(overrideOptions.shape);
234
+ }
218
235
  }
219
236
  }
237
+ this.effectData = loadEffectData(this.effect, effectOptions, this.id, reduceDuplicates);
220
238
  this.shapeData = loadShapeData(this.shape, shapeOptions, this.id, reduceDuplicates);
221
239
  particlesOptions.load(overrideOptions);
240
+ const effectData = this.effectData;
241
+ if (effectData) {
242
+ particlesOptions.load(effectData.particles);
243
+ }
222
244
  const shapeData = this.shapeData;
223
245
  if (shapeData) {
224
246
  particlesOptions.load(shapeData.particles);
@@ -227,11 +249,13 @@ export class Particle {
227
249
  interactivity.load(container.actualOptions.interactivity);
228
250
  interactivity.load(particlesOptions.interactivity);
229
251
  this.interactivity = interactivity;
230
- this.fill = shapeData?.fill ?? particlesOptions.shape.fill;
231
- this.close = shapeData?.close ?? particlesOptions.shape.close;
252
+ this.effectFill = effectData?.fill ?? particlesOptions.effect.fill;
253
+ this.effectClose = effectData?.close ?? particlesOptions.effect.close;
254
+ this.shapeFill = shapeData?.fill ?? particlesOptions.shape.fill;
255
+ this.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
232
256
  this.options = particlesOptions;
233
257
  const pathOptions = this.options.move.path;
234
- this.pathDelay = getValue(pathOptions.delay) * 1000;
258
+ this.pathDelay = getRangeValue(pathOptions.delay.value) * 1000;
235
259
  if (pathOptions.generator) {
236
260
  this.pathGenerator = this._engine.getPathGenerator(pathOptions.generator);
237
261
  if (this.pathGenerator && container.addPath(pathOptions.generator, this.pathGenerator)) {
@@ -252,34 +276,46 @@ export class Particle {
252
276
  this.velocity = this.initialVelocity.copy();
253
277
  this.moveDecay = 1 - getRangeValue(this.options.move.decay);
254
278
  const particles = container.particles;
255
- particles.needsSort = particles.needsSort || particles.lastZIndex < this.position.z;
256
- particles.lastZIndex = this.position.z;
279
+ particles.setLastZIndex(this.position.z);
257
280
  this.zIndexFactor = this.position.z / container.zLayers;
258
281
  this.sides = 24;
259
- let drawer = container.drawers.get(this.shape);
260
- if (!drawer) {
261
- drawer = this._engine.getShapeDrawer(this.shape);
262
- if (drawer) {
263
- container.drawers.set(this.shape, drawer);
282
+ let effectDrawer = container.effectDrawers.get(this.effect);
283
+ if (!effectDrawer) {
284
+ effectDrawer = this._engine.getEffectDrawer(this.effect);
285
+ if (effectDrawer) {
286
+ container.effectDrawers.set(this.effect, effectDrawer);
287
+ }
288
+ }
289
+ if (effectDrawer && effectDrawer.loadEffect) {
290
+ effectDrawer.loadEffect(this);
291
+ }
292
+ let shapeDrawer = container.shapeDrawers.get(this.shape);
293
+ if (!shapeDrawer) {
294
+ shapeDrawer = this._engine.getShapeDrawer(this.shape);
295
+ if (shapeDrawer) {
296
+ container.shapeDrawers.set(this.shape, shapeDrawer);
264
297
  }
265
298
  }
266
- if (drawer && drawer.loadShape) {
267
- drawer.loadShape(this);
299
+ if (shapeDrawer && shapeDrawer.loadShape) {
300
+ shapeDrawer.loadShape(this);
268
301
  }
269
- const sideCountFunc = drawer?.getSidesCount;
302
+ const sideCountFunc = shapeDrawer?.getSidesCount;
270
303
  if (sideCountFunc) {
271
304
  this.sides = sideCountFunc(this);
272
305
  }
273
306
  this.spawning = false;
274
307
  this.shadowColor = rangeColorToRgb(this.options.shadow.color);
275
- for (const updater of container.particles.updaters) {
308
+ for (const updater of particles.updaters) {
276
309
  updater.init(this);
277
310
  }
278
- for (const mover of container.particles.movers) {
311
+ for (const mover of particles.movers) {
279
312
  mover.init && mover.init(this);
280
313
  }
281
- if (drawer && drawer.particleInit) {
282
- drawer.particleInit(container, this);
314
+ if (effectDrawer && effectDrawer.particleInit) {
315
+ effectDrawer.particleInit(container, this);
316
+ }
317
+ if (shapeDrawer && shapeDrawer.particleInit) {
318
+ shapeDrawer.particleInit(container, this);
283
319
  }
284
320
  for (const [, plugin] of container.plugins) {
285
321
  plugin.particleCreated && plugin.particleCreated(this);
@@ -7,16 +7,29 @@ import { Rectangle } from "./Utils/Rectangle.js";
7
7
  import { errorPrefix } from "./Utils/Constants.js";
8
8
  const qTreeCapacity = 4;
9
9
  const qTreeRectangle = (canvasSize) => {
10
- return new Rectangle(-canvasSize.width / 4, -canvasSize.height / 4, (canvasSize.width * 3) / 2, (canvasSize.height * 3) / 2);
10
+ const { height, width } = canvasSize, posOffset = -0.25, sizeFactor = 1.5;
11
+ return new Rectangle(posOffset * width, posOffset * height, sizeFactor * width, sizeFactor * height);
11
12
  };
12
13
  export class Particles {
13
14
  constructor(engine, container) {
14
15
  this._applyDensity = (options, manualCount, group) => {
16
+ const numberOptions = options.number;
15
17
  if (!options.number.density?.enable) {
18
+ if (group === undefined) {
19
+ this._limit = numberOptions.limit.value;
20
+ }
21
+ else {
22
+ this._groupLimits.set(group, numberOptions.limit.value);
23
+ }
16
24
  return;
17
25
  }
18
- const numberOptions = options.number, densityFactor = this._initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit > 0 ? numberOptions.limit : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + manualCount, particlesCount = Math.min(this.count, this.filter((t) => t.group === group).length);
19
- this.limit = numberOptions.limit * densityFactor;
26
+ const densityFactor = this._initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit.value > 0 ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + manualCount, particlesCount = Math.min(this.count, this.filter((t) => t.group === group).length);
27
+ if (group === undefined) {
28
+ this._limit = numberOptions.limit.value * densityFactor;
29
+ }
30
+ else {
31
+ this._groupLimits.set(group, numberOptions.limit.value * densityFactor);
32
+ }
20
33
  if (particlesCount < particlesNumber) {
21
34
  this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
22
35
  }
@@ -34,7 +47,7 @@ export class Particles {
34
47
  };
35
48
  this._pushParticle = (position, overrideOptions, group, initializer) => {
36
49
  try {
37
- let particle = this.pool.pop();
50
+ let particle = this._pool.pop();
38
51
  if (particle) {
39
52
  particle.init(this._nextId, position, overrideOptions, group);
40
53
  }
@@ -73,7 +86,7 @@ export class Particles {
73
86
  const zIdx = this._zArray.indexOf(particle);
74
87
  this._array.splice(index, 1);
75
88
  this._zArray.splice(zIdx, 1);
76
- this.pool.push(particle);
89
+ this._pool.push(particle);
77
90
  this._engine.dispatchEvent("particleRemoved", {
78
91
  container: this._container,
79
92
  data: {
@@ -87,10 +100,11 @@ export class Particles {
87
100
  this._nextId = 0;
88
101
  this._array = [];
89
102
  this._zArray = [];
90
- this.pool = [];
91
- this.limit = 0;
92
- this.needsSort = false;
93
- this.lastZIndex = 0;
103
+ this._pool = [];
104
+ this._limit = 0;
105
+ this._groupLimits = new Map();
106
+ this._needsSort = false;
107
+ this._lastZIndex = 0;
94
108
  this._interactionManager = new InteractionManager(engine, container);
95
109
  const canvasSize = container.canvas.size;
96
110
  this.quadTree = new QuadTree(qTreeRectangle(canvasSize), qTreeCapacity);
@@ -107,17 +121,21 @@ export class Particles {
107
121
  }
108
122
  }
109
123
  addParticle(position, overrideOptions, group, initializer) {
110
- this.pushing = true;
111
- const container = this._container, options = container.actualOptions, limit = options.particles.number.limit;
124
+ const limitOptions = this._container.actualOptions.particles.number.limit, limit = group === undefined ? this._limit : this._groupLimits.get(group) ?? this._limit, currentCount = this.count;
112
125
  if (limit > 0) {
113
- const countToRemove = this.count + 1 - limit;
114
- if (countToRemove > 0) {
115
- this.removeQuantity(countToRemove);
126
+ if (limitOptions.mode === "delete") {
127
+ const countToRemove = currentCount + 1 - limit;
128
+ if (countToRemove > 0) {
129
+ this.removeQuantity(countToRemove);
130
+ }
131
+ }
132
+ else if (limitOptions.mode === "wait") {
133
+ if (currentCount >= limit) {
134
+ return;
135
+ }
116
136
  }
117
137
  }
118
- const res = this._pushParticle(position, overrideOptions, group, initializer);
119
- this.pushing = false;
120
- return res;
138
+ return this._pushParticle(position, overrideOptions, group, initializer);
121
139
  }
122
140
  clear() {
123
141
  this._array = [];
@@ -154,8 +172,8 @@ export class Particles {
154
172
  }
155
173
  init() {
156
174
  const container = this._container, options = container.actualOptions;
157
- this.lastZIndex = 0;
158
- this.needsSort = false;
175
+ this._lastZIndex = 0;
176
+ this._needsSort = false;
159
177
  let handled = false;
160
178
  this.updaters = this._engine.getUpdaters(container, true);
161
179
  this._interactionManager.init();
@@ -217,6 +235,13 @@ export class Particles {
217
235
  }
218
236
  this._applyDensity(options.particles, options.manualParticles.length);
219
237
  }
238
+ setLastZIndex(zIndex) {
239
+ this._lastZIndex = zIndex;
240
+ this._needsSort = this._needsSort || this._lastZIndex < zIndex;
241
+ }
242
+ setResizeFactor(factor) {
243
+ this._resizeFactor = factor;
244
+ }
220
245
  async update(delta) {
221
246
  const container = this._container, particlesToDelete = new Set();
222
247
  this.quadTree = new QuadTree(qTreeRectangle(container.canvas.size), qTreeCapacity);
@@ -224,10 +249,10 @@ export class Particles {
224
249
  pathGenerator.update();
225
250
  }
226
251
  for (const [, plugin] of container.plugins) {
227
- plugin.update && plugin.update(delta);
252
+ plugin.update && (await plugin.update(delta));
228
253
  }
254
+ const resizeFactor = this._resizeFactor;
229
255
  for (const particle of this._array) {
230
- const resizeFactor = container.canvas.resizeFactor;
231
256
  if (resizeFactor && !particle.ignoresResizeRatio) {
232
257
  particle.position.x *= resizeFactor.width;
233
258
  particle.position.y *= resizeFactor.height;
@@ -255,7 +280,7 @@ export class Particles {
255
280
  const checkDelete = (p) => !particlesToDelete.has(p);
256
281
  this._array = this.filter(checkDelete);
257
282
  this._zArray = this._zArray.filter(checkDelete);
258
- this.pool.push(...particlesToDelete);
283
+ this._pool.push(...particlesToDelete);
259
284
  }
260
285
  await this._interactionManager.externalInteract(delta);
261
286
  for (const particle of this._array) {
@@ -266,12 +291,12 @@ export class Particles {
266
291
  await this._interactionManager.particlesInteract(particle, delta);
267
292
  }
268
293
  }
269
- delete container.canvas.resizeFactor;
270
- if (this.needsSort) {
294
+ delete this._resizeFactor;
295
+ if (this._needsSort) {
271
296
  const zArray = this._zArray;
272
297
  zArray.sort((a, b) => b.position.z - a.position.z || a.id - b.id);
273
- this.lastZIndex = zArray[zArray.length - 1].position.z;
274
- this.needsSort = false;
298
+ this._lastZIndex = zArray[zArray.length - 1].position.z;
299
+ this._needsSort = false;
275
300
  }
276
301
  }
277
302
  }
@@ -17,13 +17,11 @@ export class Retina {
17
17
  canvas.size.height = element.offsetHeight * ratio;
18
18
  }
19
19
  const particles = options.particles, moveOptions = particles.move;
20
- this.attractDistance = getRangeValue(moveOptions.attract.distance) * ratio;
21
20
  this.maxSpeed = getRangeValue(moveOptions.gravity.maxSpeed) * ratio;
22
21
  this.sizeAnimationSpeed = getRangeValue(particles.size.animation.speed) * ratio;
23
22
  }
24
23
  initParticle(particle) {
25
24
  const options = particle.options, ratio = this.pixelRatio, moveOptions = options.move, moveDistance = moveOptions.distance, props = particle.retina;
26
- props.attractDistance = getRangeValue(moveOptions.attract.distance) * ratio;
27
25
  props.moveDrift = getRangeValue(moveOptions.drift) * ratio;
28
26
  props.moveSpeed = getRangeValue(moveOptions.speed) * ratio;
29
27
  props.sizeAnimationSpeed = getRangeValue(options.size.animation.speed) * ratio;
@@ -8,7 +8,7 @@ export class QuadTree {
8
8
  this._subdivide = () => {
9
9
  const { x, y } = this.rectangle.position, { width, height } = this.rectangle.size, { capacity } = this;
10
10
  for (let i = 0; i < 4; i++) {
11
- this._subs.push(new QuadTree(new Rectangle(x + (width / 2) * (i % 2), y + (height / 2) * (Math.round(i / 2) - (i % 2)), width / 2, height / 2), capacity));
11
+ this._subs.push(new QuadTree(new Rectangle(x + width * 0.5 * (i % 2), y + height * 0.5 * (Math.round(i * 0.5) - (i % 2)), width * 0.5, height * 0.5), capacity));
12
12
  }
13
13
  this._divided = true;
14
14
  };
@@ -14,8 +14,7 @@ export class BackgroundMask {
14
14
  this.composite = data.composite;
15
15
  }
16
16
  if (data.cover !== undefined) {
17
- const cover = data.cover;
18
- const color = (isString(data.cover) ? { color: data.cover } : data.cover);
17
+ const cover = data.cover, color = (isString(data.cover) ? { color: data.cover } : data.cover);
19
18
  this.cover.load(cover.color !== undefined ? cover : { color: color });
20
19
  }
21
20
  if (data.enable !== undefined) {
@@ -1,38 +1,18 @@
1
+ import { AnimationOptions } from "./AnimationOptions.js";
1
2
  import { setRangeValue } from "../../Utils/NumberUtils.js";
2
- export class ColorAnimation {
3
+ export class ColorAnimation extends AnimationOptions {
3
4
  constructor() {
4
- this.count = 0;
5
- this.enable = false;
5
+ super();
6
6
  this.offset = 0;
7
- this.speed = 1;
8
- this.delay = 0;
9
- this.decay = 0;
10
7
  this.sync = true;
11
8
  }
12
9
  load(data) {
10
+ super.load(data);
13
11
  if (!data) {
14
12
  return;
15
13
  }
16
- if (data.count !== undefined) {
17
- this.count = setRangeValue(data.count);
18
- }
19
- if (data.enable !== undefined) {
20
- this.enable = data.enable;
21
- }
22
14
  if (data.offset !== undefined) {
23
15
  this.offset = setRangeValue(data.offset);
24
16
  }
25
- if (data.speed !== undefined) {
26
- this.speed = setRangeValue(data.speed);
27
- }
28
- if (data.decay !== undefined) {
29
- this.decay = setRangeValue(data.decay);
30
- }
31
- if (data.delay !== undefined) {
32
- this.delay = setRangeValue(data.delay);
33
- }
34
- if (data.sync !== undefined) {
35
- this.sync = data.sync;
36
- }
37
17
  }
38
18
  }