tsparticles 1.37.2 → 1.37.6

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 (45) hide show
  1. package/Plugins/Absorbers/AbsorberInstance.d.ts +2 -1
  2. package/Plugins/Absorbers/AbsorberInstance.js +21 -9
  3. package/Plugins/Absorbers/Options/Classes/AbsorberSize.d.ts +2 -1
  4. package/Plugins/Absorbers/Options/Classes/AbsorberSize.js +6 -4
  5. package/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.d.ts +9 -0
  6. package/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.js +21 -0
  7. package/Plugins/Absorbers/Options/Interfaces/IAbsorberSize.d.ts +3 -2
  8. package/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.d.ts +4 -0
  9. package/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.js +2 -0
  10. package/README.md +68 -0
  11. package/Utils/EventListeners.d.ts +1 -0
  12. package/Utils/EventListeners.js +13 -2
  13. package/Utils/Utils.js +4 -7
  14. package/browser/Plugins/Absorbers/AbsorberInstance.d.ts +2 -1
  15. package/browser/Plugins/Absorbers/AbsorberInstance.js +21 -9
  16. package/browser/Plugins/Absorbers/Options/Classes/AbsorberSize.d.ts +2 -1
  17. package/browser/Plugins/Absorbers/Options/Classes/AbsorberSize.js +6 -4
  18. package/browser/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.d.ts +9 -0
  19. package/browser/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.js +17 -0
  20. package/browser/Plugins/Absorbers/Options/Interfaces/IAbsorberSize.d.ts +3 -2
  21. package/browser/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.d.ts +4 -0
  22. package/browser/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.js +1 -0
  23. package/browser/Utils/EventListeners.d.ts +1 -0
  24. package/browser/Utils/EventListeners.js +14 -3
  25. package/browser/Utils/Utils.js +5 -8
  26. package/esm/Plugins/Absorbers/AbsorberInstance.d.ts +2 -1
  27. package/esm/Plugins/Absorbers/AbsorberInstance.js +21 -9
  28. package/esm/Plugins/Absorbers/Options/Classes/AbsorberSize.d.ts +2 -1
  29. package/esm/Plugins/Absorbers/Options/Classes/AbsorberSize.js +6 -4
  30. package/esm/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.d.ts +9 -0
  31. package/esm/Plugins/Absorbers/Options/Classes/AbsorberSizeLimit.js +17 -0
  32. package/esm/Plugins/Absorbers/Options/Interfaces/IAbsorberSize.d.ts +3 -2
  33. package/esm/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.d.ts +4 -0
  34. package/esm/Plugins/Absorbers/Options/Interfaces/IAbsorberSizeLimit.js +1 -0
  35. package/esm/Utils/EventListeners.d.ts +1 -0
  36. package/esm/Utils/EventListeners.js +14 -3
  37. package/esm/Utils/Utils.js +5 -8
  38. package/package.json +1 -1
  39. package/report.html +2 -2
  40. package/report.slim.html +2 -2
  41. package/scripts/install.js +14 -3
  42. package/tsparticles.js +55 -23
  43. package/tsparticles.min.js +2 -2
  44. package/tsparticles.slim.js +15 -9
  45. package/tsparticles.slim.min.js +2 -2
@@ -5,6 +5,7 @@ import type { IAbsorber } from "./Options/Interfaces/IAbsorber";
5
5
  import type { Absorbers } from "./Absorbers";
6
6
  import { Vector } from "../../Core/Particle/Vector";
7
7
  import { RotateDirection } from "../../Enums";
8
+ import { IAbsorberSizeLimit } from "./Options/Interfaces/IAbsorberSizeLimit";
8
9
  declare type OrbitingParticle = Particle & {
9
10
  absorberOrbit?: Vector;
10
11
  absorberOrbitDirection?: RotateDirection;
@@ -17,7 +18,7 @@ export declare class AbsorberInstance {
17
18
  opacity: number;
18
19
  size: number;
19
20
  color: IRgb;
20
- limit?: number;
21
+ limit: IAbsorberSizeLimit;
21
22
  readonly name?: string;
22
23
  position: Vector;
23
24
  private dragging;
@@ -6,7 +6,7 @@ const Vector_1 = require("../../Core/Particle/Vector");
6
6
  const Enums_1 = require("../../Enums");
7
7
  class AbsorberInstance {
8
8
  constructor(absorbers, container, options, position) {
9
- var _a, _b, _c;
9
+ var _a, _b, _c, _d, _e;
10
10
  this.absorbers = absorbers;
11
11
  this.container = container;
12
12
  this.initialPosition = position ? Vector_1.Vector.create(position.x, position.y) : undefined;
@@ -17,14 +17,23 @@ class AbsorberInstance {
17
17
  this.size = (0, Utils_1.getRangeValue)(options.size.value) * container.retina.pixelRatio;
18
18
  this.mass = this.size * options.size.density * container.retina.reduceFactor;
19
19
  const limit = options.size.limit;
20
- this.limit = limit !== undefined ? limit * container.retina.pixelRatio * container.retina.reduceFactor : limit;
20
+ this.limit =
21
+ typeof limit === "number"
22
+ ? {
23
+ radius: limit * container.retina.pixelRatio * container.retina.reduceFactor,
24
+ mass: 0,
25
+ }
26
+ : {
27
+ radius: ((_a = limit === null || limit === void 0 ? void 0 : limit.radius) !== null && _a !== void 0 ? _a : 0) * container.retina.pixelRatio * container.retina.reduceFactor,
28
+ mass: (_b = limit === null || limit === void 0 ? void 0 : limit.mass) !== null && _b !== void 0 ? _b : 0,
29
+ };
21
30
  const color = typeof options.color === "string" ? { value: options.color } : options.color;
22
- this.color = (_a = (0, Utils_1.colorToRgb)(color)) !== null && _a !== void 0 ? _a : {
31
+ this.color = (_c = (0, Utils_1.colorToRgb)(color)) !== null && _c !== void 0 ? _c : {
23
32
  b: 0,
24
33
  g: 0,
25
34
  r: 0,
26
35
  };
27
- this.position = (_c = (_b = this.initialPosition) === null || _b === void 0 ? void 0 : _b.copy()) !== null && _c !== void 0 ? _c : this.calcPosition();
36
+ this.position = (_e = (_d = this.initialPosition) === null || _d === void 0 ? void 0 : _d.copy()) !== null && _e !== void 0 ? _e : this.calcPosition();
28
37
  }
29
38
  attract(particle) {
30
39
  const container = this.container;
@@ -67,10 +76,12 @@ class AbsorberInstance {
67
76
  }
68
77
  this.updateParticlePosition(particle, v);
69
78
  }
70
- if (this.limit === undefined || this.size < this.limit) {
79
+ if (this.limit.radius <= 0 || this.size < this.limit.radius) {
71
80
  this.size += sizeFactor;
72
81
  }
73
- this.mass += sizeFactor * this.options.size.density * container.retina.reduceFactor;
82
+ if (this.limit.mass <= 0 || this.mass < this.limit.mass) {
83
+ this.mass += sizeFactor * this.options.size.density * container.retina.reduceFactor;
84
+ }
74
85
  }
75
86
  else {
76
87
  this.updateParticlePosition(particle, v);
@@ -105,9 +116,10 @@ class AbsorberInstance {
105
116
  const container = this.container;
106
117
  const canvasSize = container.canvas.size;
107
118
  if (particle.needsNewPosition) {
108
- const pSize = particle.getRadius();
109
- particle.position.x = (canvasSize.width - pSize * 2) * (1 + (Math.random() * 0.2 - 0.1)) + pSize;
110
- particle.position.y = (canvasSize.height - pSize * 2) * (1 + (Math.random() * 0.2 - 0.1)) + pSize;
119
+ particle.position.x = Math.floor(Math.random() * canvasSize.width);
120
+ particle.position.y = Math.floor(Math.random() * canvasSize.height);
121
+ particle.velocity.setTo(particle.initialVelocity);
122
+ particle.absorberOrbit = undefined;
111
123
  particle.needsNewPosition = false;
112
124
  }
113
125
  if (this.options.orbits) {
@@ -2,9 +2,10 @@ import type { IAbsorberSize } from "../Interfaces/IAbsorberSize";
2
2
  import type { RecursivePartial } from "../../../../Types";
3
3
  import type { IOptionLoader } from "../../../../Options/Interfaces/IOptionLoader";
4
4
  import { ValueWithRandom } from "../../../../Options/Classes/ValueWithRandom";
5
+ import { AbsorberSizeLimit } from "./AbsorberSizeLimit";
5
6
  export declare class AbsorberSize extends ValueWithRandom implements IAbsorberSize, IOptionLoader<IAbsorberSize> {
6
7
  density: number;
7
- limit?: number;
8
+ limit: AbsorberSizeLimit;
8
9
  constructor();
9
10
  load(data?: RecursivePartial<IAbsorberSize>): void;
10
11
  }
@@ -2,12 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AbsorberSize = void 0;
4
4
  const ValueWithRandom_1 = require("../../../../Options/Classes/ValueWithRandom");
5
+ const AbsorberSizeLimit_1 = require("./AbsorberSizeLimit");
5
6
  class AbsorberSize extends ValueWithRandom_1.ValueWithRandom {
6
7
  constructor() {
7
8
  super();
8
9
  this.density = 5;
9
10
  this.random.minimumValue = 1;
10
11
  this.value = 50;
12
+ this.limit = new AbsorberSizeLimit_1.AbsorberSizeLimit();
11
13
  }
12
14
  load(data) {
13
15
  if (!data) {
@@ -17,11 +19,11 @@ class AbsorberSize extends ValueWithRandom_1.ValueWithRandom {
17
19
  if (data.density !== undefined) {
18
20
  this.density = data.density;
19
21
  }
20
- if (data.limit !== undefined) {
21
- this.limit = data.limit;
22
+ if (typeof data.limit === "number") {
23
+ this.limit.radius = data.limit;
22
24
  }
23
- if (data.limit !== undefined) {
24
- this.limit = data.limit;
25
+ else {
26
+ this.limit.load(data.limit);
25
27
  }
26
28
  }
27
29
  }
@@ -0,0 +1,9 @@
1
+ import type { RecursivePartial } from "../../../../Types";
2
+ import type { IOptionLoader } from "../../../../Options/Interfaces/IOptionLoader";
3
+ import type { IAbsorberSizeLimit } from "../Interfaces/IAbsorberSizeLimit";
4
+ export declare class AbsorberSizeLimit implements IAbsorberSizeLimit, IOptionLoader<IAbsorberSizeLimit> {
5
+ radius: number;
6
+ mass: number;
7
+ constructor();
8
+ load(data?: RecursivePartial<IAbsorberSizeLimit>): void;
9
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbsorberSizeLimit = void 0;
4
+ class AbsorberSizeLimit {
5
+ constructor() {
6
+ this.radius = 0;
7
+ this.mass = 0;
8
+ }
9
+ load(data) {
10
+ if (!data) {
11
+ return;
12
+ }
13
+ if (data.mass !== undefined) {
14
+ this.mass = data.mass;
15
+ }
16
+ if (data.radius !== undefined) {
17
+ this.radius = data.radius;
18
+ }
19
+ }
20
+ }
21
+ exports.AbsorberSizeLimit = AbsorberSizeLimit;
@@ -1,5 +1,6 @@
1
- import { IValueWithRandom } from "../../../../Options/Interfaces/IValueWithRandom";
1
+ import type { IValueWithRandom } from "../../../../Options/Interfaces/IValueWithRandom";
2
+ import type { IAbsorberSizeLimit } from "./IAbsorberSizeLimit";
2
3
  export interface IAbsorberSize extends IValueWithRandom {
3
- limit?: number;
4
+ limit?: number | IAbsorberSizeLimit;
4
5
  density: number;
5
6
  }
@@ -0,0 +1,4 @@
1
+ export interface IAbsorberSizeLimit {
2
+ radius: number;
3
+ mass: number;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/README.md CHANGED
@@ -13,6 +13,36 @@ React.js, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Inferno, Riot.j
13
13
 
14
14
  ---
15
15
 
16
+ ## Table of Contents
17
+
18
+ - [Use for your website](#Do-you-want-to-use-it-on-your-website)
19
+ - [Library installation](#Library-installation)
20
+ - [Official components for some of the most used frameworks](#Official-components-for-some-of-the-most-used-frameworks)
21
+ - [Angular](#Angular)
22
+ - [Inferno](#Inferno)
23
+ - [jQuery](#jQuery)
24
+ - [Preact](#Preact)
25
+ - [ReactJS](#ReactJS)
26
+ - [RiotJS](#RiotJS)
27
+ - [SolidJS](#SolidJS)
28
+ - [Svelte](#Svelte)
29
+ - [VueJS 2.x](#VueJS-2x)
30
+ - [VueJS 3.x](#VueJS-3x)
31
+ - [Web Components](#Web-Components)
32
+ - [WordPress](#WordPress)
33
+ - [Templates and Resources](#Templates-and-Resources)
34
+ - [Demo / Generator](#Demo--Generator)
35
+ - [Characters as particles](#Characters-as-particles)
36
+ - [Mouse hover connections](#Mouse-hover-connections)
37
+ - [Polygon mask](#Polygon-mask)
38
+ - [Animated stars](#Animated-stars)
39
+ - [Nyan cat flying on scrolling stars](#Nyan-cat-flying-on-scrolling-stars)
40
+ - [Background Mask particles](#Background-Mask-particles)
41
+ - [Migrating from Particles.js](#Migrating-from-Particlesjs)
42
+ - [Plugins/Customizations](#PluginsCustomizations)
43
+
44
+ ---
45
+
16
46
  ## Do you want to use it on your website?
17
47
 
18
48
  _Documentation and Development references [here](https://particles.js.org/docs/) 📖_
@@ -221,6 +251,22 @@ You can find the instructions [here](https://github.com/matteobruni/tsparticles/
221
251
 
222
252
  You can find the instructions [here](https://github.com/matteobruni/tsparticles/blob/main/components/react/README.md)
223
253
 
254
+ ### RiotJS
255
+
256
+ #### `riot-particles`
257
+
258
+ [![npm](https://img.shields.io/npm/v/riot-particles)](https://www.npmjs.com/package/riot-particles) [![npm](https://img.shields.io/npm/dm/riot-particles)](https://www.npmjs.com/package/riot-particles)
259
+
260
+ You can find the instructions [here](https://github.com/matteobruni/tsparticles/blob/main/components/riot/README.md)
261
+
262
+ ### SolidJS
263
+
264
+ #### `solid-particles`
265
+
266
+ [![npm](https://img.shields.io/npm/v/solid-particles)](https://www.npmjs.com/package/solid-particles) [![npm](https://img.shields.io/npm/dm/solid-particles)](https://www.npmjs.com/package/solid-particles)
267
+
268
+ You can find the instructions [here](https://github.com/matteobruni/tsparticles/blob/main/components/solid/README.md)
269
+
224
270
  ### Svelte
225
271
 
226
272
  #### `svelte-particles`
@@ -245,6 +291,28 @@ You can find the instructions [here](https://github.com/matteobruni/tsparticles/
245
291
 
246
292
  You can find the instructions [here](https://github.com/matteobruni/tsparticles/blob/main/components/vue3/README.md)
247
293
 
294
+ ### Web Components
295
+
296
+ #### `web-particles`
297
+
298
+ [![npm](https://img.shields.io/npm/v/web-particles)](https://www.npmjs.com/package/web-particles) [![npm](https://img.shields.io/npm/dm/web-particles)](https://www.npmjs.com/package/web-particles)
299
+
300
+ You can find the instructions [here](https://github.com/matteobruni/tsparticles/blob/main/components/web/README.md)
301
+
302
+ ### WordPress
303
+
304
+ Actually an official tsParticles plugin isn't existing, but I have a collaboration with
305
+ the `Premium Addons for Elementor` plugin collection.
306
+
307
+ <div style="float: left; margin-right: 10px;">
308
+ <img width="64" alt="Premium Addons for Elementor" src="https://particles.js.org/images/premium-addons-wordpress-plugin.png" />
309
+ </div>
310
+ <div>
311
+ Premium Addons for Elementor is one of the most common plugins for Elementor that offers more than 55 highly customizable Elementor Widgets and Section Add-ons. tsParticles is exclusively included in Premium Particles Section Add-on for Elementor Page Builder. <a href="https://premiumaddons.com/particles-section-addon-for-elementor-page-builder/" target="_blank">Check It Now</a>.<br />
312
+ Use Premium Addons for Elementor Page Builder and get the chance to include tsParticles in your next WordPress website without the need to write a single line of code. <a href="https://premiumaddons.com/particles-section-addon-for-elementor-page-builder/" target="_blank">See a Live Example</a>.
313
+ </div>
314
+ <div style="clear: both;"></div>
315
+
248
316
  ---
249
317
 
250
318
  ## **_Demo / Generator_**
@@ -12,6 +12,7 @@ export declare class EventListeners {
12
12
  private readonly mouseUpHandler;
13
13
  private readonly visibilityChangeHandler;
14
14
  private readonly themeChangeHandler;
15
+ private readonly oldThemeChangeHandler;
15
16
  private readonly resizeHandler;
16
17
  private canPush;
17
18
  private resizeTimeout?;
@@ -35,6 +35,7 @@ class EventListeners {
35
35
  this.mouseDownHandler = () => this.mouseDown();
36
36
  this.visibilityChangeHandler = () => this.handleVisibilityChange();
37
37
  this.themeChangeHandler = (e) => this.handleThemeChange(e);
38
+ this.oldThemeChangeHandler = (e) => this.handleThemeChange(e);
38
39
  this.resizeHandler = () => this.handleWindowResize();
39
40
  }
40
41
  addListeners() {
@@ -60,9 +61,19 @@ class EventListeners {
60
61
  else {
61
62
  container.interactivity.element = container.canvas.element;
62
63
  }
63
- const mediaMatch = typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)");
64
+ const mediaMatch = !(0, Utils_1.isSsr)() && typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)");
64
65
  if (mediaMatch) {
65
- manageListener(mediaMatch, "change", this.themeChangeHandler, add);
66
+ if (mediaMatch.addEventListener !== undefined) {
67
+ manageListener(mediaMatch, "change", this.themeChangeHandler, add);
68
+ }
69
+ else if (mediaMatch.addListener !== undefined) {
70
+ if (add) {
71
+ mediaMatch.addListener(this.oldThemeChangeHandler);
72
+ }
73
+ else {
74
+ mediaMatch.removeListener(this.oldThemeChangeHandler);
75
+ }
76
+ }
66
77
  }
67
78
  const interactivityEl = container.interactivity.element;
68
79
  if (!interactivityEl) {
package/Utils/Utils.js CHANGED
@@ -200,14 +200,11 @@ function circleBounceDataFromParticle(p) {
200
200
  }
201
201
  exports.circleBounceDataFromParticle = circleBounceDataFromParticle;
202
202
  function circleBounce(p1, p2) {
203
- const xVelocityDiff = p1.velocity.x;
204
- const yVelocityDiff = p1.velocity.y;
205
- const pos1 = p1.position;
206
- const pos2 = p2.position;
207
- const xDist = pos2.x - pos1.x;
208
- const yDist = pos2.y - pos1.y;
203
+ const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity);
204
+ const [pos1, pos2] = [p1.position, p2.position];
205
+ const { dx: xDist, dy: yDist } = (0, NumberUtils_1.getDistances)(pos2, pos1);
209
206
  if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {
210
- const angle = -Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
207
+ const angle = -Math.atan2(yDist, xDist);
211
208
  const m1 = p1.mass;
212
209
  const m2 = p2.mass;
213
210
  const u1 = p1.velocity.rotate(angle);
@@ -5,6 +5,7 @@ import type { IAbsorber } from "./Options/Interfaces/IAbsorber";
5
5
  import type { Absorbers } from "./Absorbers";
6
6
  import { Vector } from "../../Core/Particle/Vector";
7
7
  import { RotateDirection } from "../../Enums";
8
+ import { IAbsorberSizeLimit } from "./Options/Interfaces/IAbsorberSizeLimit";
8
9
  declare type OrbitingParticle = Particle & {
9
10
  absorberOrbit?: Vector;
10
11
  absorberOrbitDirection?: RotateDirection;
@@ -20,7 +21,7 @@ export declare class AbsorberInstance {
20
21
  opacity: number;
21
22
  size: number;
22
23
  color: IRgb;
23
- limit?: number;
24
+ limit: IAbsorberSizeLimit;
24
25
  readonly name?: string;
25
26
  position: Vector;
26
27
  private dragging;
@@ -6,7 +6,7 @@ import { RotateDirection } from "../../Enums";
6
6
  */
7
7
  export class AbsorberInstance {
8
8
  constructor(absorbers, container, options, position) {
9
- var _a, _b, _c;
9
+ var _a, _b, _c, _d, _e;
10
10
  this.absorbers = absorbers;
11
11
  this.container = container;
12
12
  this.initialPosition = position ? Vector.create(position.x, position.y) : undefined;
@@ -17,14 +17,23 @@ export class AbsorberInstance {
17
17
  this.size = getRangeValue(options.size.value) * container.retina.pixelRatio;
18
18
  this.mass = this.size * options.size.density * container.retina.reduceFactor;
19
19
  const limit = options.size.limit;
20
- this.limit = limit !== undefined ? limit * container.retina.pixelRatio * container.retina.reduceFactor : limit;
20
+ this.limit =
21
+ typeof limit === "number"
22
+ ? {
23
+ radius: limit * container.retina.pixelRatio * container.retina.reduceFactor,
24
+ mass: 0,
25
+ }
26
+ : {
27
+ radius: ((_a = limit === null || limit === void 0 ? void 0 : limit.radius) !== null && _a !== void 0 ? _a : 0) * container.retina.pixelRatio * container.retina.reduceFactor,
28
+ mass: (_b = limit === null || limit === void 0 ? void 0 : limit.mass) !== null && _b !== void 0 ? _b : 0,
29
+ };
21
30
  const color = typeof options.color === "string" ? { value: options.color } : options.color;
22
- this.color = (_a = colorToRgb(color)) !== null && _a !== void 0 ? _a : {
31
+ this.color = (_c = colorToRgb(color)) !== null && _c !== void 0 ? _c : {
23
32
  b: 0,
24
33
  g: 0,
25
34
  r: 0,
26
35
  };
27
- this.position = (_c = (_b = this.initialPosition) === null || _b === void 0 ? void 0 : _b.copy()) !== null && _c !== void 0 ? _c : this.calcPosition();
36
+ this.position = (_e = (_d = this.initialPosition) === null || _d === void 0 ? void 0 : _d.copy()) !== null && _e !== void 0 ? _e : this.calcPosition();
28
37
  }
29
38
  attract(particle) {
30
39
  const container = this.container;
@@ -67,10 +76,12 @@ export class AbsorberInstance {
67
76
  }
68
77
  this.updateParticlePosition(particle, v);
69
78
  }
70
- if (this.limit === undefined || this.size < this.limit) {
79
+ if (this.limit.radius <= 0 || this.size < this.limit.radius) {
71
80
  this.size += sizeFactor;
72
81
  }
73
- this.mass += sizeFactor * this.options.size.density * container.retina.reduceFactor;
82
+ if (this.limit.mass <= 0 || this.mass < this.limit.mass) {
83
+ this.mass += sizeFactor * this.options.size.density * container.retina.reduceFactor;
84
+ }
74
85
  }
75
86
  else {
76
87
  this.updateParticlePosition(particle, v);
@@ -105,9 +116,10 @@ export class AbsorberInstance {
105
116
  const container = this.container;
106
117
  const canvasSize = container.canvas.size;
107
118
  if (particle.needsNewPosition) {
108
- const pSize = particle.getRadius();
109
- particle.position.x = (canvasSize.width - pSize * 2) * (1 + (Math.random() * 0.2 - 0.1)) + pSize;
110
- particle.position.y = (canvasSize.height - pSize * 2) * (1 + (Math.random() * 0.2 - 0.1)) + pSize;
119
+ particle.position.x = Math.floor(Math.random() * canvasSize.width);
120
+ particle.position.y = Math.floor(Math.random() * canvasSize.height);
121
+ particle.velocity.setTo(particle.initialVelocity);
122
+ particle.absorberOrbit = undefined;
111
123
  particle.needsNewPosition = false;
112
124
  }
113
125
  if (this.options.orbits) {
@@ -2,9 +2,10 @@ import type { IAbsorberSize } from "../Interfaces/IAbsorberSize";
2
2
  import type { RecursivePartial } from "../../../../Types";
3
3
  import type { IOptionLoader } from "../../../../Options/Interfaces/IOptionLoader";
4
4
  import { ValueWithRandom } from "../../../../Options/Classes/ValueWithRandom";
5
+ import { AbsorberSizeLimit } from "./AbsorberSizeLimit";
5
6
  export declare class AbsorberSize extends ValueWithRandom implements IAbsorberSize, IOptionLoader<IAbsorberSize> {
6
7
  density: number;
7
- limit?: number;
8
+ limit: AbsorberSizeLimit;
8
9
  constructor();
9
10
  load(data?: RecursivePartial<IAbsorberSize>): void;
10
11
  }
@@ -1,10 +1,12 @@
1
1
  import { ValueWithRandom } from "../../../../Options/Classes/ValueWithRandom";
2
+ import { AbsorberSizeLimit } from "./AbsorberSizeLimit";
2
3
  export class AbsorberSize extends ValueWithRandom {
3
4
  constructor() {
4
5
  super();
5
6
  this.density = 5;
6
7
  this.random.minimumValue = 1;
7
8
  this.value = 50;
9
+ this.limit = new AbsorberSizeLimit();
8
10
  }
9
11
  load(data) {
10
12
  if (!data) {
@@ -14,11 +16,11 @@ export class AbsorberSize extends ValueWithRandom {
14
16
  if (data.density !== undefined) {
15
17
  this.density = data.density;
16
18
  }
17
- if (data.limit !== undefined) {
18
- this.limit = data.limit;
19
+ if (typeof data.limit === "number") {
20
+ this.limit.radius = data.limit;
19
21
  }
20
- if (data.limit !== undefined) {
21
- this.limit = data.limit;
22
+ else {
23
+ this.limit.load(data.limit);
22
24
  }
23
25
  }
24
26
  }
@@ -0,0 +1,9 @@
1
+ import type { RecursivePartial } from "../../../../Types";
2
+ import type { IOptionLoader } from "../../../../Options/Interfaces/IOptionLoader";
3
+ import type { IAbsorberSizeLimit } from "../Interfaces/IAbsorberSizeLimit";
4
+ export declare class AbsorberSizeLimit implements IAbsorberSizeLimit, IOptionLoader<IAbsorberSizeLimit> {
5
+ radius: number;
6
+ mass: number;
7
+ constructor();
8
+ load(data?: RecursivePartial<IAbsorberSizeLimit>): void;
9
+ }
@@ -0,0 +1,17 @@
1
+ export class AbsorberSizeLimit {
2
+ constructor() {
3
+ this.radius = 0;
4
+ this.mass = 0;
5
+ }
6
+ load(data) {
7
+ if (!data) {
8
+ return;
9
+ }
10
+ if (data.mass !== undefined) {
11
+ this.mass = data.mass;
12
+ }
13
+ if (data.radius !== undefined) {
14
+ this.radius = data.radius;
15
+ }
16
+ }
17
+ }
@@ -1,5 +1,6 @@
1
- import { IValueWithRandom } from "../../../../Options/Interfaces/IValueWithRandom";
1
+ import type { IValueWithRandom } from "../../../../Options/Interfaces/IValueWithRandom";
2
+ import type { IAbsorberSizeLimit } from "./IAbsorberSizeLimit";
2
3
  export interface IAbsorberSize extends IValueWithRandom {
3
- limit?: number;
4
+ limit?: number | IAbsorberSizeLimit;
4
5
  density: number;
5
6
  }
@@ -0,0 +1,4 @@
1
+ export interface IAbsorberSizeLimit {
2
+ radius: number;
3
+ mass: number;
4
+ }
@@ -16,6 +16,7 @@ export declare class EventListeners {
16
16
  private readonly mouseUpHandler;
17
17
  private readonly visibilityChangeHandler;
18
18
  private readonly themeChangeHandler;
19
+ private readonly oldThemeChangeHandler;
19
20
  private readonly resizeHandler;
20
21
  private canPush;
21
22
  private resizeTimeout?;
@@ -1,6 +1,6 @@
1
1
  import { ClickMode, InteractivityDetect } from "../Enums";
2
2
  import { Constants } from "./Constants";
3
- import { itemFromArray } from "./Utils";
3
+ import { isSsr, itemFromArray } from "./Utils";
4
4
  function manageListener(element, event, handler, add, options) {
5
5
  if (add) {
6
6
  let addOptions = { passive: true };
@@ -40,6 +40,7 @@ export class EventListeners {
40
40
  this.mouseDownHandler = () => this.mouseDown();
41
41
  this.visibilityChangeHandler = () => this.handleVisibilityChange();
42
42
  this.themeChangeHandler = (e) => this.handleThemeChange(e);
43
+ this.oldThemeChangeHandler = (e) => this.handleThemeChange(e);
43
44
  this.resizeHandler = () => this.handleWindowResize();
44
45
  }
45
46
  /**
@@ -75,9 +76,19 @@ export class EventListeners {
75
76
  else {
76
77
  container.interactivity.element = container.canvas.element;
77
78
  }
78
- const mediaMatch = typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)");
79
+ const mediaMatch = !isSsr() && typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)");
79
80
  if (mediaMatch) {
80
- manageListener(mediaMatch, "change", this.themeChangeHandler, add);
81
+ if (mediaMatch.addEventListener !== undefined) {
82
+ manageListener(mediaMatch, "change", this.themeChangeHandler, add);
83
+ }
84
+ else if (mediaMatch.addListener !== undefined) {
85
+ if (add) {
86
+ mediaMatch.addListener(this.oldThemeChangeHandler);
87
+ }
88
+ else {
89
+ mediaMatch.removeListener(this.oldThemeChangeHandler);
90
+ }
91
+ }
81
92
  }
82
93
  const interactivityEl = container.interactivity.element;
83
94
  /* detect mouse pos - on hover / click event */
@@ -1,5 +1,5 @@
1
1
  import { OutModeDirection } from "../Enums";
2
- import { collisionVelocity, getValue } from "./NumberUtils";
2
+ import { collisionVelocity, getValue, getDistances } from "./NumberUtils";
3
3
  import { Vector } from "../Core/Particle/Vector";
4
4
  function rectSideBounce(pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor) {
5
5
  const res = { bounced: false };
@@ -188,16 +188,13 @@ export function circleBounceDataFromParticle(p) {
188
188
  };
189
189
  }
190
190
  export function circleBounce(p1, p2) {
191
- const xVelocityDiff = p1.velocity.x;
192
- const yVelocityDiff = p1.velocity.y;
193
- const pos1 = p1.position;
194
- const pos2 = p2.position;
195
- const xDist = pos2.x - pos1.x;
196
- const yDist = pos2.y - pos1.y;
191
+ const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity);
192
+ const [pos1, pos2] = [p1.position, p2.position];
193
+ const { dx: xDist, dy: yDist } = getDistances(pos2, pos1);
197
194
  // Prevent accidental overlap of particles
198
195
  if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {
199
196
  // Grab angle between the two colliding particles
200
- const angle = -Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x);
197
+ const angle = -Math.atan2(yDist, xDist);
201
198
  // Store mass in var for better readability in collision equation
202
199
  const m1 = p1.mass;
203
200
  const m2 = p2.mass;
@@ -5,6 +5,7 @@ import type { IAbsorber } from "./Options/Interfaces/IAbsorber";
5
5
  import type { Absorbers } from "./Absorbers";
6
6
  import { Vector } from "../../Core/Particle/Vector";
7
7
  import { RotateDirection } from "../../Enums";
8
+ import { IAbsorberSizeLimit } from "./Options/Interfaces/IAbsorberSizeLimit";
8
9
  declare type OrbitingParticle = Particle & {
9
10
  absorberOrbit?: Vector;
10
11
  absorberOrbitDirection?: RotateDirection;
@@ -17,7 +18,7 @@ export declare class AbsorberInstance {
17
18
  opacity: number;
18
19
  size: number;
19
20
  color: IRgb;
20
- limit?: number;
21
+ limit: IAbsorberSizeLimit;
21
22
  readonly name?: string;
22
23
  position: Vector;
23
24
  private dragging;