@tsparticles/engine 4.0.0-alpha.1 → 4.0.0-alpha.3
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/638.min.js +1 -1
- package/638.min.js.LICENSE.txt +1 -1
- package/browser/Core/Canvas.js +15 -19
- package/browser/Core/Container.js +10 -15
- package/browser/Core/Engine.js +34 -14
- package/browser/Core/Interfaces/IParticleOpacityData.js +1 -0
- package/browser/Core/Interfaces/IParticleRotateData.js +1 -0
- package/browser/Core/Particle.js +95 -49
- package/browser/Utils/CanvasUtils.js +26 -83
- package/browser/Utils/ColorUtils.js +15 -2
- package/browser/Utils/MathUtils.js +3 -2
- package/browser/Utils/Utils.js +1 -1
- package/cjs/Core/Canvas.js +15 -19
- package/cjs/Core/Container.js +10 -15
- package/cjs/Core/Engine.js +34 -14
- package/cjs/Core/Interfaces/IParticleOpacityData.js +1 -0
- package/cjs/Core/Interfaces/IParticleRotateData.js +1 -0
- package/cjs/Core/Particle.js +95 -49
- package/cjs/Utils/CanvasUtils.js +26 -83
- package/cjs/Utils/ColorUtils.js +15 -2
- package/cjs/Utils/MathUtils.js +3 -2
- package/cjs/Utils/Utils.js +1 -1
- package/dist_browser_Core_Container_js.js +4 -4
- package/esm/Core/Canvas.js +15 -19
- package/esm/Core/Container.js +10 -15
- package/esm/Core/Engine.js +34 -14
- package/esm/Core/Interfaces/IParticleOpacityData.js +1 -0
- package/esm/Core/Interfaces/IParticleRotateData.js +1 -0
- package/esm/Core/Particle.js +95 -49
- package/esm/Utils/CanvasUtils.js +26 -83
- package/esm/Utils/ColorUtils.js +15 -2
- package/esm/Utils/MathUtils.js +3 -2
- package/esm/Utils/Utils.js +1 -1
- package/package.json +1 -1
- package/report.html +1 -1
- package/scripts/install.js +28 -9
- package/tsparticles.engine.js +6 -6
- package/tsparticles.engine.min.js +1 -1
- package/tsparticles.engine.min.js.LICENSE.txt +1 -1
- package/types/Core/Canvas.d.ts +3 -0
- package/types/Core/Container.d.ts +1 -0
- package/types/Core/Engine.d.ts +1 -1
- package/types/Core/Interfaces/IDrawParticleParams.d.ts +1 -1
- package/types/Core/Interfaces/IParticleOpacityData.d.ts +4 -0
- package/types/Core/Interfaces/IParticleRotateData.d.ts +4 -0
- package/types/Core/Interfaces/IParticleTransformValues.d.ts +4 -4
- package/types/Core/Interfaces/IParticleUpdater.d.ts +1 -1
- package/types/Core/Particle.d.ts +12 -0
- package/types/Types/CustomEventListener.d.ts +1 -1
- package/types/Utils/CanvasUtils.d.ts +6 -21
- package/types/Utils/EventDispatcher.d.ts +1 -1
- package/umd/Core/Canvas.js +14 -18
- package/umd/Core/Container.js +10 -15
- package/umd/Core/Engine.js +34 -14
- package/umd/Core/Interfaces/IParticleOpacityData.js +12 -0
- package/umd/Core/Interfaces/IParticleRotateData.js +12 -0
- package/umd/Core/Particle.js +94 -48
- package/umd/Utils/CanvasUtils.js +25 -82
- package/umd/Utils/ColorUtils.js +15 -2
- package/umd/Utils/MathUtils.js +3 -2
- package/umd/Utils/Utils.js +1 -1
package/esm/Core/Container.js
CHANGED
|
@@ -12,11 +12,9 @@ import { safeIntersectionObserver } from "../Utils/Utils.js";
|
|
|
12
12
|
function guardCheck(container) {
|
|
13
13
|
return !container.destroyed;
|
|
14
14
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
factor: smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds,
|
|
19
|
-
};
|
|
15
|
+
function updateDelta(delta, value, fpsLimit = defaultFps, smooth = false) {
|
|
16
|
+
delta.value = value;
|
|
17
|
+
delta.factor = smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds;
|
|
20
18
|
}
|
|
21
19
|
function loadContainerOptions(engine, container, ...sourceOptionsArr) {
|
|
22
20
|
const options = new Options(engine, container);
|
|
@@ -25,6 +23,7 @@ function loadContainerOptions(engine, container, ...sourceOptionsArr) {
|
|
|
25
23
|
}
|
|
26
24
|
export class Container {
|
|
27
25
|
constructor(engine, id, sourceOptions) {
|
|
26
|
+
this._delta = { value: 0, factor: 0 };
|
|
28
27
|
this._intersectionManager = entries => {
|
|
29
28
|
if (!guardCheck(this) || !this.actualOptions.pauseOnOutsideViewport) {
|
|
30
29
|
return;
|
|
@@ -50,14 +49,14 @@ export class Container {
|
|
|
50
49
|
return;
|
|
51
50
|
}
|
|
52
51
|
this._lastFrameTime ??= timestamp;
|
|
53
|
-
|
|
54
|
-
this.addLifeTime(
|
|
52
|
+
updateDelta(this._delta, timestamp - this._lastFrameTime, this.fpsLimit, this._smooth);
|
|
53
|
+
this.addLifeTime(this._delta.value);
|
|
55
54
|
this._lastFrameTime = timestamp;
|
|
56
|
-
if (
|
|
55
|
+
if (this._delta.value > millisecondsToSeconds) {
|
|
57
56
|
this.draw(false);
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
|
-
this.canvas.drawParticles(
|
|
59
|
+
this.canvas.drawParticles(this._delta);
|
|
61
60
|
if (!this.alive()) {
|
|
62
61
|
this.destroy();
|
|
63
62
|
return;
|
|
@@ -222,15 +221,11 @@ export class Container {
|
|
|
222
221
|
for (const effectDrawer of this.effectDrawers.values()) {
|
|
223
222
|
effectDrawer.destroy?.(this);
|
|
224
223
|
}
|
|
224
|
+
this.effectDrawers.clear();
|
|
225
225
|
for (const shapeDrawer of this.shapeDrawers.values()) {
|
|
226
226
|
shapeDrawer.destroy?.(this);
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
this.effectDrawers.delete(key);
|
|
230
|
-
}
|
|
231
|
-
for (const key of this.shapeDrawers.keys()) {
|
|
232
|
-
this.shapeDrawers.delete(key);
|
|
233
|
-
}
|
|
228
|
+
this.shapeDrawers.clear();
|
|
234
229
|
this._engine.clearPlugins(this);
|
|
235
230
|
this.destroyed = true;
|
|
236
231
|
if (remove) {
|
package/esm/Core/Engine.js
CHANGED
|
@@ -4,6 +4,7 @@ import { EventDispatcher } from "../Utils/EventDispatcher.js";
|
|
|
4
4
|
import { EventType } from "../Enums/Types/EventType.js";
|
|
5
5
|
import { getLogger } from "../Utils/LogUtils.js";
|
|
6
6
|
import { getRandom } from "../Utils/MathUtils.js";
|
|
7
|
+
const fullPercent = "100%";
|
|
7
8
|
async function getItemsFromInitializer(container, map, initializers, force = false) {
|
|
8
9
|
let res = map.get(container);
|
|
9
10
|
if (!res || force) {
|
|
@@ -25,6 +26,7 @@ async function getDataFromUrl(data) {
|
|
|
25
26
|
return data.fallback;
|
|
26
27
|
}
|
|
27
28
|
const getCanvasFromContainer = (domContainer) => {
|
|
29
|
+
const documentSafe = safeDocument();
|
|
28
30
|
let canvasEl;
|
|
29
31
|
if (domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag) {
|
|
30
32
|
canvasEl = domContainer;
|
|
@@ -37,28 +39,24 @@ const getCanvasFromContainer = (domContainer) => {
|
|
|
37
39
|
canvasEl.dataset[generatedAttribute] = generatedFalse;
|
|
38
40
|
}
|
|
39
41
|
else {
|
|
40
|
-
canvasEl =
|
|
42
|
+
canvasEl = documentSafe.createElement(canvasTag);
|
|
41
43
|
canvasEl.dataset[generatedAttribute] = generatedTrue;
|
|
42
44
|
domContainer.appendChild(canvasEl);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
canvasEl.style.width = fullPercent;
|
|
48
|
-
}
|
|
49
|
-
if (!canvasEl.style.height) {
|
|
50
|
-
canvasEl.style.height = fullPercent;
|
|
51
|
-
}
|
|
47
|
+
canvasEl.style.width ||= fullPercent;
|
|
48
|
+
canvasEl.style.height ||= fullPercent;
|
|
52
49
|
return canvasEl;
|
|
53
50
|
}, getDomContainer = (id, source) => {
|
|
54
|
-
|
|
51
|
+
const documentSafe = safeDocument();
|
|
52
|
+
let domContainer = source ?? document.getElementById(id);
|
|
55
53
|
if (domContainer) {
|
|
56
54
|
return domContainer;
|
|
57
55
|
}
|
|
58
|
-
domContainer =
|
|
56
|
+
domContainer = documentSafe.createElement("div");
|
|
59
57
|
domContainer.id = id;
|
|
60
58
|
domContainer.dataset[generatedAttribute] = generatedTrue;
|
|
61
|
-
|
|
59
|
+
documentSafe.body.append(domContainer);
|
|
62
60
|
return domContainer;
|
|
63
61
|
};
|
|
64
62
|
export class Engine {
|
|
@@ -95,7 +93,7 @@ export class Engine {
|
|
|
95
93
|
return this._domArray;
|
|
96
94
|
}
|
|
97
95
|
get version() {
|
|
98
|
-
return "4.0.0-alpha.
|
|
96
|
+
return "4.0.0-alpha.3";
|
|
99
97
|
}
|
|
100
98
|
addColorManager(manager) {
|
|
101
99
|
this.colorManagers.set(manager.key, manager);
|
|
@@ -221,10 +219,32 @@ export class Engine {
|
|
|
221
219
|
if (this._initialized) {
|
|
222
220
|
return;
|
|
223
221
|
}
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
const executed = new Set(), allLoaders = new Set(this._loadPromises), stack = [...allLoaders];
|
|
223
|
+
while (stack.length) {
|
|
224
|
+
const loader = stack.shift();
|
|
225
|
+
if (!loader) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (executed.has(loader)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
executed.add(loader);
|
|
232
|
+
const inner = [], origRegister = this.register.bind(this);
|
|
233
|
+
this.register = (...loaders) => {
|
|
234
|
+
inner.push(...loaders);
|
|
235
|
+
for (const loader of loaders) {
|
|
236
|
+
allLoaders.add(loader);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
await loader(this);
|
|
240
|
+
this.register = origRegister;
|
|
241
|
+
stack.unshift(...inner);
|
|
242
|
+
this._loadPromises.delete(loader);
|
|
226
243
|
}
|
|
227
244
|
this._loadPromises.clear();
|
|
245
|
+
for (const loader of allLoaders) {
|
|
246
|
+
this._loadPromises.add(loader);
|
|
247
|
+
}
|
|
228
248
|
this._initialized = true;
|
|
229
249
|
}
|
|
230
250
|
item(index) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/esm/Core/Particle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Vector, Vector3d } from "./Utils/Vectors.js";
|
|
2
2
|
import { calcExactPositionOrRandomFromSize, clamp, degToRad, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRangeValue, setRangeValue, } from "../Utils/MathUtils.js";
|
|
3
|
-
import { decayOffset, defaultAngle, defaultRetryCount, double, half, millisecondsToSeconds, minZ, none, randomColorValue, rollFactor, squareExp, tryCountIncrement, } from "./Utils/Constants.js";
|
|
3
|
+
import { decayOffset, defaultAngle, defaultOpacity, defaultRetryCount, defaultTransform, double, half, identity, millisecondsToSeconds, minZ, none, randomColorValue, rollFactor, squareExp, tryCountIncrement, zIndexFactorOffset, } from "./Utils/Constants.js";
|
|
4
4
|
import { deepExtend, getPosition, initParticleNumericAnimationValue, isInArray, itemFromSingleOrMultiple, } from "../Utils/Utils.js";
|
|
5
5
|
import { EventType } from "../Enums/Types/EventType.js";
|
|
6
6
|
import { Interactivity } from "../Options/Classes/Interactivity/Interactivity.js";
|
|
@@ -39,51 +39,50 @@ function fixOutMode(data) {
|
|
|
39
39
|
export class Particle {
|
|
40
40
|
constructor(engine, container) {
|
|
41
41
|
this.container = container;
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
this._cachedOpacityData = {
|
|
43
|
+
opacity: defaultOpacity,
|
|
44
|
+
strokeOpacity: defaultOpacity,
|
|
45
|
+
};
|
|
46
|
+
this._cachedPosition = Vector3d.origin;
|
|
47
|
+
this._cachedRotateData = { sin: 0, cos: 0 };
|
|
48
|
+
this._cachedTransform = {
|
|
49
|
+
a: 1,
|
|
50
|
+
b: 0,
|
|
51
|
+
c: 0,
|
|
52
|
+
d: 1,
|
|
53
|
+
};
|
|
54
|
+
this._calcPosition = (position, zIndex) => {
|
|
55
|
+
let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
|
|
56
|
+
const container = this.container, plugins = Array.from(container.plugins.values()), outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size, abortController = new AbortController(), { signal } = abortController;
|
|
57
|
+
while (!signal.aborted) {
|
|
58
|
+
for (const plugin of plugins) {
|
|
59
|
+
const pluginPos = plugin.particlePosition?.(posVec, this);
|
|
60
|
+
if (pluginPos) {
|
|
61
|
+
return Vector3d.create(pluginPos.x, pluginPos.y, zIndex);
|
|
62
|
+
}
|
|
48
63
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
fixOutMode({
|
|
64
|
-
outMode,
|
|
65
|
-
checkModes: [OutMode.bounce],
|
|
66
|
-
coord: pos.y,
|
|
67
|
-
maxCoord: container.canvas.size.height,
|
|
68
|
-
setCb: (value) => (pos.y += value),
|
|
69
|
-
radius,
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
fixHorizontal(outModes.left ?? outModes.default);
|
|
73
|
-
fixHorizontal(outModes.right ?? outModes.default);
|
|
74
|
-
fixVertical(outModes.top ?? outModes.default);
|
|
75
|
-
fixVertical(outModes.bottom ?? outModes.default);
|
|
76
|
-
let isValidPosition = true;
|
|
77
|
-
for (const plugin of plugins) {
|
|
78
|
-
isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount);
|
|
79
|
-
if (isValidPosition === false) {
|
|
80
|
-
break;
|
|
64
|
+
const exactPosition = calcExactPositionOrRandomFromSize({
|
|
65
|
+
size: canvasSize,
|
|
66
|
+
position: posVec,
|
|
67
|
+
}), pos = Vector3d.create(exactPosition.x, exactPosition.y, zIndex);
|
|
68
|
+
this._fixHorizontal(pos, radius, outModes.left ?? outModes.default);
|
|
69
|
+
this._fixHorizontal(pos, radius, outModes.right ?? outModes.default);
|
|
70
|
+
this._fixVertical(pos, radius, outModes.top ?? outModes.default);
|
|
71
|
+
this._fixVertical(pos, radius, outModes.bottom ?? outModes.default);
|
|
72
|
+
let isValidPosition = true;
|
|
73
|
+
for (const plugin of plugins) {
|
|
74
|
+
isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount) ?? true;
|
|
75
|
+
if (!isValidPosition) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
81
78
|
}
|
|
79
|
+
if (isValidPosition) {
|
|
80
|
+
return pos;
|
|
81
|
+
}
|
|
82
|
+
tryCount += tryCountIncrement;
|
|
83
|
+
posVec = undefined;
|
|
82
84
|
}
|
|
83
|
-
|
|
84
|
-
return this._calcPosition(container, undefined, zIndex, tryCount + tryCountIncrement);
|
|
85
|
-
}
|
|
86
|
-
return pos;
|
|
85
|
+
return posVec;
|
|
87
86
|
};
|
|
88
87
|
this._calculateVelocity = () => {
|
|
89
88
|
const baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy(), moveOptions = this.options.move;
|
|
@@ -102,6 +101,26 @@ export class Particle {
|
|
|
102
101
|
}
|
|
103
102
|
return res;
|
|
104
103
|
};
|
|
104
|
+
this._fixHorizontal = (pos, radius, outMode) => {
|
|
105
|
+
fixOutMode({
|
|
106
|
+
outMode,
|
|
107
|
+
checkModes: [OutMode.bounce],
|
|
108
|
+
coord: pos.x,
|
|
109
|
+
maxCoord: this.container.canvas.size.width,
|
|
110
|
+
setCb: (value) => (pos.x += value),
|
|
111
|
+
radius,
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
this._fixVertical = (pos, radius, outMode) => {
|
|
115
|
+
fixOutMode({
|
|
116
|
+
outMode,
|
|
117
|
+
checkModes: [OutMode.bounce],
|
|
118
|
+
coord: pos.y,
|
|
119
|
+
maxCoord: this.container.canvas.size.height,
|
|
120
|
+
setCb: (value) => (pos.y += value),
|
|
121
|
+
radius,
|
|
122
|
+
});
|
|
123
|
+
};
|
|
105
124
|
this._getRollColor = color => {
|
|
106
125
|
if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
|
|
107
126
|
return color;
|
|
@@ -120,7 +139,11 @@ export class Particle {
|
|
|
120
139
|
};
|
|
121
140
|
this._initPosition = position => {
|
|
122
141
|
const container = this.container, zIndexValue = getRangeValue(this.options.zIndex.value);
|
|
123
|
-
|
|
142
|
+
const initialPosition = this._calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
|
|
143
|
+
if (!initialPosition) {
|
|
144
|
+
throw new Error("a valid position cannot be found for particle");
|
|
145
|
+
}
|
|
146
|
+
this.position = initialPosition;
|
|
124
147
|
this.initialPosition = this.position.copy();
|
|
125
148
|
const canvasSize = container.canvas.size;
|
|
126
149
|
this.moveCenter = {
|
|
@@ -180,19 +203,42 @@ export class Particle {
|
|
|
180
203
|
getMass() {
|
|
181
204
|
return this.getRadius() ** squareExp * Math.PI * half;
|
|
182
205
|
}
|
|
206
|
+
getOpacity() {
|
|
207
|
+
const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, opacity = this.bubble.opacity ?? getRangeValue(this.opacity?.value ?? defaultOpacity), strokeOpacity = this.strokeOpacity ?? opacity;
|
|
208
|
+
this._cachedOpacityData.opacity = opacity * zOpacityFactor;
|
|
209
|
+
this._cachedOpacityData.strokeOpacity = strokeOpacity * zOpacityFactor;
|
|
210
|
+
return this._cachedOpacityData;
|
|
211
|
+
}
|
|
183
212
|
getPosition() {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
};
|
|
213
|
+
this._cachedPosition.x = this.position.x + this.offset.x;
|
|
214
|
+
this._cachedPosition.y = this.position.y + this.offset.y;
|
|
215
|
+
this._cachedPosition.z = this.position.z;
|
|
216
|
+
return this._cachedPosition;
|
|
189
217
|
}
|
|
190
218
|
getRadius() {
|
|
191
219
|
return this.bubble.radius ?? this.size.value;
|
|
192
220
|
}
|
|
221
|
+
getRotateData() {
|
|
222
|
+
const angle = this.getAngle();
|
|
223
|
+
this._cachedRotateData.sin = Math.sin(angle);
|
|
224
|
+
this._cachedRotateData.cos = Math.cos(angle);
|
|
225
|
+
return this._cachedRotateData;
|
|
226
|
+
}
|
|
193
227
|
getStrokeColor() {
|
|
194
228
|
return this._getRollColor(this.bubble.color ?? getHslFromAnimation(this.strokeColor));
|
|
195
229
|
}
|
|
230
|
+
getTransformData(externalTransform) {
|
|
231
|
+
const rotateData = this.getRotateData(), rotating = this.isRotating;
|
|
232
|
+
this._cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform.a);
|
|
233
|
+
this._cachedTransform.b = rotating
|
|
234
|
+
? rotateData.sin * (externalTransform.b ?? identity)
|
|
235
|
+
: (externalTransform.b ?? defaultTransform.b);
|
|
236
|
+
this._cachedTransform.c = rotating
|
|
237
|
+
? -rotateData.sin * (externalTransform.c ?? identity)
|
|
238
|
+
: (externalTransform.c ?? defaultTransform.c);
|
|
239
|
+
this._cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform.d);
|
|
240
|
+
return this._cachedTransform;
|
|
241
|
+
}
|
|
196
242
|
init(id, position, overrideOptions, group) {
|
|
197
243
|
const container = this.container, engine = this._engine;
|
|
198
244
|
this.id = id;
|
package/esm/Utils/CanvasUtils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { lFactor, minStrokeWidth, originPoint } from "../Core/Utils/Constants.js";
|
|
2
2
|
import { AlterType } from "../Enums/Types/AlterType.js";
|
|
3
3
|
export function clearDrawPlugin(context, plugin, delta) {
|
|
4
4
|
if (!plugin.clearDraw) {
|
|
@@ -28,15 +28,7 @@ export function clear(context, dimension) {
|
|
|
28
28
|
context.clearRect(originPoint.x, originPoint.y, dimension.width, dimension.height);
|
|
29
29
|
}
|
|
30
30
|
export function drawParticle(data) {
|
|
31
|
-
const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, pos = particle.getPosition(),
|
|
32
|
-
sin: Math.sin(angle),
|
|
33
|
-
cos: Math.cos(angle),
|
|
34
|
-
}, rotating = particle.isRotating, transformData = {
|
|
35
|
-
a: rotateData.cos * (transform.a ?? defaultTransform.a),
|
|
36
|
-
b: rotating ? rotateData.sin * (transform.b ?? identity) : (transform.b ?? defaultTransform.b),
|
|
37
|
-
c: rotating ? -rotateData.sin * (transform.c ?? identity) : (transform.c ?? defaultTransform.c),
|
|
38
|
-
d: rotateData.cos * (transform.d ?? defaultTransform.d),
|
|
39
|
-
};
|
|
31
|
+
const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, pos = particle.getPosition(), transformData = particle.getTransformData(transform);
|
|
40
32
|
context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
|
|
41
33
|
if (colorStyles.fill) {
|
|
42
34
|
context.fillStyle = colorStyles.fill;
|
|
@@ -47,24 +39,25 @@ export function drawParticle(data) {
|
|
|
47
39
|
context.strokeStyle = colorStyles.stroke;
|
|
48
40
|
}
|
|
49
41
|
const drawData = {
|
|
50
|
-
container,
|
|
51
42
|
context,
|
|
52
43
|
particle,
|
|
53
44
|
radius,
|
|
54
45
|
opacity,
|
|
55
46
|
delta,
|
|
47
|
+
pixelRatio: container.retina.pixelRatio,
|
|
48
|
+
fill: particle.shapeFill,
|
|
49
|
+
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
56
50
|
transformData,
|
|
57
|
-
strokeWidth,
|
|
58
51
|
};
|
|
59
|
-
drawBeforeEffect(drawData);
|
|
60
|
-
drawShapeBeforeDraw(drawData);
|
|
61
|
-
drawShape(drawData);
|
|
62
|
-
drawShapeAfterDraw(drawData);
|
|
63
|
-
drawAfterEffect(drawData);
|
|
52
|
+
drawBeforeEffect(container, drawData);
|
|
53
|
+
drawShapeBeforeDraw(container, drawData);
|
|
54
|
+
drawShape(container, drawData);
|
|
55
|
+
drawShapeAfterDraw(container, drawData);
|
|
56
|
+
drawAfterEffect(container, drawData);
|
|
64
57
|
context.resetTransform();
|
|
65
58
|
}
|
|
66
|
-
export function drawAfterEffect(data) {
|
|
67
|
-
const {
|
|
59
|
+
export function drawAfterEffect(container, data) {
|
|
60
|
+
const { particle } = data;
|
|
68
61
|
if (!particle.effect) {
|
|
69
62
|
return;
|
|
70
63
|
}
|
|
@@ -72,20 +65,10 @@ export function drawAfterEffect(data) {
|
|
|
72
65
|
if (!drawFunc) {
|
|
73
66
|
return;
|
|
74
67
|
}
|
|
75
|
-
drawFunc(
|
|
76
|
-
context,
|
|
77
|
-
particle,
|
|
78
|
-
radius,
|
|
79
|
-
opacity,
|
|
80
|
-
delta,
|
|
81
|
-
pixelRatio: container.retina.pixelRatio,
|
|
82
|
-
fill: particle.shapeFill,
|
|
83
|
-
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
84
|
-
transformData: { ...transformData },
|
|
85
|
-
});
|
|
68
|
+
drawFunc(data);
|
|
86
69
|
}
|
|
87
|
-
export function drawBeforeEffect(data) {
|
|
88
|
-
const {
|
|
70
|
+
export function drawBeforeEffect(container, data) {
|
|
71
|
+
const { particle } = data;
|
|
89
72
|
if (!particle.effect) {
|
|
90
73
|
return;
|
|
91
74
|
}
|
|
@@ -93,20 +76,10 @@ export function drawBeforeEffect(data) {
|
|
|
93
76
|
if (!drawer?.drawBefore) {
|
|
94
77
|
return;
|
|
95
78
|
}
|
|
96
|
-
drawer.drawBefore(
|
|
97
|
-
context,
|
|
98
|
-
particle,
|
|
99
|
-
radius,
|
|
100
|
-
opacity,
|
|
101
|
-
delta,
|
|
102
|
-
pixelRatio: container.retina.pixelRatio,
|
|
103
|
-
fill: particle.shapeFill,
|
|
104
|
-
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
105
|
-
transformData: { ...transformData },
|
|
106
|
-
});
|
|
79
|
+
drawer.drawBefore(data);
|
|
107
80
|
}
|
|
108
|
-
export function drawShape(data) {
|
|
109
|
-
const {
|
|
81
|
+
export function drawShape(container, data) {
|
|
82
|
+
const { context, particle, stroke } = data;
|
|
110
83
|
if (!particle.shape) {
|
|
111
84
|
return;
|
|
112
85
|
}
|
|
@@ -115,29 +88,19 @@ export function drawShape(data) {
|
|
|
115
88
|
return;
|
|
116
89
|
}
|
|
117
90
|
context.beginPath();
|
|
118
|
-
drawer.draw(
|
|
119
|
-
context,
|
|
120
|
-
particle,
|
|
121
|
-
radius,
|
|
122
|
-
opacity,
|
|
123
|
-
delta,
|
|
124
|
-
pixelRatio: container.retina.pixelRatio,
|
|
125
|
-
fill: particle.shapeFill,
|
|
126
|
-
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
127
|
-
transformData: { ...transformData },
|
|
128
|
-
});
|
|
91
|
+
drawer.draw(data);
|
|
129
92
|
if (particle.shapeClose) {
|
|
130
93
|
context.closePath();
|
|
131
94
|
}
|
|
132
|
-
if (
|
|
95
|
+
if (stroke) {
|
|
133
96
|
context.stroke();
|
|
134
97
|
}
|
|
135
98
|
if (particle.shapeFill) {
|
|
136
99
|
context.fill();
|
|
137
100
|
}
|
|
138
101
|
}
|
|
139
|
-
export function drawShapeAfterDraw(data) {
|
|
140
|
-
const {
|
|
102
|
+
export function drawShapeAfterDraw(container, data) {
|
|
103
|
+
const { particle } = data;
|
|
141
104
|
if (!particle.shape) {
|
|
142
105
|
return;
|
|
143
106
|
}
|
|
@@ -145,20 +108,10 @@ export function drawShapeAfterDraw(data) {
|
|
|
145
108
|
if (!drawer?.afterDraw) {
|
|
146
109
|
return;
|
|
147
110
|
}
|
|
148
|
-
drawer.afterDraw(
|
|
149
|
-
context,
|
|
150
|
-
particle,
|
|
151
|
-
radius,
|
|
152
|
-
opacity,
|
|
153
|
-
delta,
|
|
154
|
-
pixelRatio: container.retina.pixelRatio,
|
|
155
|
-
fill: particle.shapeFill,
|
|
156
|
-
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
157
|
-
transformData: { ...transformData },
|
|
158
|
-
});
|
|
111
|
+
drawer.afterDraw(data);
|
|
159
112
|
}
|
|
160
|
-
export function drawShapeBeforeDraw(data) {
|
|
161
|
-
const {
|
|
113
|
+
export function drawShapeBeforeDraw(container, data) {
|
|
114
|
+
const { particle } = data;
|
|
162
115
|
if (!particle.shape) {
|
|
163
116
|
return;
|
|
164
117
|
}
|
|
@@ -166,17 +119,7 @@ export function drawShapeBeforeDraw(data) {
|
|
|
166
119
|
if (!drawer?.beforeDraw) {
|
|
167
120
|
return;
|
|
168
121
|
}
|
|
169
|
-
drawer.beforeDraw(
|
|
170
|
-
context,
|
|
171
|
-
particle,
|
|
172
|
-
radius,
|
|
173
|
-
opacity,
|
|
174
|
-
delta,
|
|
175
|
-
pixelRatio: container.retina.pixelRatio,
|
|
176
|
-
fill: particle.shapeFill,
|
|
177
|
-
stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
|
|
178
|
-
transformData: { ...transformData },
|
|
179
|
-
});
|
|
122
|
+
drawer.beforeDraw(data);
|
|
180
123
|
}
|
|
181
124
|
export function drawPlugin(context, plugin, delta) {
|
|
182
125
|
if (!plugin.draw) {
|
package/esm/Utils/ColorUtils.js
CHANGED
|
@@ -3,6 +3,17 @@ import { decayOffset, defaultLoops, defaultOpacity, defaultRgbMin, defaultTime,
|
|
|
3
3
|
import { isArray, isString } from "./TypeUtils.js";
|
|
4
4
|
import { AnimationStatus } from "../Enums/AnimationStatus.js";
|
|
5
5
|
import { itemFromArray } from "./Utils.js";
|
|
6
|
+
const styleCache = new Map(), maxCacheSize = 1000;
|
|
7
|
+
function getCachedStyle(key, generator) {
|
|
8
|
+
let cached = styleCache.get(key);
|
|
9
|
+
if (!cached) {
|
|
10
|
+
cached = generator();
|
|
11
|
+
if (styleCache.size < maxCacheSize) {
|
|
12
|
+
styleCache.set(key, cached);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return cached;
|
|
16
|
+
}
|
|
6
17
|
function stringToRgba(engine, input) {
|
|
7
18
|
if (!input) {
|
|
8
19
|
return;
|
|
@@ -155,7 +166,8 @@ export function getRandomRgbColor(min) {
|
|
|
155
166
|
};
|
|
156
167
|
}
|
|
157
168
|
export function getStyleFromRgb(color, hdr, opacity) {
|
|
158
|
-
|
|
169
|
+
const op = opacity ?? defaultOpacity, key = `rgb-${color.r.toString()}-${color.g.toString()}-${color.b.toString()}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
|
|
170
|
+
return getCachedStyle(key, () => (hdr ? getHdrStyleFromRgb(color, opacity) : getSdrStyleFromRgb(color, opacity)));
|
|
159
171
|
}
|
|
160
172
|
function getHdrStyleFromRgb(color, opacity) {
|
|
161
173
|
return `color(display-p3 ${(color.r / rgbMax).toString()} ${(color.g / rgbMax).toString()} ${(color.b / rgbMax).toString()} / ${(opacity ?? defaultOpacity).toString()})`;
|
|
@@ -164,7 +176,8 @@ function getSdrStyleFromRgb(color, opacity) {
|
|
|
164
176
|
return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity).toString()})`;
|
|
165
177
|
}
|
|
166
178
|
export function getStyleFromHsl(color, hdr, opacity) {
|
|
167
|
-
|
|
179
|
+
const op = opacity ?? defaultOpacity, key = `hsl-${color.h.toString()}-${color.s.toString()}-${color.l.toString()}-${hdr ? "hdr" : "sdr"}-${op.toString()}`;
|
|
180
|
+
return getCachedStyle(key, () => (hdr ? getHdrStyleFromHsl(color, opacity) : getSdrStyleFromHsl(color, opacity)));
|
|
168
181
|
}
|
|
169
182
|
function getHdrStyleFromHsl(color, opacity) {
|
|
170
183
|
return getHdrStyleFromRgb(hslToRgb(color), opacity);
|
package/esm/Utils/MathUtils.js
CHANGED
|
@@ -137,9 +137,10 @@ export function calcPositionOrRandomFromSizeRanged(data) {
|
|
|
137
137
|
return calcPositionOrRandomFromSize({ size: data.size, position });
|
|
138
138
|
}
|
|
139
139
|
export function calcExactPositionOrRandomFromSize(data) {
|
|
140
|
+
const { position, size } = data;
|
|
140
141
|
return {
|
|
141
|
-
x:
|
|
142
|
-
y:
|
|
142
|
+
x: position?.x ?? getRandom() * size.width,
|
|
143
|
+
y: position?.y ?? getRandom() * size.height,
|
|
143
144
|
};
|
|
144
145
|
}
|
|
145
146
|
export function calcExactPositionOrRandomFromSizeRanged(data) {
|
package/esm/Utils/Utils.js
CHANGED
|
@@ -328,7 +328,7 @@ export function cloneStyle(style) {
|
|
|
328
328
|
const clonedStyle = safeDocument().createElement("div").style;
|
|
329
329
|
for (const key in style) {
|
|
330
330
|
const styleKey = style[key];
|
|
331
|
-
if (!Object.
|
|
331
|
+
if (!Object.hasOwn(style, key) || isNull(styleKey)) {
|
|
332
332
|
continue;
|
|
333
333
|
}
|
|
334
334
|
const styleValue = style.getPropertyValue?.(styleKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsparticles/engine",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.3",
|
|
4
4
|
"description": "Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
|
|
5
5
|
"homepage": "https://particles.js.org",
|
|
6
6
|
"scripts": {
|
package/report.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8"/>
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
6
|
-
<title>@tsparticles/engine [
|
|
6
|
+
<title>@tsparticles/engine [10 Jan 2026 at 19:10]</title>
|
|
7
7
|
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
|
|
8
8
|
|
|
9
9
|
<script>
|