@tsparticles/engine 3.0.3 → 3.2.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.
Files changed (156) hide show
  1. package/373.min.js +2 -0
  2. package/373.min.js.LICENSE.txt +1 -0
  3. package/438.min.js +2 -0
  4. package/438.min.js.LICENSE.txt +1 -0
  5. package/README.md +337 -216
  6. package/browser/Core/Canvas.js +102 -49
  7. package/browser/Core/Container.js +53 -41
  8. package/browser/Core/Engine.js +47 -32
  9. package/browser/Core/Particle.js +46 -48
  10. package/browser/Core/Particles.js +70 -57
  11. package/browser/Core/Retina.js +5 -4
  12. package/browser/Core/Utils/Circle.js +4 -3
  13. package/browser/Core/Utils/Constants.js +3 -0
  14. package/browser/Core/Utils/EventListeners.js +19 -16
  15. package/browser/Core/Utils/ExternalInteractorBase.js +1 -1
  16. package/browser/Core/Utils/InteractionManager.js +17 -8
  17. package/browser/Core/Utils/ParticlesInteractorBase.js +1 -1
  18. package/browser/Core/Utils/QuadTree.js +5 -3
  19. package/browser/Core/Utils/Vector.js +7 -2
  20. package/browser/Core/Utils/Vector3d.js +14 -9
  21. package/browser/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  22. package/browser/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  23. package/browser/Options/Classes/ManualParticle.js +3 -2
  24. package/browser/Options/Classes/Options.js +3 -0
  25. package/browser/Utils/CanvasUtils.js +50 -40
  26. package/browser/Utils/ColorUtils.js +124 -45
  27. package/browser/Utils/EventDispatcher.js +6 -5
  28. package/browser/Utils/HslColorManager.js +5 -5
  29. package/browser/Utils/NumberUtils.js +35 -23
  30. package/browser/Utils/RgbColorManager.js +5 -5
  31. package/browser/Utils/Utils.js +102 -19
  32. package/cjs/Core/Canvas.js +102 -49
  33. package/cjs/Core/Container.js +53 -41
  34. package/cjs/Core/Engine.js +70 -32
  35. package/cjs/Core/Particle.js +45 -47
  36. package/cjs/Core/Particles.js +93 -57
  37. package/cjs/Core/Retina.js +5 -4
  38. package/cjs/Core/Utils/Circle.js +4 -3
  39. package/cjs/Core/Utils/Constants.js +4 -1
  40. package/cjs/Core/Utils/EventListeners.js +18 -15
  41. package/cjs/Core/Utils/ExternalInteractorBase.js +1 -1
  42. package/cjs/Core/Utils/InteractionManager.js +17 -8
  43. package/cjs/Core/Utils/ParticlesInteractorBase.js +1 -1
  44. package/cjs/Core/Utils/QuadTree.js +5 -3
  45. package/cjs/Core/Utils/Vector.js +7 -2
  46. package/cjs/Core/Utils/Vector3d.js +14 -9
  47. package/cjs/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  48. package/cjs/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  49. package/cjs/Options/Classes/ManualParticle.js +3 -2
  50. package/cjs/Options/Classes/Options.js +3 -0
  51. package/cjs/Utils/CanvasUtils.js +50 -40
  52. package/cjs/Utils/ColorUtils.js +126 -45
  53. package/cjs/Utils/EventDispatcher.js +6 -5
  54. package/cjs/Utils/HslColorManager.js +5 -5
  55. package/cjs/Utils/NumberUtils.js +37 -24
  56. package/cjs/Utils/RgbColorManager.js +5 -5
  57. package/cjs/Utils/Utils.js +103 -19
  58. package/dist_browser_Core_Container_js.js +92 -0
  59. package/dist_browser_Core_Particle_js.js +32 -0
  60. package/esm/Core/Canvas.js +102 -49
  61. package/esm/Core/Container.js +53 -41
  62. package/esm/Core/Engine.js +47 -32
  63. package/esm/Core/Particle.js +46 -48
  64. package/esm/Core/Particles.js +70 -57
  65. package/esm/Core/Retina.js +5 -4
  66. package/esm/Core/Utils/Circle.js +4 -3
  67. package/esm/Core/Utils/Constants.js +3 -0
  68. package/esm/Core/Utils/EventListeners.js +19 -16
  69. package/esm/Core/Utils/ExternalInteractorBase.js +1 -1
  70. package/esm/Core/Utils/InteractionManager.js +17 -8
  71. package/esm/Core/Utils/ParticlesInteractorBase.js +1 -1
  72. package/esm/Core/Utils/QuadTree.js +5 -3
  73. package/esm/Core/Utils/Vector.js +7 -2
  74. package/esm/Core/Utils/Vector3d.js +14 -9
  75. package/esm/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  76. package/esm/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  77. package/esm/Options/Classes/ManualParticle.js +3 -2
  78. package/esm/Options/Classes/Options.js +3 -0
  79. package/esm/Utils/CanvasUtils.js +50 -40
  80. package/esm/Utils/ColorUtils.js +124 -45
  81. package/esm/Utils/EventDispatcher.js +6 -5
  82. package/esm/Utils/HslColorManager.js +5 -5
  83. package/esm/Utils/NumberUtils.js +35 -23
  84. package/esm/Utils/RgbColorManager.js +5 -5
  85. package/esm/Utils/Utils.js +102 -19
  86. package/package.json +1 -1
  87. package/report.html +3 -3
  88. package/tsparticles.engine.js +894 -5461
  89. package/tsparticles.engine.min.js +1 -1
  90. package/tsparticles.engine.min.js.LICENSE.txt +1 -1
  91. package/types/Core/Canvas.d.ts +5 -3
  92. package/types/Core/Container.d.ts +1 -1
  93. package/types/Core/Engine.d.ts +13 -8
  94. package/types/Core/Interfaces/IContainerPlugin.d.ts +3 -3
  95. package/types/Core/Interfaces/IEffectDrawer.d.ts +3 -3
  96. package/types/Core/Interfaces/IMovePathGenerator.d.ts +2 -2
  97. package/types/Core/Interfaces/IParticleHslAnimation.d.ts +4 -4
  98. package/types/Core/Interfaces/IParticleMover.d.ts +2 -2
  99. package/types/Core/Interfaces/IParticleUpdater.d.ts +2 -2
  100. package/types/Core/Interfaces/IParticleValueAnimation.d.ts +4 -0
  101. package/types/Core/Interfaces/IPlugin.d.ts +1 -1
  102. package/types/Core/Interfaces/IShapeDrawData.d.ts +2 -2
  103. package/types/Core/Interfaces/IShapeDrawer.d.ts +4 -4
  104. package/types/Core/Particle.d.ts +3 -3
  105. package/types/Core/Particles.d.ts +12 -8
  106. package/types/Core/Utils/Constants.d.ts +3 -0
  107. package/types/Core/Utils/ExternalInteractorBase.d.ts +1 -1
  108. package/types/Core/Utils/InteractionManager.d.ts +3 -3
  109. package/types/Core/Utils/ParticlesInteractorBase.d.ts +1 -1
  110. package/types/Core/Utils/Point.d.ts +1 -1
  111. package/types/Enums/Modes/OutMode.d.ts +0 -3
  112. package/types/Options/Classes/BackgroundMask/BackgroundMaskCover.d.ts +2 -1
  113. package/types/Options/Classes/Options.d.ts +1 -0
  114. package/types/Options/Classes/Particles/Move/Move.d.ts +1 -2
  115. package/types/Options/Classes/Particles/Move/OutModes.d.ts +5 -6
  116. package/types/Options/Interfaces/BackgroundMask/IBackgroundMask.d.ts +2 -1
  117. package/types/Options/Interfaces/BackgroundMask/IBackgroundMaskCover.d.ts +2 -1
  118. package/types/Options/Interfaces/IOptions.d.ts +1 -0
  119. package/types/Options/Interfaces/Interactivity/Modes/IModes.d.ts +1 -3
  120. package/types/Options/Interfaces/Particles/Move/IMove.d.ts +2 -2
  121. package/types/Options/Interfaces/Particles/Move/IOutModes.d.ts +6 -6
  122. package/types/Types/CustomEventArgs.d.ts +2 -2
  123. package/types/Types/ExportResult.d.ts +2 -2
  124. package/types/Types/ParticlesGroups.d.ts +1 -3
  125. package/types/Types/PathOptions.d.ts +1 -3
  126. package/types/Types/ShapeData.d.ts +1 -3
  127. package/types/Utils/CanvasUtils.d.ts +9 -8
  128. package/types/Utils/ColorUtils.d.ts +5 -0
  129. package/types/Utils/NumberUtils.d.ts +2 -2
  130. package/types/Utils/Utils.d.ts +9 -6
  131. package/umd/Core/Canvas.js +102 -49
  132. package/umd/Core/Container.js +54 -42
  133. package/umd/Core/Engine.js +72 -33
  134. package/umd/Core/Particle.js +46 -48
  135. package/umd/Core/Particles.js +95 -58
  136. package/umd/Core/Retina.js +5 -4
  137. package/umd/Core/Utils/Circle.js +4 -3
  138. package/umd/Core/Utils/Constants.js +4 -1
  139. package/umd/Core/Utils/EventListeners.js +18 -15
  140. package/umd/Core/Utils/ExternalInteractorBase.js +1 -1
  141. package/umd/Core/Utils/InteractionManager.js +17 -8
  142. package/umd/Core/Utils/ParticlesInteractorBase.js +1 -1
  143. package/umd/Core/Utils/QuadTree.js +5 -3
  144. package/umd/Core/Utils/Vector.js +7 -2
  145. package/umd/Core/Utils/Vector3d.js +14 -9
  146. package/umd/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  147. package/umd/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  148. package/umd/Options/Classes/ManualParticle.js +3 -2
  149. package/umd/Options/Classes/Options.js +3 -0
  150. package/umd/Utils/CanvasUtils.js +50 -40
  151. package/umd/Utils/ColorUtils.js +127 -46
  152. package/umd/Utils/EventDispatcher.js +6 -5
  153. package/umd/Utils/HslColorManager.js +5 -5
  154. package/umd/Utils/NumberUtils.js +38 -25
  155. package/umd/Utils/RgbColorManager.js +5 -5
  156. package/umd/Utils/Utils.js +104 -20
@@ -1,11 +1,10 @@
1
1
  import { getLogger, getPosition } from "../Utils/Utils.js";
2
2
  import { InteractionManager } from "./Utils/InteractionManager.js";
3
- import { Particle } from "./Particle.js";
4
3
  import { Point } from "./Utils/Point.js";
5
4
  import { QuadTree } from "./Utils/QuadTree.js";
6
5
  import { Rectangle } from "./Utils/Rectangle.js";
7
6
  import { errorPrefix } from "./Utils/Constants.js";
8
- const qTreeCapacity = 4;
7
+ const qTreeCapacity = 4, squareExp = 2, defaultRemoveQuantity = 1;
9
8
  const qTreeRectangle = (canvasSize) => {
10
9
  const { height, width } = canvasSize, posOffset = -0.25, sizeFactor = 1.5;
11
10
  return new Rectangle(posOffset * width, posOffset * height, sizeFactor * width, sizeFactor * height);
@@ -17,7 +16,7 @@ export class Particles {
17
16
  this._pool.push(particle);
18
17
  }
19
18
  };
20
- this._applyDensity = (options, manualCount, group) => {
19
+ this._applyDensity = async (options, manualCount, group) => {
21
20
  const numberOptions = options.number;
22
21
  if (!options.number.density?.enable) {
23
22
  if (group === undefined) {
@@ -28,7 +27,7 @@ export class Particles {
28
27
  }
29
28
  return;
30
29
  }
31
- 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);
30
+ const densityFactor = this._initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, minLimit = 0, optParticlesLimit = numberOptions.limit.value > minLimit ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + manualCount, particlesCount = Math.min(this.count, this.filter((t) => t.group === group).length);
32
31
  if (group === undefined) {
33
32
  this._limit = numberOptions.limit.value * densityFactor;
34
33
  }
@@ -36,29 +35,28 @@ export class Particles {
36
35
  this._groupLimits.set(group, numberOptions.limit.value * densityFactor);
37
36
  }
38
37
  if (particlesCount < particlesNumber) {
39
- this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
38
+ await this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
40
39
  }
41
40
  else if (particlesCount > particlesNumber) {
42
41
  this.removeQuantity(particlesCount - particlesNumber, group);
43
42
  }
44
43
  };
45
44
  this._initDensityFactor = (densityOptions) => {
46
- const container = this._container;
45
+ const container = this._container, defaultFactor = 1;
47
46
  if (!container.canvas.element || !densityOptions.enable) {
48
- return 1;
47
+ return defaultFactor;
49
48
  }
50
49
  const canvas = container.canvas.element, pxRatio = container.retina.pixelRatio;
51
- return (canvas.width * canvas.height) / (densityOptions.height * densityOptions.width * pxRatio ** 2);
50
+ return (canvas.width * canvas.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp);
52
51
  };
53
- this._pushParticle = (position, overrideOptions, group, initializer) => {
52
+ this._pushParticle = async (position, overrideOptions, group, initializer) => {
54
53
  try {
55
54
  let particle = this._pool.pop();
56
- if (particle) {
57
- particle.init(this._nextId, position, overrideOptions, group);
58
- }
59
- else {
60
- particle = new Particle(this._engine, this._nextId, this._container, position, overrideOptions, group);
55
+ if (!particle) {
56
+ const { Particle } = await import("./Particle.js");
57
+ particle = new Particle(this._engine, this._container);
61
58
  }
59
+ await particle.init(this._nextId, position, overrideOptions, group);
62
60
  let canAdd = true;
63
61
  if (initializer) {
64
62
  canAdd = initializer(particle);
@@ -79,7 +77,6 @@ export class Particles {
79
77
  }
80
78
  catch (e) {
81
79
  getLogger().warning(`${errorPrefix} adding particle: ${e}`);
82
- return;
83
80
  }
84
81
  };
85
82
  this._removeParticle = (index, group, override) => {
@@ -87,9 +84,9 @@ export class Particles {
87
84
  if (!particle || particle.group !== group) {
88
85
  return false;
89
86
  }
90
- const zIdx = this._zArray.indexOf(particle);
91
- this._array.splice(index, 1);
92
- this._zArray.splice(zIdx, 1);
87
+ const zIdx = this._zArray.indexOf(particle), deleteCount = 1;
88
+ this._array.splice(index, deleteCount);
89
+ this._zArray.splice(zIdx, deleteCount);
93
90
  particle.destroy(override);
94
91
  this._engine.dispatchEvent("particleRemoved", {
95
92
  container: this._container,
@@ -111,26 +108,27 @@ export class Particles {
111
108
  this._needsSort = false;
112
109
  this._lastZIndex = 0;
113
110
  this._interactionManager = new InteractionManager(engine, container);
111
+ this._pluginsInitialized = false;
114
112
  const canvasSize = container.canvas.size;
115
113
  this.quadTree = new QuadTree(qTreeRectangle(canvasSize), qTreeCapacity);
116
- this.movers = this._engine.getMovers(container, true);
117
- this.updaters = this._engine.getUpdaters(container, true);
114
+ this.movers = [];
115
+ this.updaters = [];
118
116
  }
119
117
  get count() {
120
118
  return this._array.length;
121
119
  }
122
- addManualParticles() {
120
+ async addManualParticles() {
123
121
  const container = this._container, options = container.actualOptions;
124
122
  for (const particle of options.manualParticles) {
125
- this.addParticle(particle.position ? getPosition(particle.position, container.canvas.size) : undefined, particle.options);
123
+ await this.addParticle(particle.position ? getPosition(particle.position, container.canvas.size) : undefined, particle.options);
126
124
  }
127
125
  }
128
- addParticle(position, overrideOptions, group, initializer) {
129
- const limitOptions = this._container.actualOptions.particles.number.limit, limit = group === undefined ? this._limit : this._groupLimits.get(group) ?? this._limit, currentCount = this.count;
130
- if (limit > 0) {
126
+ async addParticle(position, overrideOptions, group, initializer) {
127
+ const limitOptions = this._container.actualOptions.particles.number.limit, limit = group === undefined ? this._limit : this._groupLimits.get(group) ?? this._limit, currentCount = this.count, minLimit = 0;
128
+ if (limit > minLimit) {
131
129
  if (limitOptions.mode === "delete") {
132
- const countToRemove = currentCount + 1 - limit;
133
- if (countToRemove > 0) {
130
+ const countOffset = 1, minCount = 0, countToRemove = currentCount + countOffset - limit;
131
+ if (countToRemove > minCount) {
134
132
  this.removeQuantity(countToRemove);
135
133
  }
136
134
  }
@@ -140,11 +138,12 @@ export class Particles {
140
138
  }
141
139
  }
142
140
  }
143
- return this._pushParticle(position, overrideOptions, group, initializer);
141
+ return await this._pushParticle(position, overrideOptions, group, initializer);
144
142
  }
145
143
  clear() {
146
144
  this._array = [];
147
145
  this._zArray = [];
146
+ this._pluginsInitialized = false;
148
147
  }
149
148
  destroy() {
150
149
  this._array = [];
@@ -157,10 +156,10 @@ export class Particles {
157
156
  canvas.clear();
158
157
  await this.update(delta);
159
158
  for (const [, plugin] of container.plugins) {
160
- canvas.drawPlugin(plugin, delta);
159
+ await canvas.drawPlugin(plugin, delta);
161
160
  }
162
161
  for (const p of this._zArray) {
163
- p.draw(delta);
162
+ await p.draw(delta);
164
163
  }
165
164
  }
166
165
  filter(condition) {
@@ -175,13 +174,12 @@ export class Particles {
175
174
  handleClickMode(mode) {
176
175
  this._interactionManager.handleClickMode(mode);
177
176
  }
178
- init() {
177
+ async init() {
179
178
  const container = this._container, options = container.actualOptions;
180
179
  this._lastZIndex = 0;
181
180
  this._needsSort = false;
181
+ await this.initPlugins();
182
182
  let handled = false;
183
- this.updaters = this._engine.getUpdaters(container, true);
184
- this._interactionManager.init();
185
183
  for (const [, plugin] of container.plugins) {
186
184
  if (plugin.particlesInitialization !== undefined) {
187
185
  handled = plugin.particlesInitialization();
@@ -190,55 +188,67 @@ export class Particles {
190
188
  break;
191
189
  }
192
190
  }
193
- this._interactionManager.init();
194
- for (const [, pathGenerator] of container.pathGenerators) {
195
- pathGenerator.init(container);
196
- }
197
- this.addManualParticles();
191
+ await this.addManualParticles();
198
192
  if (!handled) {
199
193
  const particlesOptions = options.particles, groups = particlesOptions.groups;
200
194
  for (const group in groups) {
201
195
  const groupOptions = groups[group];
202
196
  for (let i = this.count, j = 0; j < groupOptions.number?.value && i < particlesOptions.number.value; i++, j++) {
203
- this.addParticle(undefined, groupOptions, group);
197
+ await this.addParticle(undefined, groupOptions, group);
204
198
  }
205
199
  }
206
200
  for (let i = this.count; i < particlesOptions.number.value; i++) {
207
- this.addParticle();
201
+ await this.addParticle();
208
202
  }
209
203
  }
210
204
  }
211
- push(nb, mouse, overrideOptions, group) {
205
+ async initPlugins() {
206
+ if (this._pluginsInitialized) {
207
+ return;
208
+ }
209
+ const container = this._container;
210
+ this.movers = await this._engine.getMovers(container, true);
211
+ this.updaters = await this._engine.getUpdaters(container, true);
212
+ await this._interactionManager.init();
213
+ for (const [, pathGenerator] of container.pathGenerators) {
214
+ await pathGenerator.init(container);
215
+ }
216
+ }
217
+ async push(nb, mouse, overrideOptions, group) {
212
218
  for (let i = 0; i < nb; i++) {
213
- this.addParticle(mouse?.position, overrideOptions, group);
219
+ await this.addParticle(mouse?.position, overrideOptions, group);
214
220
  }
215
221
  }
216
222
  async redraw() {
217
223
  this.clear();
218
- this.init();
224
+ await this.init();
219
225
  await this.draw({ value: 0, factor: 0 });
220
226
  }
221
227
  remove(particle, group, override) {
222
228
  this.removeAt(this._array.indexOf(particle), undefined, group, override);
223
229
  }
224
- removeAt(index, quantity = 1, group, override) {
225
- if (index < 0 || index > this.count) {
230
+ removeAt(index, quantity = defaultRemoveQuantity, group, override) {
231
+ const minIndex = 0;
232
+ if (index < minIndex || index > this.count) {
226
233
  return;
227
234
  }
228
235
  let deleted = 0;
229
236
  for (let i = index; deleted < quantity && i < this.count; i++) {
230
- this._removeParticle(i--, group, override) && deleted++;
237
+ if (this._removeParticle(i--, group, override)) {
238
+ deleted++;
239
+ }
231
240
  }
232
241
  }
233
242
  removeQuantity(quantity, group) {
234
- this.removeAt(0, quantity, group);
243
+ const defaultIndex = 0;
244
+ this.removeAt(defaultIndex, quantity, group);
235
245
  }
236
- setDensity() {
237
- const options = this._container.actualOptions, groups = options.particles.groups;
246
+ async setDensity() {
247
+ const options = this._container.actualOptions, groups = options.particles.groups, manualCount = 0;
238
248
  for (const group in groups) {
239
- this._applyDensity(groups[group], 0, group);
249
+ await this._applyDensity(groups[group], manualCount, group);
240
250
  }
241
- this._applyDensity(options.particles, options.manualParticles.length);
251
+ await this._applyDensity(options.particles, options.manualParticles.length);
242
252
  }
243
253
  setLastZIndex(zIndex) {
244
254
  this._lastZIndex = zIndex;
@@ -254,7 +264,7 @@ export class Particles {
254
264
  pathGenerator.update();
255
265
  }
256
266
  for (const [, plugin] of container.plugins) {
257
- plugin.update && (await plugin.update(delta));
267
+ await plugin.update?.(delta);
258
268
  }
259
269
  const resizeFactor = this._resizeFactor;
260
270
  for (const particle of this._array) {
@@ -265,15 +275,17 @@ export class Particles {
265
275
  particle.initialPosition.y *= resizeFactor.height;
266
276
  }
267
277
  particle.ignoresResizeRatio = false;
268
- await this._interactionManager.reset(particle);
278
+ this._interactionManager.reset(particle);
269
279
  for (const [, plugin] of this._container.plugins) {
270
280
  if (particle.destroyed) {
271
281
  break;
272
282
  }
273
- plugin.particleUpdate && plugin.particleUpdate(particle, delta);
283
+ plugin.particleUpdate?.(particle, delta);
274
284
  }
275
285
  for (const mover of this.movers) {
276
- mover.isEnabled(particle) && mover.move(particle, delta);
286
+ if (mover.isEnabled(particle)) {
287
+ await mover.move(particle, delta);
288
+ }
277
289
  }
278
290
  if (particle.destroyed) {
279
291
  particlesToDelete.add(particle);
@@ -298,7 +310,7 @@ export class Particles {
298
310
  await this._interactionManager.externalInteract(delta);
299
311
  for (const particle of this._array) {
300
312
  for (const updater of this.updaters) {
301
- updater.update(particle, delta);
313
+ await updater.update(particle, delta);
302
314
  }
303
315
  if (!particle.destroyed && !particle.spawning) {
304
316
  await this._interactionManager.particlesInteract(particle, delta);
@@ -308,7 +320,8 @@ export class Particles {
308
320
  if (this._needsSort) {
309
321
  const zArray = this._zArray;
310
322
  zArray.sort((a, b) => b.position.z - a.position.z || a.id - b.id);
311
- this._lastZIndex = zArray[zArray.length - 1].position.z;
323
+ const lengthOffset = 1;
324
+ this._lastZIndex = zArray[zArray.length - lengthOffset].position.z;
312
325
  this._needsSort = false;
313
326
  }
314
327
  }
@@ -1,15 +1,16 @@
1
1
  import { getRangeValue } from "../Utils/NumberUtils.js";
2
2
  import { isSsr } from "../Utils/Utils.js";
3
+ const defaultRatio = 1, defaultReduceFactor = 1;
3
4
  export class Retina {
4
5
  constructor(container) {
5
6
  this.container = container;
6
- this.pixelRatio = 1;
7
- this.reduceFactor = 1;
7
+ this.pixelRatio = defaultRatio;
8
+ this.reduceFactor = defaultReduceFactor;
8
9
  }
9
10
  init() {
10
11
  const container = this.container, options = container.actualOptions;
11
- this.pixelRatio = !options.detectRetina || isSsr() ? 1 : window.devicePixelRatio;
12
- this.reduceFactor = 1;
12
+ this.pixelRatio = !options.detectRetina || isSsr() ? defaultRatio : window.devicePixelRatio;
13
+ this.reduceFactor = defaultReduceFactor;
13
14
  const ratio = this.pixelRatio, canvas = container.canvas;
14
15
  if (canvas.element) {
15
16
  const element = canvas.element;
@@ -1,6 +1,7 @@
1
1
  import { Range } from "./Range.js";
2
2
  import { Rectangle } from "./Rectangle.js";
3
3
  import { getDistance } from "../../Utils/NumberUtils.js";
4
+ const squareExp = 2;
4
5
  export class Circle extends Range {
5
6
  constructor(x, y, radius) {
6
7
  super(x, y);
@@ -12,12 +13,12 @@ export class Circle extends Range {
12
13
  intersects(range) {
13
14
  const pos1 = this.position, pos2 = range.position, distPos = { x: Math.abs(pos2.x - pos1.x), y: Math.abs(pos2.y - pos1.y) }, r = this.radius;
14
15
  if (range instanceof Circle) {
15
- const rSum = r + range.radius, dist = Math.sqrt(distPos.x ** 2 + distPos.y ** 2);
16
+ const rSum = r + range.radius, dist = Math.sqrt(distPos.x ** squareExp + distPos.y ** squareExp);
16
17
  return rSum > dist;
17
18
  }
18
19
  else if (range instanceof Rectangle) {
19
- const { width, height } = range.size, edges = Math.pow(distPos.x - width, 2) + Math.pow(distPos.y - height, 2);
20
- return (edges <= r ** 2 ||
20
+ const { width, height } = range.size, edges = Math.pow(distPos.x - width, squareExp) + Math.pow(distPos.y - height, squareExp);
21
+ return (edges <= r ** squareExp ||
21
22
  (distPos.x <= r + width && distPos.y <= r + height) ||
22
23
  distPos.x <= width ||
23
24
  distPos.y <= height);
@@ -11,3 +11,6 @@ export const touchCancelEvent = "touchcancel";
11
11
  export const resizeEvent = "resize";
12
12
  export const visibilityChangeEvent = "visibilitychange";
13
13
  export const errorPrefix = "tsParticles - Error";
14
+ export const percentDenominator = 100;
15
+ export const halfRandom = 0.5;
16
+ export const millisecondsToSeconds = 1000;
@@ -1,5 +1,6 @@
1
1
  import { executeOnSingleOrMultiple, isBoolean, safeMatchMedia } from "../../Utils/Utils.js";
2
- import { mouseDownEvent, mouseLeaveEvent, mouseMoveEvent, mouseOutEvent, mouseUpEvent, resizeEvent, touchCancelEvent, touchEndEvent, touchMoveEvent, touchStartEvent, visibilityChangeEvent, } from "./Constants.js";
2
+ import { millisecondsToSeconds, mouseDownEvent, mouseLeaveEvent, mouseMoveEvent, mouseOutEvent, mouseUpEvent, resizeEvent, touchCancelEvent, touchEndEvent, touchMoveEvent, touchStartEvent, visibilityChangeEvent, } from "./Constants.js";
3
+ const double = 2;
3
4
  function manageListener(element, event, handler, add, options) {
4
5
  if (add) {
5
6
  let addOptions = { passive: true };
@@ -32,13 +33,14 @@ export class EventListeners {
32
33
  executeOnSingleOrMultiple(onClick.mode, (mode) => this.container.handleClickMode(mode));
33
34
  }
34
35
  if (e.type === "touchend") {
35
- setTimeout(() => this._mouseTouchFinish(), 500);
36
+ const touchDelay = 500;
37
+ setTimeout(() => this._mouseTouchFinish(), touchDelay);
36
38
  }
37
39
  };
38
40
  this._handleThemeChange = (e) => {
39
41
  const mediaEvent = e, container = this.container, options = container.options, defaultThemes = options.defaultThemes, themeName = mediaEvent.matches ? defaultThemes.dark : defaultThemes.light, theme = options.themes.find((theme) => theme.name === themeName);
40
42
  if (theme && theme.default.auto) {
41
- container.loadTheme(themeName);
43
+ void container.loadTheme(themeName);
42
44
  }
43
45
  };
44
46
  this._handleVisibilityChange = () => {
@@ -53,7 +55,7 @@ export class EventListeners {
53
55
  }
54
56
  else {
55
57
  container.pageHidden = false;
56
- if (container.getAnimationStatus()) {
58
+ if (container.animationStatus) {
57
59
  container.play(true);
58
60
  }
59
61
  else {
@@ -61,15 +63,16 @@ export class EventListeners {
61
63
  }
62
64
  }
63
65
  };
64
- this._handleWindowResize = async () => {
66
+ this._handleWindowResize = () => {
65
67
  if (this._resizeTimeout) {
66
68
  clearTimeout(this._resizeTimeout);
67
69
  delete this._resizeTimeout;
68
70
  }
69
- this._resizeTimeout = setTimeout(async () => {
71
+ const handleResize = async () => {
70
72
  const canvas = this.container.canvas;
71
- canvas && (await canvas.windowResize());
72
- }, this.container.actualOptions.interactivity.events.resize.delay * 1000);
73
+ await canvas?.windowResize();
74
+ };
75
+ this._resizeTimeout = setTimeout(() => void handleResize(), this.container.actualOptions.interactivity.events.resize.delay * millisecondsToSeconds);
73
76
  };
74
77
  this._manageInteractivityListeners = (mouseLeaveTmpEvent, add) => {
75
78
  const handlers = this._handlers, container = this.container, options = container.actualOptions;
@@ -155,12 +158,12 @@ export class EventListeners {
155
158
  delete this._resizeObserver;
156
159
  }
157
160
  else if (!this._resizeObserver && add && canvasEl) {
158
- this._resizeObserver = new ResizeObserver(async (entries) => {
161
+ this._resizeObserver = new ResizeObserver((entries) => {
159
162
  const entry = entries.find((e) => e.target === canvasEl);
160
163
  if (!entry) {
161
164
  return;
162
165
  }
163
- await this._handleWindowResize();
166
+ this._handleWindowResize();
164
167
  });
165
168
  this._resizeObserver.observe(canvasEl);
166
169
  }
@@ -211,7 +214,7 @@ export class EventListeners {
211
214
  };
212
215
  this._mouseTouchMove = (e) => {
213
216
  const container = this.container, options = container.actualOptions, interactivity = container.interactivity, canvasEl = container.canvas.element;
214
- if (!interactivity || !interactivity.element) {
217
+ if (!interactivity?.element) {
215
218
  return;
216
219
  }
217
220
  interactivity.mouse.inside = true;
@@ -233,8 +236,8 @@ export class EventListeners {
233
236
  if (source && target && canvasEl) {
234
237
  const sourceRect = source.getBoundingClientRect(), targetRect = target.getBoundingClientRect(), canvasRect = canvasEl.getBoundingClientRect();
235
238
  pos = {
236
- x: mouseEvent.offsetX + 2 * sourceRect.left - (targetRect.left + canvasRect.left),
237
- y: mouseEvent.offsetY + 2 * sourceRect.top - (targetRect.top + canvasRect.top),
239
+ x: mouseEvent.offsetX + double * sourceRect.left - (targetRect.left + canvasRect.left),
240
+ y: mouseEvent.offsetY + double * sourceRect.top - (targetRect.top + canvasRect.top),
238
241
  };
239
242
  }
240
243
  else {
@@ -254,10 +257,10 @@ export class EventListeners {
254
257
  else {
255
258
  this._canPush = e.type !== "touchmove";
256
259
  if (canvasEl) {
257
- const touchEvent = e, lastTouch = touchEvent.touches[touchEvent.touches.length - 1], canvasRect = canvasEl.getBoundingClientRect();
260
+ const touchEvent = e, lengthOffset = 1, lastTouch = touchEvent.touches[touchEvent.touches.length - lengthOffset], canvasRect = canvasEl.getBoundingClientRect(), defaultCoordinate = 0;
258
261
  pos = {
259
- x: lastTouch.clientX - (canvasRect.left ?? 0),
260
- y: lastTouch.clientY - (canvasRect.top ?? 0),
262
+ x: lastTouch.clientX - (canvasRect.left ?? defaultCoordinate),
263
+ y: lastTouch.clientY - (canvasRect.top ?? defaultCoordinate),
261
264
  };
262
265
  }
263
266
  }
@@ -1,6 +1,6 @@
1
1
  export class ExternalInteractorBase {
2
2
  constructor(container) {
3
- this.container = container;
4
3
  this.type = "external";
4
+ this.container = container;
5
5
  }
6
6
  }
@@ -2,21 +2,24 @@ export class InteractionManager {
2
2
  constructor(engine, container) {
3
3
  this.container = container;
4
4
  this._engine = engine;
5
- this._interactors = engine.getInteractors(this.container, true);
5
+ this._interactors = [];
6
6
  this._externalInteractors = [];
7
7
  this._particleInteractors = [];
8
8
  }
9
9
  async externalInteract(delta) {
10
10
  for (const interactor of this._externalInteractors) {
11
- interactor.isEnabled() && (await interactor.interact(delta));
11
+ if (interactor.isEnabled()) {
12
+ await interactor.interact(delta);
13
+ }
12
14
  }
13
15
  }
14
16
  handleClickMode(mode) {
15
17
  for (const interactor of this._externalInteractors) {
16
- interactor.handleClickMode && interactor.handleClickMode(mode);
18
+ interactor.handleClickMode?.(mode);
17
19
  }
18
20
  }
19
- init() {
21
+ async init() {
22
+ this._interactors = await this._engine.getInteractors(this.container, true);
20
23
  this._externalInteractors = [];
21
24
  this._particleInteractors = [];
22
25
  for (const interactor of this._interactors) {
@@ -36,15 +39,21 @@ export class InteractionManager {
36
39
  interactor.clear(particle, delta);
37
40
  }
38
41
  for (const interactor of this._particleInteractors) {
39
- interactor.isEnabled(particle) && (await interactor.interact(particle, delta));
42
+ if (interactor.isEnabled(particle)) {
43
+ await interactor.interact(particle, delta);
44
+ }
40
45
  }
41
46
  }
42
- async reset(particle) {
47
+ reset(particle) {
43
48
  for (const interactor of this._externalInteractors) {
44
- interactor.isEnabled() && interactor.reset(particle);
49
+ if (interactor.isEnabled()) {
50
+ interactor.reset(particle);
51
+ }
45
52
  }
46
53
  for (const interactor of this._particleInteractors) {
47
- interactor.isEnabled(particle) && interactor.reset(particle);
54
+ if (interactor.isEnabled(particle)) {
55
+ interactor.reset(particle);
56
+ }
48
57
  }
49
58
  }
50
59
  }
@@ -1,6 +1,6 @@
1
1
  export class ParticlesInteractorBase {
2
2
  constructor(container) {
3
- this.container = container;
4
3
  this.type = "particles";
4
+ this.container = container;
5
5
  }
6
6
  }
@@ -1,14 +1,16 @@
1
1
  import { Circle } from "./Circle.js";
2
2
  import { Rectangle } from "./Rectangle.js";
3
3
  import { getDistance } from "../../Utils/NumberUtils.js";
4
+ const half = 0.5, double = 2, subdivideCount = 4;
4
5
  export class QuadTree {
5
6
  constructor(rectangle, capacity) {
6
7
  this.rectangle = rectangle;
7
8
  this.capacity = capacity;
8
9
  this._subdivide = () => {
9
10
  const { x, y } = this.rectangle.position, { width, height } = this.rectangle.size, { capacity } = this;
10
- for (let i = 0; i < 4; i++) {
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));
11
+ for (let i = 0; i < subdivideCount; i++) {
12
+ const fixedIndex = i % double;
13
+ this._subs.push(new QuadTree(new Rectangle(x + width * half * fixedIndex, y + height * half * (Math.round(i * half) - fixedIndex), width * half, height * half), capacity));
12
14
  }
13
15
  this._divided = true;
14
16
  };
@@ -30,7 +32,7 @@ export class QuadTree {
30
32
  return this._subs.some((sub) => sub.insert(point));
31
33
  }
32
34
  query(range, check, found) {
33
- const res = found || [];
35
+ const res = found ?? [];
34
36
  if (!range.intersects(this.rectangle)) {
35
37
  return [];
36
38
  }
@@ -1,10 +1,15 @@
1
1
  import { Vector3d } from "./Vector3d.js";
2
+ const origin = {
3
+ x: 0,
4
+ y: 0,
5
+ z: 0,
6
+ };
2
7
  export class Vector extends Vector3d {
3
8
  constructor(xOrCoords, y) {
4
- super(xOrCoords, y, 0);
9
+ super(xOrCoords, y, origin.z);
5
10
  }
6
11
  static get origin() {
7
- return Vector.create(0, 0);
12
+ return Vector.create(origin.x, origin.y);
8
13
  }
9
14
  static clone(source) {
10
15
  return Vector.create(source.x, source.y);
@@ -1,5 +1,10 @@
1
1
  import { errorPrefix } from "./Constants.js";
2
2
  import { isNumber } from "../../Utils/Utils.js";
3
+ const origin = {
4
+ x: 0,
5
+ y: 0,
6
+ z: 0,
7
+ }, squareExp = 2, inverseFactorNumerator = 1.0;
3
8
  export class Vector3d {
4
9
  constructor(xOrCoords, y, z) {
5
10
  this._updateFromAngle = (angle, length) => {
@@ -10,19 +15,19 @@ export class Vector3d {
10
15
  this.x = xOrCoords.x;
11
16
  this.y = xOrCoords.y;
12
17
  const coords3d = xOrCoords;
13
- this.z = coords3d.z ? coords3d.z : 0;
18
+ this.z = coords3d.z ? coords3d.z : origin.z;
14
19
  }
15
20
  else if (xOrCoords !== undefined && y !== undefined) {
16
21
  this.x = xOrCoords;
17
22
  this.y = y;
18
- this.z = z ?? 0;
23
+ this.z = z ?? origin.z;
19
24
  }
20
25
  else {
21
26
  throw new Error(`${errorPrefix} Vector3d not initialized correctly`);
22
27
  }
23
28
  }
24
29
  static get origin() {
25
- return Vector3d.create(0, 0, 0);
30
+ return Vector3d.create(origin.x, origin.y, origin.z);
26
31
  }
27
32
  get angle() {
28
33
  return Math.atan2(this.y, this.x);
@@ -68,7 +73,7 @@ export class Vector3d {
68
73
  this.z /= n;
69
74
  }
70
75
  getLengthSq() {
71
- return this.x ** 2 + this.y ** 2;
76
+ return this.x ** squareExp + this.y ** squareExp;
72
77
  }
73
78
  mult(n) {
74
79
  return Vector3d.create(this.x * n, this.y * n, this.z * n);
@@ -79,19 +84,19 @@ export class Vector3d {
79
84
  this.z *= n;
80
85
  }
81
86
  normalize() {
82
- const length = this.length;
83
- if (length != 0) {
84
- this.multTo(1.0 / length);
87
+ const length = this.length, noLength = 0;
88
+ if (length != noLength) {
89
+ this.multTo(inverseFactorNumerator / length);
85
90
  }
86
91
  }
87
92
  rotate(angle) {
88
- return Vector3d.create(this.x * Math.cos(angle) - this.y * Math.sin(angle), this.x * Math.sin(angle) + this.y * Math.cos(angle), 0);
93
+ return Vector3d.create(this.x * Math.cos(angle) - this.y * Math.sin(angle), this.x * Math.sin(angle) + this.y * Math.cos(angle), origin.z);
89
94
  }
90
95
  setTo(c) {
91
96
  this.x = c.x;
92
97
  this.y = c.y;
93
98
  const v3d = c;
94
- this.z = v3d.z ? v3d.z : 0;
99
+ this.z = v3d.z ? v3d.z : origin.z;
95
100
  }
96
101
  sub(v) {
97
102
  return Vector3d.create(this.x - v.x, this.y - v.y, this.z - v.z);
@@ -15,7 +15,7 @@ export class BackgroundMask {
15
15
  }
16
16
  if (data.cover !== undefined) {
17
17
  const cover = data.cover, color = (isString(data.cover) ? { color: data.cover } : data.cover);
18
- this.cover.load(cover.color !== undefined ? cover : { color: color });
18
+ this.cover.load(cover.color !== undefined || cover.image !== undefined ? cover : { color: color });
19
19
  }
20
20
  if (data.enable !== undefined) {
21
21
  this.enable = data.enable;