@tsparticles/engine 3.0.3 → 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 +265 -145
- 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,5 +1,6 @@
|
|
|
1
|
-
import { getRandom, getRangeValue, mix, randomInRange, setRangeValue } from "./NumberUtils.js";
|
|
1
|
+
import { clamp, getRandom, getRangeMax, getRangeMin, getRangeValue, mix, randomInRange, setRangeValue, } from "./NumberUtils.js";
|
|
2
2
|
import { isArray, isString, itemFromArray } from "./Utils.js";
|
|
3
|
+
import { millisecondsToSeconds, percentDenominator } from "../Core/Utils/Constants.js";
|
|
3
4
|
const randomColorValue = "random", midColorValue = "mid", colorManagers = new Map();
|
|
4
5
|
export function addColorManager(manager) {
|
|
5
6
|
colorManagers.set(manager.key, manager);
|
|
@@ -12,13 +13,15 @@ function stringToRgba(input) {
|
|
|
12
13
|
}
|
|
13
14
|
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexFixed = input.replace(shorthandRegex, (_, r, g, b, a) => {
|
|
14
15
|
return r + r + g + g + b + b + (a !== undefined ? a + a : "");
|
|
15
|
-
}), regex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, result = regex.exec(hexFixed);
|
|
16
|
+
}), regex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, result = regex.exec(hexFixed), radix = 16, defaultAlpha = 1, alphaFactor = 0xff;
|
|
16
17
|
return result
|
|
17
18
|
? {
|
|
18
|
-
a: result[4] !== undefined
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
a: result[4] !== undefined
|
|
20
|
+
? parseInt(result[4], radix) / alphaFactor
|
|
21
|
+
: defaultAlpha,
|
|
22
|
+
b: parseInt(result[3], radix),
|
|
23
|
+
g: parseInt(result[2], radix),
|
|
24
|
+
r: parseInt(result[1], radix),
|
|
22
25
|
}
|
|
23
26
|
: undefined;
|
|
24
27
|
}
|
|
@@ -71,26 +74,26 @@ export function rangeColorToHsl(color, index, useIndex = true) {
|
|
|
71
74
|
return rgb ? rgbToHsl(rgb) : undefined;
|
|
72
75
|
}
|
|
73
76
|
export function rgbToHsl(color) {
|
|
74
|
-
const r1 = color.r /
|
|
75
|
-
h:
|
|
76
|
-
l: (max + min) *
|
|
77
|
-
s:
|
|
77
|
+
const rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, half = 0.5, double = 2, r1 = color.r / rgbMax, g1 = color.g / rgbMax, b1 = color.b / rgbMax, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
|
|
78
|
+
h: hMin,
|
|
79
|
+
l: (max + min) * half,
|
|
80
|
+
s: sMin,
|
|
78
81
|
};
|
|
79
82
|
if (max !== min) {
|
|
80
|
-
res.s = res.l <
|
|
83
|
+
res.s = res.l < half ? (max - min) / (max + min) : (max - min) / (double - max - min);
|
|
81
84
|
res.h =
|
|
82
85
|
r1 === max
|
|
83
86
|
? (g1 - b1) / (max - min)
|
|
84
|
-
: (res.h = g1 === max ?
|
|
87
|
+
: (res.h = g1 === max ? double + (b1 - r1) / (max - min) : double * double + (r1 - g1) / (max - min));
|
|
85
88
|
}
|
|
86
|
-
res.l *=
|
|
87
|
-
res.s *=
|
|
88
|
-
res.h *=
|
|
89
|
-
if (res.h <
|
|
90
|
-
res.h +=
|
|
89
|
+
res.l *= lMax;
|
|
90
|
+
res.s *= sMax;
|
|
91
|
+
res.h *= hPhase;
|
|
92
|
+
if (res.h < hMin) {
|
|
93
|
+
res.h += hMax;
|
|
91
94
|
}
|
|
92
|
-
if (res.h >=
|
|
93
|
-
res.h -=
|
|
95
|
+
if (res.h >= hMax) {
|
|
96
|
+
res.h -= hMax;
|
|
94
97
|
}
|
|
95
98
|
return res;
|
|
96
99
|
}
|
|
@@ -101,29 +104,33 @@ export function stringToRgb(input) {
|
|
|
101
104
|
return stringToRgba(input);
|
|
102
105
|
}
|
|
103
106
|
export function hslToRgb(hsl) {
|
|
104
|
-
const h = ((hsl.h %
|
|
105
|
-
if (s ===
|
|
106
|
-
const grayscaleValue = Math.round(lNormalized *
|
|
107
|
+
const hMax = 360, sMax = 100, lMax = 100, sMin = 0, lMin = 0, h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax, rgbFactor = 255, triple = 3;
|
|
108
|
+
if (s === sMin) {
|
|
109
|
+
const grayscaleValue = Math.round(lNormalized * rgbFactor);
|
|
107
110
|
return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
|
|
108
111
|
}
|
|
109
|
-
const channel = (temp1, temp2, temp3) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const half = 0.5, double = 2, channel = (temp1, temp2, temp3) => {
|
|
113
|
+
const temp3Min = 0, temp3Max = 1, sextuple = 6;
|
|
114
|
+
if (temp3 < temp3Min) {
|
|
115
|
+
temp3++;
|
|
112
116
|
}
|
|
113
|
-
if (temp3 >
|
|
114
|
-
temp3
|
|
117
|
+
if (temp3 > temp3Max) {
|
|
118
|
+
temp3--;
|
|
115
119
|
}
|
|
116
|
-
if (temp3 *
|
|
117
|
-
return temp1 + (temp2 - temp1) *
|
|
120
|
+
if (temp3 * sextuple < temp3Max) {
|
|
121
|
+
return temp1 + (temp2 - temp1) * sextuple * temp3;
|
|
118
122
|
}
|
|
119
|
-
if (temp3 *
|
|
123
|
+
if (temp3 * double < temp3Max) {
|
|
120
124
|
return temp2;
|
|
121
125
|
}
|
|
122
|
-
if (temp3 *
|
|
123
|
-
|
|
126
|
+
if (temp3 * triple < temp3Max * double) {
|
|
127
|
+
const temp3Offset = double / triple;
|
|
128
|
+
return temp1 + (temp2 - temp1) * (temp3Offset - temp3) * sextuple;
|
|
124
129
|
}
|
|
125
130
|
return temp1;
|
|
126
|
-
},
|
|
131
|
+
}, sNormalizedOffset = 1, temp1 = lNormalized < half
|
|
132
|
+
? lNormalized * (sNormalizedOffset + sNormalized)
|
|
133
|
+
: lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseNumerator = 1, phaseThird = phaseNumerator / triple, red = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized)), blue = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized - phaseThird));
|
|
127
134
|
return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
|
|
128
135
|
}
|
|
129
136
|
export function hslaToRgba(hsla) {
|
|
@@ -136,18 +143,20 @@ export function hslaToRgba(hsla) {
|
|
|
136
143
|
};
|
|
137
144
|
}
|
|
138
145
|
export function getRandomRgbColor(min) {
|
|
139
|
-
const fixedMin = min ??
|
|
146
|
+
const defaultMin = 0, fixedMin = min ?? defaultMin, rgbMax = 256;
|
|
140
147
|
return {
|
|
141
|
-
b: Math.floor(randomInRange(setRangeValue(fixedMin,
|
|
142
|
-
g: Math.floor(randomInRange(setRangeValue(fixedMin,
|
|
143
|
-
r: Math.floor(randomInRange(setRangeValue(fixedMin,
|
|
148
|
+
b: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
|
149
|
+
g: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
|
150
|
+
r: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
|
144
151
|
};
|
|
145
152
|
}
|
|
146
153
|
export function getStyleFromRgb(color, opacity) {
|
|
147
|
-
|
|
154
|
+
const defaultOpacity = 1;
|
|
155
|
+
return `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity ?? defaultOpacity})`;
|
|
148
156
|
}
|
|
149
157
|
export function getStyleFromHsl(color, opacity) {
|
|
150
|
-
|
|
158
|
+
const defaultOpacity = 1;
|
|
159
|
+
return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${opacity ?? defaultOpacity})`;
|
|
151
160
|
}
|
|
152
161
|
export function colorMix(color1, color2, size1, size2) {
|
|
153
162
|
let rgb1 = color1, rgb2 = color2;
|
|
@@ -238,21 +247,91 @@ export function getHslAnimationFromHsl(hsl, animationOptions, reduceFactor) {
|
|
|
238
247
|
}
|
|
239
248
|
function setColorAnimation(colorValue, colorAnimation, reduceFactor) {
|
|
240
249
|
colorValue.enable = colorAnimation.enable;
|
|
250
|
+
const defaultVelocity = 0, decayOffset = 1, defaultLoops = 0, defaultTime = 0;
|
|
241
251
|
if (colorValue.enable) {
|
|
242
|
-
colorValue.velocity = (getRangeValue(colorAnimation.speed) /
|
|
243
|
-
colorValue.decay =
|
|
252
|
+
colorValue.velocity = (getRangeValue(colorAnimation.speed) / percentDenominator) * reduceFactor;
|
|
253
|
+
colorValue.decay = decayOffset - getRangeValue(colorAnimation.decay);
|
|
244
254
|
colorValue.status = "increasing";
|
|
245
|
-
colorValue.loops =
|
|
255
|
+
colorValue.loops = defaultLoops;
|
|
246
256
|
colorValue.maxLoops = getRangeValue(colorAnimation.count);
|
|
247
|
-
colorValue.time =
|
|
248
|
-
colorValue.delayTime = getRangeValue(colorAnimation.delay) *
|
|
257
|
+
colorValue.time = defaultTime;
|
|
258
|
+
colorValue.delayTime = getRangeValue(colorAnimation.delay) * millisecondsToSeconds;
|
|
249
259
|
if (!colorAnimation.sync) {
|
|
250
260
|
colorValue.velocity *= getRandom();
|
|
251
261
|
colorValue.value *= getRandom();
|
|
252
262
|
}
|
|
253
263
|
colorValue.initialValue = colorValue.value;
|
|
264
|
+
colorValue.offset = setRangeValue(colorAnimation.offset);
|
|
254
265
|
}
|
|
255
266
|
else {
|
|
256
|
-
colorValue.velocity =
|
|
267
|
+
colorValue.velocity = defaultVelocity;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
export function updateColorValue(data, range, decrease, delta) {
|
|
271
|
+
const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
|
|
272
|
+
if (!data ||
|
|
273
|
+
!data.enable ||
|
|
274
|
+
((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (!data.time) {
|
|
278
|
+
data.time = 0;
|
|
279
|
+
}
|
|
280
|
+
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
|
281
|
+
data.time += delta.value;
|
|
282
|
+
}
|
|
283
|
+
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const offset = data.offset ? randomInRange(data.offset) : minOffset, velocity = (data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor, decay = data.decay ?? identity, max = getRangeMax(range), min = getRangeMin(range);
|
|
287
|
+
if (!decrease || data.status === "increasing") {
|
|
288
|
+
data.value += velocity;
|
|
289
|
+
if (data.value > max) {
|
|
290
|
+
if (!data.loops) {
|
|
291
|
+
data.loops = 0;
|
|
292
|
+
}
|
|
293
|
+
data.loops++;
|
|
294
|
+
if (decrease) {
|
|
295
|
+
data.status = "decreasing";
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
data.value -= max;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
data.value -= velocity;
|
|
304
|
+
const minValue = 0;
|
|
305
|
+
if (data.value < minValue) {
|
|
306
|
+
if (!data.loops) {
|
|
307
|
+
data.loops = 0;
|
|
308
|
+
}
|
|
309
|
+
data.loops++;
|
|
310
|
+
data.status = "increasing";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (data.velocity && decay !== identity) {
|
|
314
|
+
data.velocity *= decay;
|
|
315
|
+
}
|
|
316
|
+
data.value = clamp(data.value, min, max);
|
|
317
|
+
}
|
|
318
|
+
export function updateColor(color, delta) {
|
|
319
|
+
if (!color) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const { h, s, l } = color;
|
|
323
|
+
const ranges = {
|
|
324
|
+
h: { min: 0, max: 360 },
|
|
325
|
+
s: { min: 0, max: 100 },
|
|
326
|
+
l: { min: 0, max: 100 },
|
|
327
|
+
};
|
|
328
|
+
if (h) {
|
|
329
|
+
updateColorValue(h, ranges.h, false, delta);
|
|
330
|
+
}
|
|
331
|
+
if (s) {
|
|
332
|
+
updateColorValue(s, ranges.s, true, delta);
|
|
333
|
+
}
|
|
334
|
+
if (l) {
|
|
335
|
+
updateColorValue(l, ranges.l, true, delta);
|
|
257
336
|
}
|
|
258
337
|
}
|
|
@@ -13,7 +13,7 @@ export class EventDispatcher {
|
|
|
13
13
|
}
|
|
14
14
|
dispatchEvent(type, args) {
|
|
15
15
|
const listeners = this._listeners.get(type);
|
|
16
|
-
listeners
|
|
16
|
+
listeners?.forEach((handler) => handler(args));
|
|
17
17
|
}
|
|
18
18
|
hasEventListener(type) {
|
|
19
19
|
return !!this._listeners.get(type);
|
|
@@ -31,15 +31,16 @@ export class EventDispatcher {
|
|
|
31
31
|
if (!arr) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
const length = arr.length, idx = arr.indexOf(listener);
|
|
35
|
-
if (idx <
|
|
34
|
+
const length = arr.length, idx = arr.indexOf(listener), minIndex = 0;
|
|
35
|
+
if (idx < minIndex) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
const deleteCount = 1;
|
|
39
|
+
if (length === deleteCount) {
|
|
39
40
|
this._listeners.delete(type);
|
|
40
41
|
}
|
|
41
42
|
else {
|
|
42
|
-
arr.splice(idx,
|
|
43
|
+
arr.splice(idx, deleteCount);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -25,13 +25,13 @@ export class HslColorManager {
|
|
|
25
25
|
if (!input.startsWith("hsl")) {
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
const regex = /hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([\d.%]+)\s*)?\)/i, result = regex.exec(input);
|
|
28
|
+
const regex = /hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(,\s*([\d.%]+)\s*)?\)/i, result = regex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
|
|
29
29
|
return result
|
|
30
30
|
? hslaToRgba({
|
|
31
|
-
a: result.length >
|
|
32
|
-
h: parseInt(result[1],
|
|
33
|
-
l: parseInt(result[3],
|
|
34
|
-
s: parseInt(result[2],
|
|
31
|
+
a: result.length > minLength ? parseAlpha(result[5]) : defaultAlpha,
|
|
32
|
+
h: parseInt(result[1], radix),
|
|
33
|
+
l: parseInt(result[3], radix),
|
|
34
|
+
s: parseInt(result[2], radix),
|
|
35
35
|
})
|
|
36
36
|
: undefined;
|
|
37
37
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Vector } from "../Core/Utils/Vector.js";
|
|
2
2
|
import { isNumber } from "./Utils.js";
|
|
3
|
+
import { percentDenominator } from "../Core/Utils/Constants.js";
|
|
3
4
|
let _random = Math.random;
|
|
4
|
-
const easings = new Map();
|
|
5
|
+
const easings = new Map(), double = 2, doublePI = Math.PI * double;
|
|
5
6
|
export function addEasing(name, easing) {
|
|
6
7
|
if (easings.get(name)) {
|
|
7
8
|
return;
|
|
@@ -9,13 +10,14 @@ export function addEasing(name, easing) {
|
|
|
9
10
|
easings.set(name, easing);
|
|
10
11
|
}
|
|
11
12
|
export function getEasing(name) {
|
|
12
|
-
return easings.get(name)
|
|
13
|
+
return easings.get(name) ?? ((value) => value);
|
|
13
14
|
}
|
|
14
15
|
export function setRandom(rnd = Math.random) {
|
|
15
16
|
_random = rnd;
|
|
16
17
|
}
|
|
17
18
|
export function getRandom() {
|
|
18
|
-
|
|
19
|
+
const min = 0, max = 1;
|
|
20
|
+
return clamp(_random(), min, max - Number.EPSILON);
|
|
19
21
|
}
|
|
20
22
|
export function clamp(num, min, max) {
|
|
21
23
|
return Math.min(Math.max(num, min), max);
|
|
@@ -24,10 +26,10 @@ export function mix(comp1, comp2, weight1, weight2) {
|
|
|
24
26
|
return Math.floor((comp1 * weight1 + comp2 * weight2) / (weight1 + weight2));
|
|
25
27
|
}
|
|
26
28
|
export function randomInRange(r) {
|
|
27
|
-
const max = getRangeMax(r);
|
|
29
|
+
const max = getRangeMax(r), minOffset = 0;
|
|
28
30
|
let min = getRangeMin(r);
|
|
29
31
|
if (max === min) {
|
|
30
|
-
min =
|
|
32
|
+
min = minOffset;
|
|
31
33
|
}
|
|
32
34
|
return getRandom() * (max - min) + min;
|
|
33
35
|
}
|
|
@@ -53,39 +55,44 @@ export function setRangeValue(source, value) {
|
|
|
53
55
|
: setRangeValue(min, max);
|
|
54
56
|
}
|
|
55
57
|
export function getDistances(pointA, pointB) {
|
|
56
|
-
const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y;
|
|
57
|
-
return { dx: dx, dy: dy, distance: Math.sqrt(dx **
|
|
58
|
+
const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y, squareExp = 2;
|
|
59
|
+
return { dx: dx, dy: dy, distance: Math.sqrt(dx ** squareExp + dy ** squareExp) };
|
|
58
60
|
}
|
|
59
61
|
export function getDistance(pointA, pointB) {
|
|
60
62
|
return getDistances(pointA, pointB).distance;
|
|
61
63
|
}
|
|
64
|
+
export function degToRad(degrees) {
|
|
65
|
+
const PIDeg = 180;
|
|
66
|
+
return (degrees * Math.PI) / PIDeg;
|
|
67
|
+
}
|
|
62
68
|
export function getParticleDirectionAngle(direction, position, center) {
|
|
63
69
|
if (isNumber(direction)) {
|
|
64
|
-
return (direction
|
|
70
|
+
return degToRad(direction);
|
|
65
71
|
}
|
|
72
|
+
const empty = 0, half = 0.5, quarter = 0.25, threeQuarter = half + quarter;
|
|
66
73
|
switch (direction) {
|
|
67
74
|
case "top":
|
|
68
|
-
return -Math.PI *
|
|
75
|
+
return -Math.PI * half;
|
|
69
76
|
case "top-right":
|
|
70
|
-
return -Math.PI *
|
|
77
|
+
return -Math.PI * quarter;
|
|
71
78
|
case "right":
|
|
72
|
-
return
|
|
79
|
+
return empty;
|
|
73
80
|
case "bottom-right":
|
|
74
|
-
return Math.PI *
|
|
81
|
+
return Math.PI * quarter;
|
|
75
82
|
case "bottom":
|
|
76
|
-
return Math.PI *
|
|
83
|
+
return Math.PI * half;
|
|
77
84
|
case "bottom-left":
|
|
78
|
-
return Math.PI *
|
|
85
|
+
return Math.PI * threeQuarter;
|
|
79
86
|
case "left":
|
|
80
87
|
return Math.PI;
|
|
81
88
|
case "top-left":
|
|
82
|
-
return -Math.PI *
|
|
89
|
+
return -Math.PI * threeQuarter;
|
|
83
90
|
case "inside":
|
|
84
91
|
return Math.atan2(center.y - position.y, center.x - position.x);
|
|
85
92
|
case "outside":
|
|
86
93
|
return Math.atan2(position.y - center.y, position.x - center.x);
|
|
87
94
|
default:
|
|
88
|
-
return getRandom() *
|
|
95
|
+
return getRandom() * doublePI;
|
|
89
96
|
}
|
|
90
97
|
}
|
|
91
98
|
export function getParticleBaseVelocity(direction) {
|
|
@@ -95,20 +102,21 @@ export function getParticleBaseVelocity(direction) {
|
|
|
95
102
|
return baseVelocity;
|
|
96
103
|
}
|
|
97
104
|
export function collisionVelocity(v1, v2, m1, m2) {
|
|
98
|
-
|
|
105
|
+
const double = 2;
|
|
106
|
+
return Vector.create((v1.x * (m1 - m2)) / (m1 + m2) + (v2.x * double * m2) / (m1 + m2), v1.y);
|
|
99
107
|
}
|
|
100
108
|
export function calcPositionFromSize(data) {
|
|
101
|
-
return data.position
|
|
109
|
+
return data.position?.x !== undefined && data.position.y !== undefined
|
|
102
110
|
? {
|
|
103
|
-
x: (data.position.x * data.size.width) /
|
|
104
|
-
y: (data.position.y * data.size.height) /
|
|
111
|
+
x: (data.position.x * data.size.width) / percentDenominator,
|
|
112
|
+
y: (data.position.y * data.size.height) / percentDenominator,
|
|
105
113
|
}
|
|
106
114
|
: undefined;
|
|
107
115
|
}
|
|
108
116
|
export function calcPositionOrRandomFromSize(data) {
|
|
109
117
|
return {
|
|
110
|
-
x: ((data.position?.x ?? getRandom() *
|
|
111
|
-
y: ((data.position?.y ?? getRandom() *
|
|
118
|
+
x: ((data.position?.x ?? getRandom() * percentDenominator) * data.size.width) / percentDenominator,
|
|
119
|
+
y: ((data.position?.y ?? getRandom() * percentDenominator) * data.size.height) / percentDenominator,
|
|
112
120
|
};
|
|
113
121
|
}
|
|
114
122
|
export function calcPositionOrRandomFromSizeRanged(data) {
|
|
@@ -132,5 +140,9 @@ export function calcExactPositionOrRandomFromSizeRanged(data) {
|
|
|
132
140
|
return calcExactPositionOrRandomFromSize({ size: data.size, position });
|
|
133
141
|
}
|
|
134
142
|
export function parseAlpha(input) {
|
|
135
|
-
|
|
143
|
+
const defaultAlpha = 1;
|
|
144
|
+
if (!input) {
|
|
145
|
+
return defaultAlpha;
|
|
146
|
+
}
|
|
147
|
+
return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
|
|
136
148
|
}
|
|
@@ -24,13 +24,13 @@ export class RgbColorManager {
|
|
|
24
24
|
if (!input.startsWith(this.stringPrefix)) {
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
const regex = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.%]+)\s*)?\)/i, result = regex.exec(input);
|
|
27
|
+
const regex = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([\d.%]+)\s*)?\)/i, result = regex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
|
|
28
28
|
return result
|
|
29
29
|
? {
|
|
30
|
-
a: result.length >
|
|
31
|
-
b: parseInt(result[3],
|
|
32
|
-
g: parseInt(result[2],
|
|
33
|
-
r: parseInt(result[1],
|
|
30
|
+
a: result.length > minLength ? parseAlpha(result[5]) : defaultAlpha,
|
|
31
|
+
b: parseInt(result[3], radix),
|
|
32
|
+
g: parseInt(result[2], radix),
|
|
33
|
+
r: parseInt(result[1], radix),
|
|
34
34
|
}
|
|
35
35
|
: undefined;
|
|
36
36
|
}
|
package/browser/Utils/Utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { collisionVelocity, getDistances, getRandom, getRangeMax, getRangeMin, getRangeValue, randomInRange, } from "./NumberUtils.js";
|
|
1
|
+
import { clamp, collisionVelocity, getDistances, getRandom, getRangeMax, getRangeMin, getRangeValue, randomInRange, } from "./NumberUtils.js";
|
|
2
|
+
import { halfRandom, millisecondsToSeconds, percentDenominator } from "../Core/Utils/Constants.js";
|
|
2
3
|
import { Vector } from "../Core/Utils/Vector.js";
|
|
3
4
|
const _logger = {
|
|
4
5
|
debug: console.debug,
|
|
@@ -20,15 +21,15 @@ export function getLogger() {
|
|
|
20
21
|
return _logger;
|
|
21
22
|
}
|
|
22
23
|
function rectSideBounce(data) {
|
|
23
|
-
const res = { bounced: false }, { pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor } = data;
|
|
24
|
+
const res = { bounced: false }, { pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor } = data, half = 0.5, minVelocity = 0;
|
|
24
25
|
if (pOtherSide.min < rectOtherSide.min ||
|
|
25
26
|
pOtherSide.min > rectOtherSide.max ||
|
|
26
27
|
pOtherSide.max < rectOtherSide.min ||
|
|
27
28
|
pOtherSide.max > rectOtherSide.max) {
|
|
28
29
|
return res;
|
|
29
30
|
}
|
|
30
|
-
if ((pSide.max >= rectSide.min && pSide.max <= (rectSide.max + rectSide.min) *
|
|
31
|
-
(pSide.min <= rectSide.max && pSide.min > (rectSide.max + rectSide.min) *
|
|
31
|
+
if ((pSide.max >= rectSide.min && pSide.max <= (rectSide.max + rectSide.min) * half && velocity > minVelocity) ||
|
|
32
|
+
(pSide.min <= rectSide.max && pSide.min > (rectSide.max + rectSide.min) * half && velocity < minVelocity)) {
|
|
32
33
|
res.velocity = velocity * -factor;
|
|
33
34
|
res.bounced = true;
|
|
34
35
|
}
|
|
@@ -65,7 +66,8 @@ export function safeMutationObserver(callback) {
|
|
|
65
66
|
return new MutationObserver(callback);
|
|
66
67
|
}
|
|
67
68
|
export function isInArray(value, array) {
|
|
68
|
-
|
|
69
|
+
const invalidIndex = -1;
|
|
70
|
+
return value === array || (isArray(array) && array.indexOf(value) > invalidIndex);
|
|
69
71
|
}
|
|
70
72
|
export async function loadFont(font, weight) {
|
|
71
73
|
try {
|
|
@@ -81,7 +83,8 @@ export function itemFromArray(array, index, useIndex = true) {
|
|
|
81
83
|
return array[index !== undefined && useIndex ? index % array.length : arrayRandomIndex(array)];
|
|
82
84
|
}
|
|
83
85
|
export function isPointInside(point, size, offset, radius, direction) {
|
|
84
|
-
|
|
86
|
+
const minRadius = 0;
|
|
87
|
+
return areBoundsInside(calculateBounds(point, radius ?? minRadius), size, offset, direction);
|
|
85
88
|
}
|
|
86
89
|
export function areBoundsInside(bounds, size, offset, direction) {
|
|
87
90
|
let inside = true;
|
|
@@ -171,8 +174,8 @@ export function circleBounceDataFromParticle(p) {
|
|
|
171
174
|
};
|
|
172
175
|
}
|
|
173
176
|
export function circleBounce(p1, p2) {
|
|
174
|
-
const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity), [pos1, pos2] = [p1.position, p2.position], { dx: xDist, dy: yDist } = getDistances(pos2, pos1);
|
|
175
|
-
if (xVelocityDiff * xDist + yVelocityDiff * yDist <
|
|
177
|
+
const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity), [pos1, pos2] = [p1.position, p2.position], { dx: xDist, dy: yDist } = getDistances(pos2, pos1), minimumDistance = 0;
|
|
178
|
+
if (xVelocityDiff * xDist + yVelocityDiff * yDist < minimumDistance) {
|
|
176
179
|
return;
|
|
177
180
|
}
|
|
178
181
|
const angle = -Math.atan2(yDist, xDist), m1 = p1.mass, m2 = p2.mass, u1 = p1.velocity.rotate(angle), u2 = p2.velocity.rotate(angle), v1 = collisionVelocity(u1, u2, m1, m2), v2 = collisionVelocity(u2, u1, m1, m2), vFinal1 = v1.rotate(-angle), vFinal2 = v2.rotate(-angle);
|
|
@@ -240,17 +243,22 @@ export function rectBounce(particle, divBounds) {
|
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
245
|
export function executeOnSingleOrMultiple(obj, callback) {
|
|
243
|
-
|
|
246
|
+
const defaultIndex = 0;
|
|
247
|
+
return isArray(obj) ? obj.map((item, index) => callback(item, index)) : callback(obj, defaultIndex);
|
|
244
248
|
}
|
|
245
249
|
export function itemFromSingleOrMultiple(obj, index, useIndex) {
|
|
246
250
|
return isArray(obj) ? itemFromArray(obj, index, useIndex) : obj;
|
|
247
251
|
}
|
|
248
252
|
export function findItemFromSingleOrMultiple(obj, callback) {
|
|
249
|
-
|
|
253
|
+
if (isArray(obj)) {
|
|
254
|
+
return obj.find((t, index) => callback(t, index));
|
|
255
|
+
}
|
|
256
|
+
const defaultIndex = 0;
|
|
257
|
+
return callback(obj, defaultIndex) ? obj : undefined;
|
|
250
258
|
}
|
|
251
259
|
export function initParticleNumericAnimationValue(options, pxRatio) {
|
|
252
260
|
const valueRange = options.value, animationOptions = options.animation, res = {
|
|
253
|
-
delayTime: getRangeValue(animationOptions.delay) *
|
|
261
|
+
delayTime: getRangeValue(animationOptions.delay) * millisecondsToSeconds,
|
|
254
262
|
enable: animationOptions.enable,
|
|
255
263
|
value: getRangeValue(options.value) * pxRatio,
|
|
256
264
|
max: getRangeMax(valueRange) * pxRatio,
|
|
@@ -258,9 +266,9 @@ export function initParticleNumericAnimationValue(options, pxRatio) {
|
|
|
258
266
|
loops: 0,
|
|
259
267
|
maxLoops: getRangeValue(animationOptions.count),
|
|
260
268
|
time: 0,
|
|
261
|
-
};
|
|
269
|
+
}, decayOffset = 1;
|
|
262
270
|
if (animationOptions.enable) {
|
|
263
|
-
res.decay =
|
|
271
|
+
res.decay = decayOffset - getRangeValue(animationOptions.decay);
|
|
264
272
|
switch (animationOptions.mode) {
|
|
265
273
|
case "increase":
|
|
266
274
|
res.status = "increasing";
|
|
@@ -269,7 +277,7 @@ export function initParticleNumericAnimationValue(options, pxRatio) {
|
|
|
269
277
|
res.status = "decreasing";
|
|
270
278
|
break;
|
|
271
279
|
case "random":
|
|
272
|
-
res.status = getRandom() >=
|
|
280
|
+
res.status = getRandom() >= halfRandom ? "increasing" : "decreasing";
|
|
273
281
|
break;
|
|
274
282
|
}
|
|
275
283
|
const autoStatus = animationOptions.mode === "auto";
|
|
@@ -290,7 +298,7 @@ export function initParticleNumericAnimationValue(options, pxRatio) {
|
|
|
290
298
|
default:
|
|
291
299
|
res.value = randomInRange(res);
|
|
292
300
|
if (autoStatus) {
|
|
293
|
-
res.status = getRandom() >=
|
|
301
|
+
res.status = getRandom() >= halfRandom ? "increasing" : "decreasing";
|
|
294
302
|
}
|
|
295
303
|
break;
|
|
296
304
|
}
|
|
@@ -307,14 +315,14 @@ function getPositionOrSize(positionOrSize, canvasSize) {
|
|
|
307
315
|
const isPosition = "x" in positionOrSize;
|
|
308
316
|
if (isPosition) {
|
|
309
317
|
return {
|
|
310
|
-
x: (positionOrSize.x /
|
|
311
|
-
y: (positionOrSize.y /
|
|
318
|
+
x: (positionOrSize.x / percentDenominator) * canvasSize.width,
|
|
319
|
+
y: (positionOrSize.y / percentDenominator) * canvasSize.height,
|
|
312
320
|
};
|
|
313
321
|
}
|
|
314
322
|
else {
|
|
315
323
|
return {
|
|
316
|
-
width: (positionOrSize.width /
|
|
317
|
-
height: (positionOrSize.height /
|
|
324
|
+
width: (positionOrSize.width / percentDenominator) * canvasSize.width,
|
|
325
|
+
height: (positionOrSize.height / percentDenominator) * canvasSize.height,
|
|
318
326
|
};
|
|
319
327
|
}
|
|
320
328
|
}
|
|
@@ -342,3 +350,78 @@ export function isObject(arg) {
|
|
|
342
350
|
export function isArray(arg) {
|
|
343
351
|
return Array.isArray(arg);
|
|
344
352
|
}
|
|
353
|
+
function checkDestroy(particle, destroyType, value, minValue, maxValue) {
|
|
354
|
+
switch (destroyType) {
|
|
355
|
+
case "max":
|
|
356
|
+
if (value >= maxValue) {
|
|
357
|
+
particle.destroy();
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
case "min":
|
|
361
|
+
if (value <= minValue) {
|
|
362
|
+
particle.destroy();
|
|
363
|
+
}
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
export function updateAnimation(particle, data, changeDirection, destroyType, delta) {
|
|
368
|
+
const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minDecay = 1;
|
|
369
|
+
if (particle.destroyed ||
|
|
370
|
+
!data ||
|
|
371
|
+
!data.enable ||
|
|
372
|
+
((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const velocity = (data.velocity ?? minVelocity) * delta.factor, minValue = data.min, maxValue = data.max, decay = data.decay ?? minDecay;
|
|
376
|
+
if (!data.time) {
|
|
377
|
+
data.time = 0;
|
|
378
|
+
}
|
|
379
|
+
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
|
380
|
+
data.time += delta.value;
|
|
381
|
+
}
|
|
382
|
+
if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
switch (data.status) {
|
|
386
|
+
case "increasing":
|
|
387
|
+
if (data.value >= maxValue) {
|
|
388
|
+
if (changeDirection) {
|
|
389
|
+
data.status = "decreasing";
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
data.value -= maxValue;
|
|
393
|
+
}
|
|
394
|
+
if (!data.loops) {
|
|
395
|
+
data.loops = minLoops;
|
|
396
|
+
}
|
|
397
|
+
data.loops++;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
data.value += velocity;
|
|
401
|
+
}
|
|
402
|
+
break;
|
|
403
|
+
case "decreasing":
|
|
404
|
+
if (data.value <= minValue) {
|
|
405
|
+
if (changeDirection) {
|
|
406
|
+
data.status = "increasing";
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
data.value += maxValue;
|
|
410
|
+
}
|
|
411
|
+
if (!data.loops) {
|
|
412
|
+
data.loops = minLoops;
|
|
413
|
+
}
|
|
414
|
+
data.loops++;
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
data.value -= velocity;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (data.velocity && decay !== identity) {
|
|
421
|
+
data.velocity *= decay;
|
|
422
|
+
}
|
|
423
|
+
checkDestroy(particle, destroyType, data.value, minValue, maxValue);
|
|
424
|
+
if (!particle.destroyed) {
|
|
425
|
+
data.value = clamp(data.value, minValue, maxValue);
|
|
426
|
+
}
|
|
427
|
+
}
|