@tsparticles/engine 3.0.2 → 3.1.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.
- package/README.md +283 -163
- package/browser/Core/Canvas.js +19 -19
- package/browser/Core/Container.js +45 -34
- package/browser/Core/Engine.js +36 -20
- package/browser/Core/Particle.js +35 -36
- package/browser/Core/Particles.js +30 -24
- package/browser/Core/Retina.js +5 -4
- package/browser/Core/Utils/Circle.js +4 -3
- package/browser/Core/Utils/Constants.js +3 -0
- package/browser/Core/Utils/EventListeners.js +18 -15
- package/browser/Core/Utils/ExternalInteractorBase.js +1 -1
- package/browser/Core/Utils/InteractionManager.js +14 -6
- package/browser/Core/Utils/ParticlesInteractorBase.js +1 -1
- package/browser/Core/Utils/QuadTree.js +5 -3
- package/browser/Core/Utils/Vector.js +7 -2
- package/browser/Core/Utils/Vector3d.js +14 -9
- package/browser/Options/Classes/ManualParticle.js +3 -2
- package/browser/Options/Classes/Options.js +3 -0
- package/browser/Utils/CanvasUtils.js +36 -26
- package/browser/Utils/ColorUtils.js +124 -45
- package/browser/Utils/EventDispatcher.js +6 -5
- package/browser/Utils/HslColorManager.js +5 -5
- package/browser/Utils/NumberUtils.js +35 -23
- package/browser/Utils/RgbColorManager.js +5 -5
- package/browser/Utils/Utils.js +102 -19
- package/cjs/Core/Canvas.js +19 -19
- package/cjs/Core/Container.js +45 -34
- package/cjs/Core/Engine.js +36 -20
- package/cjs/Core/Particle.js +34 -35
- package/cjs/Core/Particles.js +30 -24
- package/cjs/Core/Retina.js +5 -4
- package/cjs/Core/Utils/Circle.js +4 -3
- package/cjs/Core/Utils/Constants.js +4 -1
- package/cjs/Core/Utils/EventListeners.js +17 -14
- package/cjs/Core/Utils/ExternalInteractorBase.js +1 -1
- package/cjs/Core/Utils/InteractionManager.js +14 -6
- package/cjs/Core/Utils/ParticlesInteractorBase.js +1 -1
- package/cjs/Core/Utils/QuadTree.js +5 -3
- package/cjs/Core/Utils/Vector.js +7 -2
- package/cjs/Core/Utils/Vector3d.js +14 -9
- package/cjs/Options/Classes/ManualParticle.js +3 -2
- package/cjs/Options/Classes/Options.js +3 -0
- package/cjs/Utils/CanvasUtils.js +36 -26
- package/cjs/Utils/ColorUtils.js +126 -45
- package/cjs/Utils/EventDispatcher.js +6 -5
- package/cjs/Utils/HslColorManager.js +5 -5
- package/cjs/Utils/NumberUtils.js +37 -24
- package/cjs/Utils/RgbColorManager.js +5 -5
- package/cjs/Utils/Utils.js +103 -19
- package/esm/Core/Canvas.js +19 -19
- package/esm/Core/Container.js +45 -34
- package/esm/Core/Engine.js +36 -20
- package/esm/Core/Particle.js +35 -36
- package/esm/Core/Particles.js +30 -24
- package/esm/Core/Retina.js +5 -4
- package/esm/Core/Utils/Circle.js +4 -3
- package/esm/Core/Utils/Constants.js +3 -0
- package/esm/Core/Utils/EventListeners.js +18 -15
- package/esm/Core/Utils/ExternalInteractorBase.js +1 -1
- package/esm/Core/Utils/InteractionManager.js +14 -6
- package/esm/Core/Utils/ParticlesInteractorBase.js +1 -1
- package/esm/Core/Utils/QuadTree.js +5 -3
- package/esm/Core/Utils/Vector.js +7 -2
- package/esm/Core/Utils/Vector3d.js +14 -9
- package/esm/Options/Classes/ManualParticle.js +3 -2
- package/esm/Options/Classes/Options.js +3 -0
- package/esm/Utils/CanvasUtils.js +36 -26
- package/esm/Utils/ColorUtils.js +124 -45
- package/esm/Utils/EventDispatcher.js +6 -5
- package/esm/Utils/HslColorManager.js +5 -5
- package/esm/Utils/NumberUtils.js +35 -23
- package/esm/Utils/RgbColorManager.js +5 -5
- package/esm/Utils/Utils.js +102 -19
- package/package.json +1 -1
- package/report.html +2 -2
- package/tsparticles.engine.js +693 -334
- package/tsparticles.engine.min.js +1 -1
- package/tsparticles.engine.min.js.LICENSE.txt +1 -1
- package/types/Core/Interfaces/IParticleHslAnimation.d.ts +4 -4
- package/types/Core/Interfaces/IParticleValueAnimation.d.ts +4 -0
- package/types/Core/Interfaces/IShapeDrawData.d.ts +2 -2
- package/types/Core/Utils/Constants.d.ts +3 -0
- package/types/Core/Utils/ExternalInteractorBase.d.ts +1 -1
- package/types/Core/Utils/InteractionManager.d.ts +1 -1
- package/types/Core/Utils/ParticlesInteractorBase.d.ts +1 -1
- package/types/Core/Utils/Point.d.ts +1 -1
- package/types/Options/Classes/Options.d.ts +1 -0
- package/types/Options/Classes/Particles/Move/Move.d.ts +1 -2
- package/types/Options/Classes/Particles/Move/OutModes.d.ts +1 -2
- package/types/Options/Interfaces/IOptions.d.ts +1 -0
- package/types/Options/Interfaces/Interactivity/Modes/IModes.d.ts +1 -3
- package/types/Types/CustomEventArgs.d.ts +2 -2
- package/types/Types/ExportResult.d.ts +2 -2
- package/types/Types/ParticlesGroups.d.ts +1 -3
- package/types/Types/PathOptions.d.ts +1 -3
- package/types/Types/ShapeData.d.ts +1 -3
- package/types/Utils/CanvasUtils.d.ts +3 -2
- package/types/Utils/ColorUtils.d.ts +5 -0
- package/types/Utils/NumberUtils.d.ts +2 -2
- package/types/Utils/Utils.d.ts +9 -6
- package/umd/Core/Canvas.js +19 -19
- package/umd/Core/Container.js +46 -35
- package/umd/Core/Engine.js +36 -20
- package/umd/Core/Particle.js +35 -36
- package/umd/Core/Particles.js +30 -24
- package/umd/Core/Retina.js +5 -4
- package/umd/Core/Utils/Circle.js +4 -3
- package/umd/Core/Utils/Constants.js +4 -1
- package/umd/Core/Utils/EventListeners.js +17 -14
- package/umd/Core/Utils/ExternalInteractorBase.js +1 -1
- package/umd/Core/Utils/InteractionManager.js +14 -6
- package/umd/Core/Utils/ParticlesInteractorBase.js +1 -1
- package/umd/Core/Utils/QuadTree.js +5 -3
- package/umd/Core/Utils/Vector.js +7 -2
- package/umd/Core/Utils/Vector3d.js +14 -9
- package/umd/Options/Classes/ManualParticle.js +3 -2
- package/umd/Options/Classes/Options.js +3 -0
- package/umd/Utils/CanvasUtils.js +36 -26
- package/umd/Utils/ColorUtils.js +127 -46
- package/umd/Utils/EventDispatcher.js +6 -5
- package/umd/Utils/HslColorManager.js +5 -5
- package/umd/Utils/NumberUtils.js +38 -25
- package/umd/Utils/RgbColorManager.js +5 -5
- package/umd/Utils/Utils.js +104 -20
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { errorPrefix, millisecondsToSeconds } from "./Utils/Constants.js";
|
|
1
2
|
import { getLogger, safeIntersectionObserver } from "../Utils/Utils.js";
|
|
2
3
|
import { Canvas } from "./Canvas.js";
|
|
3
4
|
import { EventListeners } from "./Utils/EventListeners.js";
|
|
4
5
|
import { Options } from "../Options/Classes/Options.js";
|
|
5
6
|
import { Particles } from "./Particles.js";
|
|
6
7
|
import { Retina } from "./Retina.js";
|
|
7
|
-
import { errorPrefix } from "./Utils/Constants.js";
|
|
8
8
|
import { getRangeValue } from "../Utils/NumberUtils.js";
|
|
9
9
|
import { loadOptions } from "../Utils/OptionsUtils.js";
|
|
10
10
|
function guardCheck(container) {
|
|
11
11
|
return container && !container.destroyed;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
const defaultFps = 60;
|
|
14
|
+
function initDelta(value, fpsLimit = defaultFps, smooth = false) {
|
|
14
15
|
return {
|
|
15
16
|
value,
|
|
16
|
-
factor: smooth ?
|
|
17
|
+
factor: smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds,
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
function loadContainerOptions(engine, container, ...sourceOptionsArr) {
|
|
@@ -31,14 +32,19 @@ export class Container {
|
|
|
31
32
|
if (entry.target !== this.interactivity.element) {
|
|
32
33
|
continue;
|
|
33
34
|
}
|
|
34
|
-
(entry.isIntersecting
|
|
35
|
+
if (entry.isIntersecting) {
|
|
36
|
+
this.play();
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.pause();
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
};
|
|
37
43
|
this._nextFrame = async (timestamp) => {
|
|
38
44
|
try {
|
|
39
45
|
if (!this._smooth &&
|
|
40
46
|
this._lastFrameTime !== undefined &&
|
|
41
|
-
timestamp < this._lastFrameTime +
|
|
47
|
+
timestamp < this._lastFrameTime + millisecondsToSeconds / this.fpsLimit) {
|
|
42
48
|
this.draw(false);
|
|
43
49
|
return;
|
|
44
50
|
}
|
|
@@ -46,7 +52,7 @@ export class Container {
|
|
|
46
52
|
const delta = initDelta(timestamp - this._lastFrameTime, this.fpsLimit, this._smooth);
|
|
47
53
|
this.addLifeTime(delta.value);
|
|
48
54
|
this._lastFrameTime = timestamp;
|
|
49
|
-
if (delta.value >
|
|
55
|
+
if (delta.value > millisecondsToSeconds) {
|
|
50
56
|
this.draw(false);
|
|
51
57
|
return;
|
|
52
58
|
}
|
|
@@ -129,8 +135,8 @@ export class Container {
|
|
|
129
135
|
const mouseEvent = e, pos = {
|
|
130
136
|
x: mouseEvent.offsetX || mouseEvent.clientX,
|
|
131
137
|
y: mouseEvent.offsetY || mouseEvent.clientY,
|
|
132
|
-
};
|
|
133
|
-
clickOrTouchHandler(e, pos,
|
|
138
|
+
}, radius = 1;
|
|
139
|
+
clickOrTouchHandler(e, pos, radius);
|
|
134
140
|
};
|
|
135
141
|
const touchStartHandler = () => {
|
|
136
142
|
if (!guardCheck(this)) {
|
|
@@ -151,16 +157,17 @@ export class Container {
|
|
|
151
157
|
}
|
|
152
158
|
if (touched && !touchMoved) {
|
|
153
159
|
const touchEvent = e;
|
|
154
|
-
|
|
160
|
+
const lengthOffset = 1;
|
|
161
|
+
let lastTouch = touchEvent.touches[touchEvent.touches.length - lengthOffset];
|
|
155
162
|
if (!lastTouch) {
|
|
156
|
-
lastTouch = touchEvent.changedTouches[touchEvent.changedTouches.length -
|
|
163
|
+
lastTouch = touchEvent.changedTouches[touchEvent.changedTouches.length - lengthOffset];
|
|
157
164
|
if (!lastTouch) {
|
|
158
165
|
return;
|
|
159
166
|
}
|
|
160
167
|
}
|
|
161
|
-
const element = this.canvas.element, canvasRect = element ? element.getBoundingClientRect() : undefined, pos = {
|
|
162
|
-
x: lastTouch.clientX - (canvasRect ? canvasRect.left :
|
|
163
|
-
y: lastTouch.clientY - (canvasRect ? canvasRect.top :
|
|
168
|
+
const element = this.canvas.element, canvasRect = element ? element.getBoundingClientRect() : undefined, minCoordinate = 0, pos = {
|
|
169
|
+
x: lastTouch.clientX - (canvasRect ? canvasRect.left : minCoordinate),
|
|
170
|
+
y: lastTouch.clientY - (canvasRect ? canvasRect.top : minCoordinate),
|
|
164
171
|
};
|
|
165
172
|
clickOrTouchHandler(e, pos, Math.max(lastTouch.radiusX, lastTouch.radiusY));
|
|
166
173
|
}
|
|
@@ -202,10 +209,10 @@ export class Container {
|
|
|
202
209
|
this.particles.destroy();
|
|
203
210
|
this.canvas.destroy();
|
|
204
211
|
for (const [, effectDrawer] of this.effectDrawers) {
|
|
205
|
-
effectDrawer.destroy
|
|
212
|
+
effectDrawer.destroy?.(this);
|
|
206
213
|
}
|
|
207
214
|
for (const [, shapeDrawer] of this.shapeDrawers) {
|
|
208
|
-
shapeDrawer.destroy
|
|
215
|
+
shapeDrawer.destroy?.(this);
|
|
209
216
|
}
|
|
210
217
|
for (const key of this.effectDrawers.keys()) {
|
|
211
218
|
this.effectDrawers.delete(key);
|
|
@@ -215,9 +222,10 @@ export class Container {
|
|
|
215
222
|
}
|
|
216
223
|
this._engine.clearPlugins(this);
|
|
217
224
|
this.destroyed = true;
|
|
218
|
-
const mainArr = this._engine.dom(), idx = mainArr.findIndex((t) => t === this);
|
|
219
|
-
if (idx >=
|
|
220
|
-
|
|
225
|
+
const mainArr = this._engine.dom(), idx = mainArr.findIndex((t) => t === this), minIndex = 0;
|
|
226
|
+
if (idx >= minIndex) {
|
|
227
|
+
const deleteCount = 1;
|
|
228
|
+
mainArr.splice(idx, deleteCount);
|
|
221
229
|
}
|
|
222
230
|
this._engine.dispatchEvent("containerDestroyed", { container: this });
|
|
223
231
|
}
|
|
@@ -226,13 +234,14 @@ export class Container {
|
|
|
226
234
|
return;
|
|
227
235
|
}
|
|
228
236
|
let refreshTime = force;
|
|
229
|
-
|
|
237
|
+
const frame = async (timestamp) => {
|
|
230
238
|
if (refreshTime) {
|
|
231
239
|
this._lastFrameTime = undefined;
|
|
232
240
|
refreshTime = false;
|
|
233
241
|
}
|
|
234
242
|
await this._nextFrame(timestamp);
|
|
235
|
-
}
|
|
243
|
+
};
|
|
244
|
+
this._drawAnimationFrame = requestAnimationFrame((timestamp) => void frame(timestamp));
|
|
236
245
|
}
|
|
237
246
|
async export(type, options = {}) {
|
|
238
247
|
for (const [, plugin] of this.plugins) {
|
|
@@ -256,7 +265,7 @@ export class Container {
|
|
|
256
265
|
}
|
|
257
266
|
this.particles.handleClickMode(mode);
|
|
258
267
|
for (const [, plugin] of this.plugins) {
|
|
259
|
-
plugin.handleClickMode
|
|
268
|
+
plugin.handleClickMode?.(mode);
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
271
|
async init() {
|
|
@@ -289,25 +298,26 @@ export class Container {
|
|
|
289
298
|
this.canvas.initBackground();
|
|
290
299
|
this.canvas.resize();
|
|
291
300
|
this.zLayers = this.actualOptions.zLayers;
|
|
292
|
-
this._duration = getRangeValue(this.actualOptions.duration) *
|
|
293
|
-
this._delay = getRangeValue(this.actualOptions.delay) *
|
|
301
|
+
this._duration = getRangeValue(this.actualOptions.duration) * millisecondsToSeconds;
|
|
302
|
+
this._delay = getRangeValue(this.actualOptions.delay) * millisecondsToSeconds;
|
|
294
303
|
this._lifeTime = 0;
|
|
295
|
-
|
|
304
|
+
const defaultFpsLimit = 120, minFpsLimit = 0;
|
|
305
|
+
this.fpsLimit = this.actualOptions.fpsLimit > minFpsLimit ? this.actualOptions.fpsLimit : defaultFpsLimit;
|
|
296
306
|
this._smooth = this.actualOptions.smooth;
|
|
297
307
|
for (const [, drawer] of this.effectDrawers) {
|
|
298
|
-
|
|
308
|
+
await drawer.init?.(this);
|
|
299
309
|
}
|
|
300
310
|
for (const [, drawer] of this.shapeDrawers) {
|
|
301
|
-
|
|
311
|
+
await drawer.init?.(this);
|
|
302
312
|
}
|
|
303
313
|
for (const [, plugin] of this.plugins) {
|
|
304
|
-
|
|
314
|
+
await plugin.init?.();
|
|
305
315
|
}
|
|
306
316
|
this._engine.dispatchEvent("containerInit", { container: this });
|
|
307
317
|
this.particles.init();
|
|
308
318
|
this.particles.setDensity();
|
|
309
319
|
for (const [, plugin] of this.plugins) {
|
|
310
|
-
plugin.particlesSetup
|
|
320
|
+
plugin.particlesSetup?.();
|
|
311
321
|
}
|
|
312
322
|
this._engine.dispatchEvent("particlesSetup", { container: this });
|
|
313
323
|
}
|
|
@@ -330,7 +340,7 @@ export class Container {
|
|
|
330
340
|
return;
|
|
331
341
|
}
|
|
332
342
|
for (const [, plugin] of this.plugins) {
|
|
333
|
-
plugin.pause
|
|
343
|
+
plugin.pause?.();
|
|
334
344
|
}
|
|
335
345
|
if (!this.pageHidden) {
|
|
336
346
|
this._paused = true;
|
|
@@ -357,7 +367,7 @@ export class Container {
|
|
|
357
367
|
}
|
|
358
368
|
}
|
|
359
369
|
this._engine.dispatchEvent("containerPlay", { container: this });
|
|
360
|
-
this.draw(needsUpdate
|
|
370
|
+
this.draw(needsUpdate ?? false);
|
|
361
371
|
}
|
|
362
372
|
async refresh() {
|
|
363
373
|
if (!guardCheck(this)) {
|
|
@@ -382,18 +392,19 @@ export class Container {
|
|
|
382
392
|
await this.init();
|
|
383
393
|
this.started = true;
|
|
384
394
|
await new Promise((resolve) => {
|
|
385
|
-
|
|
395
|
+
const start = async () => {
|
|
386
396
|
this._eventListeners.addListeners();
|
|
387
397
|
if (this.interactivity.element instanceof HTMLElement && this._intersectionObserver) {
|
|
388
398
|
this._intersectionObserver.observe(this.interactivity.element);
|
|
389
399
|
}
|
|
390
400
|
for (const [, plugin] of this.plugins) {
|
|
391
|
-
|
|
401
|
+
await plugin.start?.();
|
|
392
402
|
}
|
|
393
403
|
this._engine.dispatchEvent("containerStarted", { container: this });
|
|
394
404
|
this.play();
|
|
395
405
|
resolve();
|
|
396
|
-
}
|
|
406
|
+
};
|
|
407
|
+
this._delayTimeout = setTimeout(() => void start(), this._delay);
|
|
397
408
|
});
|
|
398
409
|
}
|
|
399
410
|
stop() {
|
|
@@ -414,7 +425,7 @@ export class Container {
|
|
|
414
425
|
this._intersectionObserver.unobserve(this.interactivity.element);
|
|
415
426
|
}
|
|
416
427
|
for (const [, plugin] of this.plugins) {
|
|
417
|
-
plugin.stop
|
|
428
|
+
plugin.stop?.();
|
|
418
429
|
}
|
|
419
430
|
for (const key of this.plugins.keys()) {
|
|
420
431
|
this.plugins.delete(key);
|
package/browser/Core/Engine.js
CHANGED
|
@@ -51,16 +51,18 @@ export class Engine {
|
|
|
51
51
|
return res;
|
|
52
52
|
}
|
|
53
53
|
get version() {
|
|
54
|
-
return "3.0
|
|
54
|
+
return "3.1.0";
|
|
55
55
|
}
|
|
56
56
|
addConfig(config) {
|
|
57
|
-
const
|
|
58
|
-
this._configs.set(
|
|
59
|
-
this._eventDispatcher.dispatchEvent("configAdded", { data: { name, config } });
|
|
57
|
+
const key = config.key ?? config.name ?? "default";
|
|
58
|
+
this._configs.set(key, config);
|
|
59
|
+
this._eventDispatcher.dispatchEvent("configAdded", { data: { name: key, config } });
|
|
60
60
|
}
|
|
61
61
|
async addEffect(effect, drawer, refresh = true) {
|
|
62
62
|
executeOnSingleOrMultiple(effect, (type) => {
|
|
63
|
-
!this.getEffectDrawer(type)
|
|
63
|
+
if (!this.getEffectDrawer(type)) {
|
|
64
|
+
this.effectDrawers.set(type, drawer);
|
|
65
|
+
}
|
|
64
66
|
});
|
|
65
67
|
await this.refresh(refresh);
|
|
66
68
|
}
|
|
@@ -80,20 +82,28 @@ export class Engine {
|
|
|
80
82
|
await this.refresh(refresh);
|
|
81
83
|
}
|
|
82
84
|
async addPathGenerator(name, generator, refresh = true) {
|
|
83
|
-
!this.getPathGenerator(name)
|
|
85
|
+
if (!this.getPathGenerator(name)) {
|
|
86
|
+
this.pathGenerators.set(name, generator);
|
|
87
|
+
}
|
|
84
88
|
await this.refresh(refresh);
|
|
85
89
|
}
|
|
86
90
|
async addPlugin(plugin, refresh = true) {
|
|
87
|
-
!this.getPlugin(plugin.id)
|
|
91
|
+
if (!this.getPlugin(plugin.id)) {
|
|
92
|
+
this.plugins.push(plugin);
|
|
93
|
+
}
|
|
88
94
|
await this.refresh(refresh);
|
|
89
95
|
}
|
|
90
96
|
async addPreset(preset, options, override = false, refresh = true) {
|
|
91
|
-
(override || !this.getPreset(preset))
|
|
97
|
+
if (override || !this.getPreset(preset)) {
|
|
98
|
+
this.presets.set(preset, options);
|
|
99
|
+
}
|
|
92
100
|
await this.refresh(refresh);
|
|
93
101
|
}
|
|
94
102
|
async addShape(shape, drawer, refresh = true) {
|
|
95
103
|
executeOnSingleOrMultiple(shape, (type) => {
|
|
96
|
-
!this.getShapeDrawer(type)
|
|
104
|
+
if (!this.getShapeDrawer(type)) {
|
|
105
|
+
this.shapeDrawers.set(type, drawer);
|
|
106
|
+
}
|
|
97
107
|
});
|
|
98
108
|
await this.refresh(refresh);
|
|
99
109
|
}
|
|
@@ -111,7 +121,8 @@ export class Engine {
|
|
|
111
121
|
domItem(index) {
|
|
112
122
|
const dom = this.dom(), item = dom[index];
|
|
113
123
|
if (!item || item.destroyed) {
|
|
114
|
-
|
|
124
|
+
const deleteCount = 1;
|
|
125
|
+
dom.splice(index, deleteCount);
|
|
115
126
|
return;
|
|
116
127
|
}
|
|
117
128
|
return item;
|
|
@@ -119,7 +130,9 @@ export class Engine {
|
|
|
119
130
|
getAvailablePlugins(container) {
|
|
120
131
|
const res = new Map();
|
|
121
132
|
for (const plugin of this.plugins) {
|
|
122
|
-
plugin.needsPlugin(container.actualOptions)
|
|
133
|
+
if (plugin.needsPlugin(container.actualOptions)) {
|
|
134
|
+
res.set(plugin.id, plugin.getPlugin(container));
|
|
135
|
+
}
|
|
123
136
|
}
|
|
124
137
|
return res;
|
|
125
138
|
}
|
|
@@ -160,19 +173,20 @@ export class Engine {
|
|
|
160
173
|
this._initialized = true;
|
|
161
174
|
}
|
|
162
175
|
async load(params) {
|
|
163
|
-
const id = params.id ?? `tsparticles${Math.floor(getRandom() *
|
|
176
|
+
const randomFactor = 10000, id = params.id ?? params.element?.id ?? `tsparticles${Math.floor(getRandom() * randomFactor)}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options;
|
|
164
177
|
let domContainer = params.element ?? document.getElementById(id);
|
|
165
178
|
if (!domContainer) {
|
|
166
179
|
domContainer = document.createElement("div");
|
|
167
180
|
domContainer.id = id;
|
|
168
181
|
document.body.append(domContainer);
|
|
169
182
|
}
|
|
170
|
-
const currentOptions = itemFromSingleOrMultiple(options, index), dom = this.dom(), oldIndex = dom.findIndex((v) => v.id.description === id);
|
|
171
|
-
if (oldIndex >=
|
|
183
|
+
const currentOptions = itemFromSingleOrMultiple(options, index), dom = this.dom(), oldIndex = dom.findIndex((v) => v.id.description === id), minIndex = 0;
|
|
184
|
+
if (oldIndex >= minIndex) {
|
|
172
185
|
const old = this.domItem(oldIndex);
|
|
173
186
|
if (old && !old.destroyed) {
|
|
174
187
|
old.destroy();
|
|
175
|
-
|
|
188
|
+
const deleteCount = 1;
|
|
189
|
+
dom.splice(oldIndex, deleteCount);
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
192
|
let canvasEl;
|
|
@@ -183,7 +197,8 @@ export class Engine {
|
|
|
183
197
|
else {
|
|
184
198
|
const existingCanvases = domContainer.getElementsByTagName("canvas");
|
|
185
199
|
if (existingCanvases.length) {
|
|
186
|
-
|
|
200
|
+
const firstIndex = 0;
|
|
201
|
+
canvasEl = existingCanvases[firstIndex];
|
|
187
202
|
canvasEl.dataset[generatedAttribute] = "false";
|
|
188
203
|
}
|
|
189
204
|
else {
|
|
@@ -199,8 +214,9 @@ export class Engine {
|
|
|
199
214
|
canvasEl.style.height = "100%";
|
|
200
215
|
}
|
|
201
216
|
const newItem = new Container(this, id, currentOptions);
|
|
202
|
-
if (oldIndex >=
|
|
203
|
-
|
|
217
|
+
if (oldIndex >= minIndex) {
|
|
218
|
+
const deleteCount = 0;
|
|
219
|
+
dom.splice(oldIndex, deleteCount, newItem);
|
|
204
220
|
}
|
|
205
221
|
else {
|
|
206
222
|
dom.push(newItem);
|
|
@@ -220,14 +236,14 @@ export class Engine {
|
|
|
220
236
|
return;
|
|
221
237
|
}
|
|
222
238
|
for (const updater of updaters) {
|
|
223
|
-
updater.loadOptions
|
|
239
|
+
updater.loadOptions?.(options, ...sourceOptions);
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
242
|
async refresh(refresh = true) {
|
|
227
243
|
if (!refresh) {
|
|
228
244
|
return;
|
|
229
245
|
}
|
|
230
|
-
this.dom().
|
|
246
|
+
await Promise.allSettled(this.dom().map((t) => t.refresh()));
|
|
231
247
|
}
|
|
232
248
|
removeEventListener(type, listener) {
|
|
233
249
|
this._eventDispatcher.removeEventListener(type, listener);
|
package/browser/Core/Particle.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { calcExactPositionOrRandomFromSize, clamp, getDistance, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRange, setRangeValue, } from "../Utils/NumberUtils.js";
|
|
1
|
+
import { calcExactPositionOrRandomFromSize, clamp, degToRad, getDistance, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRange, setRangeValue, } from "../Utils/NumberUtils.js";
|
|
2
2
|
import { deepExtend, getPosition, initParticleNumericAnimationValue, isInArray, itemFromSingleOrMultiple, } from "../Utils/Utils.js";
|
|
3
|
+
import { errorPrefix, millisecondsToSeconds } from "./Utils/Constants.js";
|
|
3
4
|
import { getHslFromAnimation, rangeColorToRgb } from "../Utils/ColorUtils.js";
|
|
4
5
|
import { Interactivity } from "../Options/Classes/Interactivity/Interactivity.js";
|
|
5
6
|
import { Vector } from "./Utils/Vector.js";
|
|
6
7
|
import { Vector3d } from "./Utils/Vector3d.js";
|
|
7
8
|
import { alterHsl } from "../Utils/CanvasUtils.js";
|
|
8
|
-
import { errorPrefix } from "./Utils/Constants.js";
|
|
9
9
|
import { loadParticlesOptions } from "../Utils/OptionsUtils.js";
|
|
10
|
+
const defaultRetryCount = 0, double = 2, half = 0.5, squareExp = 2;
|
|
10
11
|
function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
|
|
11
12
|
const effectData = effectOptions.options[effect];
|
|
12
13
|
if (!effectData) {
|
|
@@ -31,7 +32,7 @@ function fixOutMode(data) {
|
|
|
31
32
|
if (!isInArray(data.outMode, data.checkModes)) {
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
|
-
const diameter = data.radius *
|
|
35
|
+
const diameter = data.radius * double;
|
|
35
36
|
if (data.coord > data.maxCoord - diameter) {
|
|
36
37
|
data.setCb(-data.radius);
|
|
37
38
|
}
|
|
@@ -42,7 +43,7 @@ function fixOutMode(data) {
|
|
|
42
43
|
export class Particle {
|
|
43
44
|
constructor(engine, id, container, position, overrideOptions, group) {
|
|
44
45
|
this.container = container;
|
|
45
|
-
this._calcPosition = (container, position, zIndex, tryCount =
|
|
46
|
+
this._calcPosition = (container, position, zIndex, tryCount = defaultRetryCount) => {
|
|
46
47
|
for (const [, plugin] of container.plugins) {
|
|
47
48
|
const pluginPos = plugin.particlePosition !== undefined ? plugin.particlePosition(position, this) : undefined;
|
|
48
49
|
if (pluginPos) {
|
|
@@ -76,7 +77,8 @@ export class Particle {
|
|
|
76
77
|
fixVertical(outModes.top ?? outModes.default);
|
|
77
78
|
fixVertical(outModes.bottom ?? outModes.default);
|
|
78
79
|
if (this._checkOverlap(pos, tryCount)) {
|
|
79
|
-
|
|
80
|
+
const increment = 1;
|
|
81
|
+
return this._calcPosition(container, undefined, zIndex, tryCount + increment);
|
|
80
82
|
}
|
|
81
83
|
return pos;
|
|
82
84
|
};
|
|
@@ -85,9 +87,9 @@ export class Particle {
|
|
|
85
87
|
if (moveOptions.direction === "inside" || moveOptions.direction === "outside") {
|
|
86
88
|
return res;
|
|
87
89
|
}
|
|
88
|
-
const rad = (
|
|
89
|
-
left: radOffset - rad *
|
|
90
|
-
right: radOffset + rad *
|
|
90
|
+
const rad = degToRad(getRangeValue(moveOptions.angle.value)), radOffset = degToRad(getRangeValue(moveOptions.angle.offset)), range = {
|
|
91
|
+
left: radOffset - rad * half,
|
|
92
|
+
right: radOffset + rad * half,
|
|
91
93
|
};
|
|
92
94
|
if (!moveOptions.straight) {
|
|
93
95
|
res.angle += randomInRange(setRangeValue(range.left, range.right));
|
|
@@ -97,7 +99,7 @@ export class Particle {
|
|
|
97
99
|
}
|
|
98
100
|
return res;
|
|
99
101
|
};
|
|
100
|
-
this._checkOverlap = (pos, tryCount =
|
|
102
|
+
this._checkOverlap = (pos, tryCount = defaultRetryCount) => {
|
|
101
103
|
const collisionsOptions = this.options.collisions, radius = this.getRadius();
|
|
102
104
|
if (!collisionsOptions.enable) {
|
|
103
105
|
return false;
|
|
@@ -106,8 +108,8 @@ export class Particle {
|
|
|
106
108
|
if (overlapOptions.enable) {
|
|
107
109
|
return false;
|
|
108
110
|
}
|
|
109
|
-
const retries = overlapOptions.retries;
|
|
110
|
-
if (retries >=
|
|
111
|
+
const retries = overlapOptions.retries, minRetries = 0;
|
|
112
|
+
if (retries >= minRetries && tryCount > retries) {
|
|
111
113
|
throw new Error(`${errorPrefix} particle is overlapping and can't be placed`);
|
|
112
114
|
}
|
|
113
115
|
return !!this.container.particles.find((particle) => getDistance(pos, particle.position) < radius + particle.getRadius());
|
|
@@ -116,7 +118,7 @@ export class Particle {
|
|
|
116
118
|
if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
|
|
117
119
|
return color;
|
|
118
120
|
}
|
|
119
|
-
const backFactor = this.roll.horizontal && this.roll.vertical ?
|
|
121
|
+
const rollFactor = 1, none = 0, backFactor = this.roll.horizontal && this.roll.vertical ? double * rollFactor : rollFactor, backSum = this.roll.horizontal ? Math.PI * half : none, rolled = Math.floor(((this.roll.angle ?? none) + backSum) / (Math.PI / backFactor)) % double;
|
|
120
122
|
if (!rolled) {
|
|
121
123
|
return color;
|
|
122
124
|
}
|
|
@@ -129,13 +131,13 @@ export class Particle {
|
|
|
129
131
|
return color;
|
|
130
132
|
};
|
|
131
133
|
this._initPosition = (position) => {
|
|
132
|
-
const container = this.container, zIndexValue = getRangeValue(this.options.zIndex.value);
|
|
133
|
-
this.position = this._calcPosition(container, position, clamp(zIndexValue,
|
|
134
|
+
const container = this.container, zIndexValue = getRangeValue(this.options.zIndex.value), minZ = 0;
|
|
135
|
+
this.position = this._calcPosition(container, position, clamp(zIndexValue, minZ, container.zLayers));
|
|
134
136
|
this.initialPosition = this.position.copy();
|
|
135
|
-
const canvasSize = container.canvas.size;
|
|
137
|
+
const canvasSize = container.canvas.size, defaultRadius = 0;
|
|
136
138
|
this.moveCenter = {
|
|
137
139
|
...getPosition(this.options.move.center, canvasSize),
|
|
138
|
-
radius: this.options.move.center.radius ??
|
|
140
|
+
radius: this.options.move.center.radius ?? defaultRadius,
|
|
139
141
|
mode: this.options.move.center.mode ?? "percent",
|
|
140
142
|
};
|
|
141
143
|
this.direction = getParticleDirectionAngle(this.options.move.direction, this.position, this.moveCenter);
|
|
@@ -160,14 +162,14 @@ export class Particle {
|
|
|
160
162
|
this.bubble.inRange = false;
|
|
161
163
|
this.slow.inRange = false;
|
|
162
164
|
const container = this.container, pathGenerator = this.pathGenerator, shapeDrawer = container.shapeDrawers.get(this.shape);
|
|
163
|
-
shapeDrawer
|
|
165
|
+
shapeDrawer?.particleDestroy?.(this);
|
|
164
166
|
for (const [, plugin] of container.plugins) {
|
|
165
|
-
plugin.particleDestroyed
|
|
167
|
+
plugin.particleDestroyed?.(this, override);
|
|
166
168
|
}
|
|
167
169
|
for (const updater of container.particles.updaters) {
|
|
168
|
-
updater.particleDestroyed
|
|
170
|
+
updater.particleDestroyed?.(this, override);
|
|
169
171
|
}
|
|
170
|
-
pathGenerator
|
|
172
|
+
pathGenerator?.reset(this);
|
|
171
173
|
this._engine.dispatchEvent("particleDestroyed", {
|
|
172
174
|
container: this.container,
|
|
173
175
|
data: {
|
|
@@ -186,7 +188,7 @@ export class Particle {
|
|
|
186
188
|
return this._getRollColor(this.bubble.color ?? getHslFromAnimation(this.color));
|
|
187
189
|
}
|
|
188
190
|
getMass() {
|
|
189
|
-
return this.getRadius() **
|
|
191
|
+
return this.getRadius() ** squareExp * Math.PI * half;
|
|
190
192
|
}
|
|
191
193
|
getPosition() {
|
|
192
194
|
return {
|
|
@@ -225,14 +227,14 @@ export class Particle {
|
|
|
225
227
|
this.shape = itemFromSingleOrMultiple(shapeType, this.id, reduceDuplicates);
|
|
226
228
|
const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
|
|
227
229
|
if (overrideOptions) {
|
|
228
|
-
if (overrideOptions.effect
|
|
230
|
+
if (overrideOptions.effect?.type) {
|
|
229
231
|
const overrideEffectType = overrideOptions.effect.type, effect = itemFromSingleOrMultiple(overrideEffectType, this.id, reduceDuplicates);
|
|
230
232
|
if (effect) {
|
|
231
233
|
this.effect = effect;
|
|
232
234
|
effectOptions.load(overrideOptions.effect);
|
|
233
235
|
}
|
|
234
236
|
}
|
|
235
|
-
if (overrideOptions.shape
|
|
237
|
+
if (overrideOptions.shape?.type) {
|
|
236
238
|
const overrideShapeType = overrideOptions.shape.type, shape = itemFromSingleOrMultiple(overrideShapeType, this.id, reduceDuplicates);
|
|
237
239
|
if (shape) {
|
|
238
240
|
this.shape = shape;
|
|
@@ -261,7 +263,7 @@ export class Particle {
|
|
|
261
263
|
this.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
|
|
262
264
|
this.options = particlesOptions;
|
|
263
265
|
const pathOptions = this.options.move.path;
|
|
264
|
-
this.pathDelay = getRangeValue(pathOptions.delay.value) *
|
|
266
|
+
this.pathDelay = getRangeValue(pathOptions.delay.value) * millisecondsToSeconds;
|
|
265
267
|
if (pathOptions.generator) {
|
|
266
268
|
this.pathGenerator = this._engine.getPathGenerator(pathOptions.generator);
|
|
267
269
|
if (this.pathGenerator && container.addPath(pathOptions.generator, this.pathGenerator)) {
|
|
@@ -280,7 +282,8 @@ export class Particle {
|
|
|
280
282
|
this._initPosition(position);
|
|
281
283
|
this.initialVelocity = this._calculateVelocity();
|
|
282
284
|
this.velocity = this.initialVelocity.copy();
|
|
283
|
-
|
|
285
|
+
const decayOffset = 1;
|
|
286
|
+
this.moveDecay = decayOffset - getRangeValue(this.options.move.decay);
|
|
284
287
|
const particles = container.particles;
|
|
285
288
|
particles.setLastZIndex(this.position.z);
|
|
286
289
|
this.zIndexFactor = this.position.z / container.zLayers;
|
|
@@ -292,7 +295,7 @@ export class Particle {
|
|
|
292
295
|
container.effectDrawers.set(this.effect, effectDrawer);
|
|
293
296
|
}
|
|
294
297
|
}
|
|
295
|
-
if (effectDrawer
|
|
298
|
+
if (effectDrawer?.loadEffect) {
|
|
296
299
|
effectDrawer.loadEffect(this);
|
|
297
300
|
}
|
|
298
301
|
let shapeDrawer = container.shapeDrawers.get(this.shape);
|
|
@@ -302,7 +305,7 @@ export class Particle {
|
|
|
302
305
|
container.shapeDrawers.set(this.shape, shapeDrawer);
|
|
303
306
|
}
|
|
304
307
|
}
|
|
305
|
-
if (shapeDrawer
|
|
308
|
+
if (shapeDrawer?.loadShape) {
|
|
306
309
|
shapeDrawer.loadShape(this);
|
|
307
310
|
}
|
|
308
311
|
const sideCountFunc = shapeDrawer?.getSidesCount;
|
|
@@ -315,16 +318,12 @@ export class Particle {
|
|
|
315
318
|
updater.init(this);
|
|
316
319
|
}
|
|
317
320
|
for (const mover of particles.movers) {
|
|
318
|
-
mover.init
|
|
319
|
-
}
|
|
320
|
-
if (effectDrawer && effectDrawer.particleInit) {
|
|
321
|
-
effectDrawer.particleInit(container, this);
|
|
322
|
-
}
|
|
323
|
-
if (shapeDrawer && shapeDrawer.particleInit) {
|
|
324
|
-
shapeDrawer.particleInit(container, this);
|
|
321
|
+
mover.init?.(this);
|
|
325
322
|
}
|
|
323
|
+
effectDrawer?.particleInit?.(container, this);
|
|
324
|
+
shapeDrawer?.particleInit?.(container, this);
|
|
326
325
|
for (const [, plugin] of container.plugins) {
|
|
327
|
-
plugin.particleCreated
|
|
326
|
+
plugin.particleCreated?.(this);
|
|
328
327
|
}
|
|
329
328
|
}
|
|
330
329
|
isInsideCanvas() {
|
|
@@ -339,7 +338,7 @@ export class Particle {
|
|
|
339
338
|
}
|
|
340
339
|
reset() {
|
|
341
340
|
for (const updater of this.container.particles.updaters) {
|
|
342
|
-
updater.reset
|
|
341
|
+
updater.reset?.(this);
|
|
343
342
|
}
|
|
344
343
|
}
|
|
345
344
|
}
|