@tsparticles/engine 3.0.3 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/373.min.js +2 -0
  2. package/373.min.js.LICENSE.txt +1 -0
  3. package/438.min.js +2 -0
  4. package/438.min.js.LICENSE.txt +1 -0
  5. package/README.md +337 -216
  6. package/browser/Core/Canvas.js +102 -49
  7. package/browser/Core/Container.js +53 -41
  8. package/browser/Core/Engine.js +47 -32
  9. package/browser/Core/Particle.js +46 -48
  10. package/browser/Core/Particles.js +70 -57
  11. package/browser/Core/Retina.js +5 -4
  12. package/browser/Core/Utils/Circle.js +4 -3
  13. package/browser/Core/Utils/Constants.js +3 -0
  14. package/browser/Core/Utils/EventListeners.js +19 -16
  15. package/browser/Core/Utils/ExternalInteractorBase.js +1 -1
  16. package/browser/Core/Utils/InteractionManager.js +17 -8
  17. package/browser/Core/Utils/ParticlesInteractorBase.js +1 -1
  18. package/browser/Core/Utils/QuadTree.js +5 -3
  19. package/browser/Core/Utils/Vector.js +7 -2
  20. package/browser/Core/Utils/Vector3d.js +14 -9
  21. package/browser/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  22. package/browser/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  23. package/browser/Options/Classes/ManualParticle.js +3 -2
  24. package/browser/Options/Classes/Options.js +3 -0
  25. package/browser/Utils/CanvasUtils.js +50 -40
  26. package/browser/Utils/ColorUtils.js +124 -45
  27. package/browser/Utils/EventDispatcher.js +6 -5
  28. package/browser/Utils/HslColorManager.js +5 -5
  29. package/browser/Utils/NumberUtils.js +35 -23
  30. package/browser/Utils/RgbColorManager.js +5 -5
  31. package/browser/Utils/Utils.js +102 -19
  32. package/cjs/Core/Canvas.js +102 -49
  33. package/cjs/Core/Container.js +53 -41
  34. package/cjs/Core/Engine.js +70 -32
  35. package/cjs/Core/Particle.js +45 -47
  36. package/cjs/Core/Particles.js +93 -57
  37. package/cjs/Core/Retina.js +5 -4
  38. package/cjs/Core/Utils/Circle.js +4 -3
  39. package/cjs/Core/Utils/Constants.js +4 -1
  40. package/cjs/Core/Utils/EventListeners.js +18 -15
  41. package/cjs/Core/Utils/ExternalInteractorBase.js +1 -1
  42. package/cjs/Core/Utils/InteractionManager.js +17 -8
  43. package/cjs/Core/Utils/ParticlesInteractorBase.js +1 -1
  44. package/cjs/Core/Utils/QuadTree.js +5 -3
  45. package/cjs/Core/Utils/Vector.js +7 -2
  46. package/cjs/Core/Utils/Vector3d.js +14 -9
  47. package/cjs/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  48. package/cjs/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  49. package/cjs/Options/Classes/ManualParticle.js +3 -2
  50. package/cjs/Options/Classes/Options.js +3 -0
  51. package/cjs/Utils/CanvasUtils.js +50 -40
  52. package/cjs/Utils/ColorUtils.js +126 -45
  53. package/cjs/Utils/EventDispatcher.js +6 -5
  54. package/cjs/Utils/HslColorManager.js +5 -5
  55. package/cjs/Utils/NumberUtils.js +37 -24
  56. package/cjs/Utils/RgbColorManager.js +5 -5
  57. package/cjs/Utils/Utils.js +103 -19
  58. package/dist_browser_Core_Container_js.js +92 -0
  59. package/dist_browser_Core_Particle_js.js +32 -0
  60. package/esm/Core/Canvas.js +102 -49
  61. package/esm/Core/Container.js +53 -41
  62. package/esm/Core/Engine.js +47 -32
  63. package/esm/Core/Particle.js +46 -48
  64. package/esm/Core/Particles.js +70 -57
  65. package/esm/Core/Retina.js +5 -4
  66. package/esm/Core/Utils/Circle.js +4 -3
  67. package/esm/Core/Utils/Constants.js +3 -0
  68. package/esm/Core/Utils/EventListeners.js +19 -16
  69. package/esm/Core/Utils/ExternalInteractorBase.js +1 -1
  70. package/esm/Core/Utils/InteractionManager.js +17 -8
  71. package/esm/Core/Utils/ParticlesInteractorBase.js +1 -1
  72. package/esm/Core/Utils/QuadTree.js +5 -3
  73. package/esm/Core/Utils/Vector.js +7 -2
  74. package/esm/Core/Utils/Vector3d.js +14 -9
  75. package/esm/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  76. package/esm/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  77. package/esm/Options/Classes/ManualParticle.js +3 -2
  78. package/esm/Options/Classes/Options.js +3 -0
  79. package/esm/Utils/CanvasUtils.js +50 -40
  80. package/esm/Utils/ColorUtils.js +124 -45
  81. package/esm/Utils/EventDispatcher.js +6 -5
  82. package/esm/Utils/HslColorManager.js +5 -5
  83. package/esm/Utils/NumberUtils.js +35 -23
  84. package/esm/Utils/RgbColorManager.js +5 -5
  85. package/esm/Utils/Utils.js +102 -19
  86. package/package.json +1 -1
  87. package/report.html +3 -3
  88. package/tsparticles.engine.js +894 -5461
  89. package/tsparticles.engine.min.js +1 -1
  90. package/tsparticles.engine.min.js.LICENSE.txt +1 -1
  91. package/types/Core/Canvas.d.ts +5 -3
  92. package/types/Core/Container.d.ts +1 -1
  93. package/types/Core/Engine.d.ts +13 -8
  94. package/types/Core/Interfaces/IContainerPlugin.d.ts +3 -3
  95. package/types/Core/Interfaces/IEffectDrawer.d.ts +3 -3
  96. package/types/Core/Interfaces/IMovePathGenerator.d.ts +2 -2
  97. package/types/Core/Interfaces/IParticleHslAnimation.d.ts +4 -4
  98. package/types/Core/Interfaces/IParticleMover.d.ts +2 -2
  99. package/types/Core/Interfaces/IParticleUpdater.d.ts +2 -2
  100. package/types/Core/Interfaces/IParticleValueAnimation.d.ts +4 -0
  101. package/types/Core/Interfaces/IPlugin.d.ts +1 -1
  102. package/types/Core/Interfaces/IShapeDrawData.d.ts +2 -2
  103. package/types/Core/Interfaces/IShapeDrawer.d.ts +4 -4
  104. package/types/Core/Particle.d.ts +3 -3
  105. package/types/Core/Particles.d.ts +12 -8
  106. package/types/Core/Utils/Constants.d.ts +3 -0
  107. package/types/Core/Utils/ExternalInteractorBase.d.ts +1 -1
  108. package/types/Core/Utils/InteractionManager.d.ts +3 -3
  109. package/types/Core/Utils/ParticlesInteractorBase.d.ts +1 -1
  110. package/types/Core/Utils/Point.d.ts +1 -1
  111. package/types/Enums/Modes/OutMode.d.ts +0 -3
  112. package/types/Options/Classes/BackgroundMask/BackgroundMaskCover.d.ts +2 -1
  113. package/types/Options/Classes/Options.d.ts +1 -0
  114. package/types/Options/Classes/Particles/Move/Move.d.ts +1 -2
  115. package/types/Options/Classes/Particles/Move/OutModes.d.ts +5 -6
  116. package/types/Options/Interfaces/BackgroundMask/IBackgroundMask.d.ts +2 -1
  117. package/types/Options/Interfaces/BackgroundMask/IBackgroundMaskCover.d.ts +2 -1
  118. package/types/Options/Interfaces/IOptions.d.ts +1 -0
  119. package/types/Options/Interfaces/Interactivity/Modes/IModes.d.ts +1 -3
  120. package/types/Options/Interfaces/Particles/Move/IMove.d.ts +2 -2
  121. package/types/Options/Interfaces/Particles/Move/IOutModes.d.ts +6 -6
  122. package/types/Types/CustomEventArgs.d.ts +2 -2
  123. package/types/Types/ExportResult.d.ts +2 -2
  124. package/types/Types/ParticlesGroups.d.ts +1 -3
  125. package/types/Types/PathOptions.d.ts +1 -3
  126. package/types/Types/ShapeData.d.ts +1 -3
  127. package/types/Utils/CanvasUtils.d.ts +9 -8
  128. package/types/Utils/ColorUtils.d.ts +5 -0
  129. package/types/Utils/NumberUtils.d.ts +2 -2
  130. package/types/Utils/Utils.d.ts +9 -6
  131. package/umd/Core/Canvas.js +102 -49
  132. package/umd/Core/Container.js +54 -42
  133. package/umd/Core/Engine.js +72 -33
  134. package/umd/Core/Particle.js +46 -48
  135. package/umd/Core/Particles.js +95 -58
  136. package/umd/Core/Retina.js +5 -4
  137. package/umd/Core/Utils/Circle.js +4 -3
  138. package/umd/Core/Utils/Constants.js +4 -1
  139. package/umd/Core/Utils/EventListeners.js +18 -15
  140. package/umd/Core/Utils/ExternalInteractorBase.js +1 -1
  141. package/umd/Core/Utils/InteractionManager.js +17 -8
  142. package/umd/Core/Utils/ParticlesInteractorBase.js +1 -1
  143. package/umd/Core/Utils/QuadTree.js +5 -3
  144. package/umd/Core/Utils/Vector.js +7 -2
  145. package/umd/Core/Utils/Vector3d.js +14 -9
  146. package/umd/Options/Classes/BackgroundMask/BackgroundMask.js +1 -1
  147. package/umd/Options/Classes/BackgroundMask/BackgroundMaskCover.js +3 -2
  148. package/umd/Options/Classes/ManualParticle.js +3 -2
  149. package/umd/Options/Classes/Options.js +3 -0
  150. package/umd/Utils/CanvasUtils.js +50 -40
  151. package/umd/Utils/ColorUtils.js +127 -46
  152. package/umd/Utils/EventDispatcher.js +6 -5
  153. package/umd/Utils/HslColorManager.js +5 -5
  154. package/umd/Utils/NumberUtils.js +38 -25
  155. package/umd/Utils/RgbColorManager.js +5 -5
  156. package/umd/Utils/Utils.js +104 -20
@@ -1,8 +1,6 @@
1
1
  import { OptionsColor } from "../OptionsColor.js";
2
2
  export class BackgroundMaskCover {
3
3
  constructor() {
4
- this.color = new OptionsColor();
5
- this.color.value = "#fff";
6
4
  this.opacity = 1;
7
5
  }
8
6
  load(data) {
@@ -12,6 +10,9 @@ export class BackgroundMaskCover {
12
10
  if (data.color !== undefined) {
13
11
  this.color = OptionsColor.create(this.color, data.color);
14
12
  }
13
+ if (data.image !== undefined) {
14
+ this.image = data.image;
15
+ }
15
16
  if (data.opacity !== undefined) {
16
17
  this.opacity = data.opacity;
17
18
  }
@@ -1,4 +1,5 @@
1
1
  import { deepExtend } from "../../Utils/Utils.js";
2
+ const defaultPosition = 50;
2
3
  export class ManualParticle {
3
4
  load(data) {
4
5
  if (!data) {
@@ -6,8 +7,8 @@ export class ManualParticle {
6
7
  }
7
8
  if (data.position) {
8
9
  this.position = {
9
- x: data.position.x ?? 50,
10
- y: data.position.y ?? 50,
10
+ x: data.position.x ?? defaultPosition,
11
+ y: data.position.y ?? defaultPosition,
11
12
  mode: data.position.mode ?? "percent",
12
13
  };
13
14
  }
@@ -53,6 +53,9 @@ export class Options {
53
53
  if (data.clear !== undefined) {
54
54
  this.clear = data.clear;
55
55
  }
56
+ if (data.key !== undefined) {
57
+ this.key = data.key;
58
+ }
56
59
  if (data.name !== undefined) {
57
60
  this.name = data.name;
58
61
  }
@@ -1,4 +1,5 @@
1
1
  import { getStyleFromRgb } from "./ColorUtils.js";
2
+ const origin = { x: 0, y: 0 };
2
3
  export function drawLine(context, begin, end) {
3
4
  context.beginPath();
4
5
  context.moveTo(begin.x, begin.y);
@@ -7,29 +8,28 @@ export function drawLine(context, begin, end) {
7
8
  }
8
9
  export function paintBase(context, dimension, baseColor) {
9
10
  context.fillStyle = baseColor ?? "rgba(0,0,0,0)";
10
- context.fillRect(0, 0, dimension.width, dimension.height);
11
+ context.fillRect(origin.x, origin.y, dimension.width, dimension.height);
11
12
  }
12
13
  export function paintImage(context, dimension, image, opacity) {
13
14
  if (!image) {
14
15
  return;
15
16
  }
16
17
  context.globalAlpha = opacity;
17
- context.drawImage(image, 0, 0, dimension.width, dimension.height);
18
+ context.drawImage(image, origin.x, origin.y, dimension.width, dimension.height);
18
19
  context.globalAlpha = 1;
19
20
  }
20
21
  export function clear(context, dimension) {
21
- context.clearRect(0, 0, dimension.width, dimension.height);
22
+ context.clearRect(origin.x, origin.y, dimension.width, dimension.height);
22
23
  }
23
- export function drawParticle(data) {
24
- const { container, context, particle, delta, colorStyles, backgroundMask, composite, radius, opacity, shadow, transform, } = data;
25
- const pos = particle.getPosition(), angle = particle.rotation + (particle.pathRotation ? particle.velocity.angle : 0), rotateData = {
24
+ export async function drawParticle(data) {
25
+ const { container, context, particle, delta, colorStyles, backgroundMask, composite, radius, opacity, shadow, transform, } = data, pos = particle.getPosition(), defaultAngle = 0, angle = particle.rotation + (particle.pathRotation ? particle.velocity.angle : defaultAngle), rotateData = {
26
26
  sin: Math.sin(angle),
27
27
  cos: Math.cos(angle),
28
- }, transformData = {
29
- a: rotateData.cos * (transform.a ?? 1),
30
- b: rotateData.sin * (transform.b ?? 1),
31
- c: -rotateData.sin * (transform.c ?? 1),
32
- d: rotateData.cos * (transform.d ?? 1),
28
+ }, defaultTransformFactor = 1, transformData = {
29
+ a: rotateData.cos * (transform.a ?? defaultTransformFactor),
30
+ b: rotateData.sin * (transform.b ?? defaultTransformFactor),
31
+ c: -rotateData.sin * (transform.c ?? defaultTransformFactor),
32
+ d: rotateData.cos * (transform.d ?? defaultTransformFactor),
33
33
  };
34
34
  context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
35
35
  if (backgroundMask) {
@@ -45,29 +45,28 @@ export function drawParticle(data) {
45
45
  if (colorStyles.fill) {
46
46
  context.fillStyle = colorStyles.fill;
47
47
  }
48
- const strokeWidth = particle.strokeWidth ?? 0;
48
+ const minStrokeWidth = 0, strokeWidth = particle.strokeWidth ?? minStrokeWidth;
49
49
  context.lineWidth = strokeWidth;
50
50
  if (colorStyles.stroke) {
51
51
  context.strokeStyle = colorStyles.stroke;
52
52
  }
53
- const drawData = { container, context, particle, radius, opacity, delta, transformData };
54
- context.beginPath();
55
- drawShape(drawData);
56
- if (particle.shapeClose) {
57
- context.closePath();
58
- }
59
- if (strokeWidth > 0) {
60
- context.stroke();
61
- }
62
- if (particle.shapeFill) {
63
- context.fill();
64
- }
65
- drawShapeAfterDraw(drawData);
66
- drawEffect(drawData);
53
+ const drawData = {
54
+ container,
55
+ context,
56
+ particle,
57
+ radius,
58
+ opacity,
59
+ delta,
60
+ transformData,
61
+ strokeWidth,
62
+ };
63
+ await drawShape(drawData);
64
+ await drawShapeAfterDraw(drawData);
65
+ await drawEffect(drawData);
67
66
  context.globalCompositeOperation = "source-over";
68
- context.setTransform(1, 0, 0, 1, 0, 0);
67
+ context.resetTransform();
69
68
  }
70
- export function drawEffect(data) {
69
+ export async function drawEffect(data) {
71
70
  const { container, context, particle, radius, opacity, delta, transformData } = data;
72
71
  if (!particle.effect) {
73
72
  return;
@@ -76,7 +75,7 @@ export function drawEffect(data) {
76
75
  if (!drawer) {
77
76
  return;
78
77
  }
79
- drawer.draw({
78
+ await drawer.draw({
80
79
  context,
81
80
  particle,
82
81
  radius,
@@ -86,8 +85,8 @@ export function drawEffect(data) {
86
85
  transformData: { ...transformData },
87
86
  });
88
87
  }
89
- export function drawShape(data) {
90
- const { container, context, particle, radius, opacity, delta, transformData } = data;
88
+ export async function drawShape(data) {
89
+ const { container, context, particle, radius, opacity, delta, strokeWidth, transformData } = data, minStrokeWidth = 0;
91
90
  if (!particle.shape) {
92
91
  return;
93
92
  }
@@ -95,7 +94,8 @@ export function drawShape(data) {
95
94
  if (!drawer) {
96
95
  return;
97
96
  }
98
- drawer.draw({
97
+ context.beginPath();
98
+ await drawer.draw({
99
99
  context,
100
100
  particle,
101
101
  radius,
@@ -104,17 +104,26 @@ export function drawShape(data) {
104
104
  pixelRatio: container.retina.pixelRatio,
105
105
  transformData: { ...transformData },
106
106
  });
107
+ if (particle.shapeClose) {
108
+ context.closePath();
109
+ }
110
+ if (strokeWidth > minStrokeWidth) {
111
+ context.stroke();
112
+ }
113
+ if (particle.shapeFill) {
114
+ context.fill();
115
+ }
107
116
  }
108
- export function drawShapeAfterDraw(data) {
117
+ export async function drawShapeAfterDraw(data) {
109
118
  const { container, context, particle, radius, opacity, delta, transformData } = data;
110
119
  if (!particle.shape) {
111
120
  return;
112
121
  }
113
122
  const drawer = container.shapeDrawers.get(particle.shape);
114
- if (!drawer || !drawer.afterDraw) {
123
+ if (!drawer?.afterDraw) {
115
124
  return;
116
125
  }
117
- drawer.afterDraw({
126
+ await drawer.afterDraw({
118
127
  context,
119
128
  particle,
120
129
  radius,
@@ -124,22 +133,23 @@ export function drawShapeAfterDraw(data) {
124
133
  transformData: { ...transformData },
125
134
  });
126
135
  }
127
- export function drawPlugin(context, plugin, delta) {
136
+ export async function drawPlugin(context, plugin, delta) {
128
137
  if (!plugin.draw) {
129
138
  return;
130
139
  }
131
- plugin.draw(context, delta);
140
+ await plugin.draw(context, delta);
132
141
  }
133
- export function drawParticlePlugin(context, plugin, particle, delta) {
142
+ export async function drawParticlePlugin(context, plugin, particle, delta) {
134
143
  if (!plugin.drawParticle) {
135
144
  return;
136
145
  }
137
- plugin.drawParticle(context, particle, delta);
146
+ await plugin.drawParticle(context, particle, delta);
138
147
  }
139
148
  export function alterHsl(color, type, value) {
149
+ const lFactor = 1;
140
150
  return {
141
151
  h: color.h,
142
152
  s: color.s,
143
- l: color.l + (type === "darken" ? -1 : 1) * value,
153
+ l: color.l + (type === "darken" ? -lFactor : lFactor) * value,
144
154
  };
145
155
  }
@@ -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 ? parseInt(result[4], 16) / 0xff : 1,
19
- b: parseInt(result[3], 16),
20
- g: parseInt(result[2], 16),
21
- r: parseInt(result[1], 16),
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 / 255, g1 = color.g / 255, b1 = color.b / 255, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
75
- h: 0,
76
- l: (max + min) * 0.5,
77
- s: 0,
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 < 0.5 ? (max - min) / (max + min) : (max - min) / (2.0 - max - min);
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 ? 2.0 + (b1 - r1) / (max - min) : 4.0 + (r1 - g1) / (max - min));
87
+ : (res.h = g1 === max ? double + (b1 - r1) / (max - min) : double * double + (r1 - g1) / (max - min));
85
88
  }
86
- res.l *= 100;
87
- res.s *= 100;
88
- res.h *= 60;
89
- if (res.h < 0) {
90
- res.h += 360;
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 >= 360) {
93
- res.h -= 360;
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 % 360) + 360) % 360, s = Math.max(0, Math.min(100, hsl.s)), l = Math.max(0, Math.min(100, hsl.l)), hNormalized = h / 360, sNormalized = s / 100, lNormalized = l / 100;
105
- if (s === 0) {
106
- const grayscaleValue = Math.round(lNormalized * 255);
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
- if (temp3 < 0) {
111
- temp3 += 1;
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 > 1) {
114
- temp3 -= 1;
117
+ if (temp3 > temp3Max) {
118
+ temp3--;
115
119
  }
116
- if (temp3 * 6 < 1) {
117
- return temp1 + (temp2 - temp1) * 6 * temp3;
120
+ if (temp3 * sextuple < temp3Max) {
121
+ return temp1 + (temp2 - temp1) * sextuple * temp3;
118
122
  }
119
- if (temp3 * 2 < 1) {
123
+ if (temp3 * double < temp3Max) {
120
124
  return temp2;
121
125
  }
122
- if (temp3 * 3 < 2) {
123
- return temp1 + (temp2 - temp1) * (2 / 3 - temp3) * 6;
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
- }, temp1 = lNormalized < 0.5 ? lNormalized * (1 + sNormalized) : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = 2 * lNormalized - temp1, red = Math.min(255, 255 * channel(temp2, temp1, hNormalized + 1 / 3)), green = Math.min(255, 255 * channel(temp2, temp1, hNormalized)), blue = Math.min(255, 255 * channel(temp2, temp1, hNormalized - 1 / 3));
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 ?? 0;
146
+ const defaultMin = 0, fixedMin = min ?? defaultMin, rgbMax = 256;
140
147
  return {
141
- b: Math.floor(randomInRange(setRangeValue(fixedMin, 256))),
142
- g: Math.floor(randomInRange(setRangeValue(fixedMin, 256))),
143
- r: Math.floor(randomInRange(setRangeValue(fixedMin, 256))),
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
- return `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity ?? 1})`;
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
- return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${opacity ?? 1})`;
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) / 100) * reduceFactor;
243
- colorValue.decay = 1 - getRangeValue(colorAnimation.decay);
252
+ colorValue.velocity = (getRangeValue(colorAnimation.speed) / percentDenominator) * reduceFactor;
253
+ colorValue.decay = decayOffset - getRangeValue(colorAnimation.decay);
244
254
  colorValue.status = "increasing";
245
- colorValue.loops = 0;
255
+ colorValue.loops = defaultLoops;
246
256
  colorValue.maxLoops = getRangeValue(colorAnimation.count);
247
- colorValue.time = 0;
248
- colorValue.delayTime = getRangeValue(colorAnimation.delay) * 1000;
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 = 0;
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 && listeners.forEach((handler) => handler(args));
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 < 0) {
34
+ const length = arr.length, idx = arr.indexOf(listener), minIndex = 0;
35
+ if (idx < minIndex) {
36
36
  return;
37
37
  }
38
- if (length === 1) {
38
+ const deleteCount = 1;
39
+ if (length === deleteCount) {
39
40
  this._listeners.delete(type);
40
41
  }
41
42
  else {
42
- arr.splice(idx, 1);
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 > 4 ? parseAlpha(result[5]) : 1,
32
- h: parseInt(result[1], 10),
33
- l: parseInt(result[3], 10),
34
- s: parseInt(result[2], 10),
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) || ((value) => value);
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
- return clamp(_random(), 0, 1 - 1e-16);
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 = 0;
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 ** 2 + dy ** 2) };
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 * Math.PI) / 180;
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 * 0.5;
75
+ return -Math.PI * half;
69
76
  case "top-right":
70
- return -Math.PI * 0.25;
77
+ return -Math.PI * quarter;
71
78
  case "right":
72
- return 0;
79
+ return empty;
73
80
  case "bottom-right":
74
- return Math.PI * 0.25;
81
+ return Math.PI * quarter;
75
82
  case "bottom":
76
- return Math.PI * 0.5;
83
+ return Math.PI * half;
77
84
  case "bottom-left":
78
- return Math.PI * 0.75;
85
+ return Math.PI * threeQuarter;
79
86
  case "left":
80
87
  return Math.PI;
81
88
  case "top-left":
82
- return -Math.PI * 0.75;
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() * Math.PI * 2;
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
- return Vector.create((v1.x * (m1 - m2)) / (m1 + m2) + (v2.x * 2 * m2) / (m1 + m2), v1.y);
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 && data.position.x !== undefined && data.position.y !== undefined
109
+ return data.position?.x !== undefined && data.position.y !== undefined
102
110
  ? {
103
- x: (data.position.x * data.size.width) / 100,
104
- y: (data.position.y * data.size.height) / 100,
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() * 100) * data.size.width) / 100,
111
- y: ((data.position?.y ?? getRandom() * 100) * data.size.height) / 100,
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
- return input ? (input.endsWith("%") ? parseFloat(input) / 100 : parseFloat(input)) : 1;
143
+ const defaultAlpha = 1;
144
+ if (!input) {
145
+ return defaultAlpha;
146
+ }
147
+ return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input);
136
148
  }