@tsparticles/interaction-external-repulse 4.0.0-alpha.5 → 4.0.0-beta.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/747.min.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(this.webpackChunk_tsparticles_interaction_external_repulse=this.webpackChunk_tsparticles_interaction_external_repulse||[]).push([[747],{747(e,t,i){i.d(t,{Repulser:()=>c});var s=i(303),n=i(702),l=i(155);let r="repulse";class c extends n.ExternalInteractorBase{handleClickMode;_clickVec;_engine;_maxDistance;_normVec;constructor(e,t){super(t),this._engine=e,this._maxDistance=0,this._normVec=s.Vector.origin,this._clickVec=s.Vector.origin,t.repulse??={particles:[]},this.handleClickMode=(e,i)=>{let n=this.container.actualOptions,l=n.interactivity?.modes.repulse;if(!l||e!==r)return;t.repulse??={particles:[]};let c=t.repulse;for(let e of(c.clicking=!0,c.count=0,t.repulse.particles))this.isEnabled(i,e)&&e.velocity.setTo(e.initialVelocity);c.particles=[],c.finish=!1,setTimeout(()=>{t.destroyed||(c.clicking=!1)},l.duration*s.millisecondsToSeconds)}}get maxDistance(){return this._maxDistance}clear(){}init(){let e=this.container,t=e.actualOptions.interactivity?.modes.repulse;t&&(this._maxDistance=t.distance,e.retina.repulseModeDistance=t.distance*e.retina.pixelRatio)}interact(e){let t=this.container.actualOptions,i=e.status===n.mouseMoveEvent,l=t.interactivity?.events;if(!l)return;let c=l.onHover,o=c.enable,a=c.mode,p=l.onClick,u=p.enable,h=p.mode,d=l.onDiv;i&&o&&(0,s.isInArray)(r,a)?this._hoverRepulse(e):u&&(0,s.isInArray)(r,h)?this._clickRepulse(e):(0,n.divModeExecute)(r,d,(t,i)=>{this._singleSelectorRepulse(e,t,i)})}isEnabled(e,t){let i=this.container.actualOptions,l=e.mouse,c=(t?.interactivity??i.interactivity)?.events;if(!c)return!1;let o=c.onDiv,a=c.onHover,p=c.onClick,u=(0,n.isDivModeEnabled)(r,o);if(!(u||a.enable&&l.position||p.enable&&l.clickPosition))return!1;let h=a.mode,d=p.mode;return(0,s.isInArray)(r,h)||(0,s.isInArray)(r,d)||u}loadModeOptions(e,...t){for(let i of(e.repulse??=new l.Z,t))e.repulse.load(i?.repulse)}reset(){}_clickRepulse=e=>{let t=this.container,i=t.actualOptions.interactivity?.modes.repulse;if(!i)return;let n=t.repulse??{particles:[]};if(n.finish||(n.count??=0,n.count++,n.count===t.particles.count&&(n.finish=!0)),n.clicking){let l=t.retina.repulseModeDistance;if(!l||l<0)return;let r=Math.pow(l/6,3),c=e.mouse.clickPosition;if(void 0===c)return;let o=new s.Circle(c.x,c.y,r);for(let l of t.particles.grid.query(o,t=>this.isEnabled(e,t))){let{dx:e,dy:t,distance:o}=(0,s.getDistances)(c,l.position),a=o**2,p=-r*i.speed/a;a<=r&&(n.particles.push(l),this._clickVec.x=e,this._clickVec.y=t,this._clickVec.length=p,l.velocity.setTo(this._clickVec))}}else if(!1===n.clicking){for(let e of n.particles)e.velocity.setTo(e.initialVelocity);n.particles=[]}};_hoverRepulse=e=>{let t=this.container,i=e.mouse.position,n=t.retina.repulseModeDistance;n&&!(n<0)&&i&&this._processRepulse(e,i,n,new s.Circle(i.x,i.y,n))};_processRepulse=(e,t,i,n,l)=>{let r=this.container,c=r.particles.grid.query(n,t=>this.isEnabled(e,t)),o=r.actualOptions.interactivity?.modes.repulse;if(!o)return;let{easing:a,speed:p,factor:u,maxSpeed:h}=o,d=this._engine.getEasing(a),f=(l?.speed??p)*u;for(let e of c){let{dx:n,dy:l,distance:r}=(0,s.getDistances)(e.position,t),c=(0,s.clamp)(d(1-r/i)*f,0,h);this._normVec.x=r?n/r*c:f,this._normVec.y=r?l/r*c:f,e.position.addTo(this._normVec)}};_singleSelectorRepulse=(e,t,i)=>{let l=this.container,r=l.actualOptions.interactivity?.modes.repulse;if(!r)return;let c=(0,s.safeDocument)().querySelectorAll(t);c.length&&c.forEach(t=>{let c=l.retina.pixelRatio,o={x:(t.offsetLeft+t.offsetWidth*s.half)*c,y:(t.offsetTop+t.offsetHeight*s.half)*c},a=t.offsetWidth*s.half*c,p=i.type===n.DivType.circle?new s.Circle(o.x,o.y,a):new s.Rectangle(t.offsetLeft*c,t.offsetTop*c,t.offsetWidth*c,t.offsetHeight*c),u=r.divs,h=(0,n.divMode)(u,t);this._processRepulse(e,o,a,p,h)})}}}}]);
package/README.md CHANGED
@@ -28,6 +28,7 @@ Once the scripts are loaded you can set up `tsParticles` and the interaction plu
28
28
 
29
29
  ```javascript
30
30
  (async () => {
31
+ await loadInteractivityPlugin(tsParticles);
31
32
  await loadExternalRepulseInteraction(tsParticles);
32
33
 
33
34
  await tsParticles.load({
@@ -57,9 +58,11 @@ Then you need to import it in the app, like this:
57
58
 
58
59
  ```javascript
59
60
  const { tsParticles } = require("@tsparticles/engine");
61
+ const { loadInteractivityPlugin } = require("@tsparticles/plugin-interactivity");
60
62
  const { loadExternalRepulseInteraction } = require("@tsparticles/interaction-external-repulse");
61
63
 
62
64
  (async () => {
65
+ await loadInteractivityPlugin(tsParticles);
63
66
  await loadExternalRepulseInteraction(tsParticles);
64
67
  })();
65
68
  ```
@@ -68,9 +71,11 @@ or
68
71
 
69
72
  ```javascript
70
73
  import { tsParticles } from "@tsparticles/engine";
74
+ import { loadInteractivityPlugin } from "@tsparticles/plugin-interactivity";
71
75
  import { loadExternalRepulseInteraction } from "@tsparticles/interaction-external-repulse";
72
76
 
73
77
  (async () => {
78
+ await loadInteractivityPlugin(tsParticles);
74
79
  await loadExternalRepulseInteraction(tsParticles);
75
80
  })();
76
81
  ```
@@ -2,6 +2,7 @@ import { executeOnSingleOrMultiple, isNull, } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  import { RepulseDiv } from "./RepulseDiv.js";
4
4
  export class Repulse extends RepulseBase {
5
+ divs;
5
6
  load(data) {
6
7
  super.load(data);
7
8
  if (isNull(data)) {
@@ -1,5 +1,11 @@
1
1
  import { EasingType, isNull } from "@tsparticles/engine";
2
2
  export class RepulseBase {
3
+ distance;
4
+ duration;
5
+ easing;
6
+ factor;
7
+ maxSpeed;
8
+ speed;
3
9
  constructor() {
4
10
  this.distance = 200;
5
11
  this.duration = 0.4;
@@ -1,6 +1,7 @@
1
1
  import { isNull } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  export class RepulseDiv extends RepulseBase {
4
+ selectors;
4
5
  constructor() {
5
6
  super();
6
7
  this.selectors = [];
@@ -3,86 +3,17 @@ import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnab
3
3
  import { Repulse } from "./Options/Classes/Repulse.js";
4
4
  const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1;
5
5
  export class Repulser extends ExternalInteractorBase {
6
+ handleClickMode;
7
+ _clickVec;
8
+ _engine;
9
+ _maxDistance;
10
+ _normVec;
6
11
  constructor(engine, container) {
7
12
  super(container);
8
- this._clickRepulse = interactivityData => {
9
- const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
10
- if (!repulseOptions) {
11
- return;
12
- }
13
- const repulse = container.repulse ?? { particles: [] };
14
- if (!repulse.finish) {
15
- repulse.count ??= 0;
16
- repulse.count++;
17
- if (repulse.count === container.particles.count) {
18
- repulse.finish = true;
19
- }
20
- }
21
- if (repulse.clicking) {
22
- const repulseDistance = container.retina.repulseModeDistance;
23
- if (!repulseDistance || repulseDistance < minDistance) {
24
- return;
25
- }
26
- const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
27
- if (mouseClickPos === undefined) {
28
- return;
29
- }
30
- const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(interactivityData, p));
31
- for (const particle of query) {
32
- const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
33
- if (d <= repulseRadius) {
34
- repulse.particles.push(particle);
35
- const vect = Vector.create(dx, dy);
36
- vect.length = force;
37
- particle.velocity.setTo(vect);
38
- }
39
- }
40
- }
41
- else if (repulse.clicking === false) {
42
- for (const particle of repulse.particles) {
43
- particle.velocity.setTo(particle.initialVelocity);
44
- }
45
- repulse.particles = [];
46
- }
47
- };
48
- this._hoverRepulse = interactivityData => {
49
- const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
50
- if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
51
- return;
52
- }
53
- this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
54
- };
55
- this._processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
56
- const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
57
- if (!repulseOptions) {
58
- return;
59
- }
60
- const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
61
- for (const particle of query) {
62
- const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed), normVec = Vector.create(!distance ? velocity : (dx / distance) * repulseFactor, !distance ? velocity : (dy / distance) * repulseFactor);
63
- particle.position.addTo(normVec);
64
- }
65
- };
66
- this._singleSelectorRepulse = (interactivityData, selector, div) => {
67
- const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
68
- if (!repulse) {
69
- return;
70
- }
71
- const query = safeDocument().querySelectorAll(selector);
72
- if (!query.length) {
73
- return;
74
- }
75
- query.forEach(item => {
76
- const elem = item, pxRatio = container.retina.pixelRatio, pos = {
77
- x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
78
- y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
79
- }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
80
- ? new Circle(pos.x, pos.y, repulseRadius)
81
- : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
82
- this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
83
- });
84
- };
85
13
  this._engine = engine;
14
+ this._maxDistance = 0;
15
+ this._normVec = Vector.origin;
16
+ this._clickVec = Vector.origin;
86
17
  container.repulse ??= { particles: [] };
87
18
  this.handleClickMode = (mode, interactivityData) => {
88
19
  const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
@@ -109,6 +40,9 @@ export class Repulser extends ExternalInteractorBase {
109
40
  }, repulseOpts.duration * millisecondsToSeconds);
110
41
  };
111
42
  }
43
+ get maxDistance() {
44
+ return this._maxDistance;
45
+ }
112
46
  clear() {
113
47
  }
114
48
  init() {
@@ -116,6 +50,7 @@ export class Repulser extends ExternalInteractorBase {
116
50
  if (!repulse) {
117
51
  return;
118
52
  }
53
+ this._maxDistance = repulse.distance;
119
54
  container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
120
55
  }
121
56
  interact(interactivityData) {
@@ -156,4 +91,84 @@ export class Repulser extends ExternalInteractorBase {
156
91
  }
157
92
  reset() {
158
93
  }
94
+ _clickRepulse = interactivityData => {
95
+ const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
96
+ if (!repulseOptions) {
97
+ return;
98
+ }
99
+ const repulse = container.repulse ?? { particles: [] };
100
+ if (!repulse.finish) {
101
+ repulse.count ??= 0;
102
+ repulse.count++;
103
+ if (repulse.count === container.particles.count) {
104
+ repulse.finish = true;
105
+ }
106
+ }
107
+ if (repulse.clicking) {
108
+ const repulseDistance = container.retina.repulseModeDistance;
109
+ if (!repulseDistance || repulseDistance < minDistance) {
110
+ return;
111
+ }
112
+ const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
113
+ if (mouseClickPos === undefined) {
114
+ return;
115
+ }
116
+ const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.grid.query(range, p => this.isEnabled(interactivityData, p));
117
+ for (const particle of query) {
118
+ const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
119
+ if (d <= repulseRadius) {
120
+ repulse.particles.push(particle);
121
+ this._clickVec.x = dx;
122
+ this._clickVec.y = dy;
123
+ this._clickVec.length = force;
124
+ particle.velocity.setTo(this._clickVec);
125
+ }
126
+ }
127
+ }
128
+ else if (repulse.clicking === false) {
129
+ for (const particle of repulse.particles) {
130
+ particle.velocity.setTo(particle.initialVelocity);
131
+ }
132
+ repulse.particles = [];
133
+ }
134
+ };
135
+ _hoverRepulse = interactivityData => {
136
+ const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
137
+ if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
138
+ return;
139
+ }
140
+ this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
141
+ };
142
+ _processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
143
+ const container = this.container, query = container.particles.grid.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
144
+ if (!repulseOptions) {
145
+ return;
146
+ }
147
+ const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
148
+ for (const particle of query) {
149
+ const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
150
+ this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
151
+ this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
152
+ particle.position.addTo(this._normVec);
153
+ }
154
+ };
155
+ _singleSelectorRepulse = (interactivityData, selector, div) => {
156
+ const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
157
+ if (!repulse) {
158
+ return;
159
+ }
160
+ const query = safeDocument().querySelectorAll(selector);
161
+ if (!query.length) {
162
+ return;
163
+ }
164
+ query.forEach(item => {
165
+ const elem = item, pxRatio = container.retina.pixelRatio, pos = {
166
+ x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
167
+ y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
168
+ }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
169
+ ? new Circle(pos.x, pos.y, repulseRadius)
170
+ : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
171
+ this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
172
+ });
173
+ };
159
174
  }
package/browser/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export async function loadExternalRepulseInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.5");
2
+ engine.checkVersion("4.0.0-beta.0");
3
3
  await engine.register(async (e) => {
4
- const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
5
- await loadInteractivityPlugin(e);
4
+ const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
5
+ ensureInteractivityPluginLoaded(e);
6
6
  e.addInteractor?.("externalRepulse", async (container) => {
7
7
  const { Repulser } = await import("./Repulser.js");
8
8
  return new Repulser(engine, container);
@@ -2,6 +2,7 @@ import { executeOnSingleOrMultiple, isNull, } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  import { RepulseDiv } from "./RepulseDiv.js";
4
4
  export class Repulse extends RepulseBase {
5
+ divs;
5
6
  load(data) {
6
7
  super.load(data);
7
8
  if (isNull(data)) {
@@ -1,5 +1,11 @@
1
1
  import { EasingType, isNull } from "@tsparticles/engine";
2
2
  export class RepulseBase {
3
+ distance;
4
+ duration;
5
+ easing;
6
+ factor;
7
+ maxSpeed;
8
+ speed;
3
9
  constructor() {
4
10
  this.distance = 200;
5
11
  this.duration = 0.4;
@@ -1,6 +1,7 @@
1
1
  import { isNull } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  export class RepulseDiv extends RepulseBase {
4
+ selectors;
4
5
  constructor() {
5
6
  super();
6
7
  this.selectors = [];
package/cjs/Repulser.js CHANGED
@@ -3,86 +3,17 @@ import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnab
3
3
  import { Repulse } from "./Options/Classes/Repulse.js";
4
4
  const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1;
5
5
  export class Repulser extends ExternalInteractorBase {
6
+ handleClickMode;
7
+ _clickVec;
8
+ _engine;
9
+ _maxDistance;
10
+ _normVec;
6
11
  constructor(engine, container) {
7
12
  super(container);
8
- this._clickRepulse = interactivityData => {
9
- const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
10
- if (!repulseOptions) {
11
- return;
12
- }
13
- const repulse = container.repulse ?? { particles: [] };
14
- if (!repulse.finish) {
15
- repulse.count ??= 0;
16
- repulse.count++;
17
- if (repulse.count === container.particles.count) {
18
- repulse.finish = true;
19
- }
20
- }
21
- if (repulse.clicking) {
22
- const repulseDistance = container.retina.repulseModeDistance;
23
- if (!repulseDistance || repulseDistance < minDistance) {
24
- return;
25
- }
26
- const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
27
- if (mouseClickPos === undefined) {
28
- return;
29
- }
30
- const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(interactivityData, p));
31
- for (const particle of query) {
32
- const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
33
- if (d <= repulseRadius) {
34
- repulse.particles.push(particle);
35
- const vect = Vector.create(dx, dy);
36
- vect.length = force;
37
- particle.velocity.setTo(vect);
38
- }
39
- }
40
- }
41
- else if (repulse.clicking === false) {
42
- for (const particle of repulse.particles) {
43
- particle.velocity.setTo(particle.initialVelocity);
44
- }
45
- repulse.particles = [];
46
- }
47
- };
48
- this._hoverRepulse = interactivityData => {
49
- const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
50
- if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
51
- return;
52
- }
53
- this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
54
- };
55
- this._processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
56
- const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
57
- if (!repulseOptions) {
58
- return;
59
- }
60
- const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
61
- for (const particle of query) {
62
- const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed), normVec = Vector.create(!distance ? velocity : (dx / distance) * repulseFactor, !distance ? velocity : (dy / distance) * repulseFactor);
63
- particle.position.addTo(normVec);
64
- }
65
- };
66
- this._singleSelectorRepulse = (interactivityData, selector, div) => {
67
- const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
68
- if (!repulse) {
69
- return;
70
- }
71
- const query = safeDocument().querySelectorAll(selector);
72
- if (!query.length) {
73
- return;
74
- }
75
- query.forEach(item => {
76
- const elem = item, pxRatio = container.retina.pixelRatio, pos = {
77
- x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
78
- y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
79
- }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
80
- ? new Circle(pos.x, pos.y, repulseRadius)
81
- : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
82
- this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
83
- });
84
- };
85
13
  this._engine = engine;
14
+ this._maxDistance = 0;
15
+ this._normVec = Vector.origin;
16
+ this._clickVec = Vector.origin;
86
17
  container.repulse ??= { particles: [] };
87
18
  this.handleClickMode = (mode, interactivityData) => {
88
19
  const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
@@ -109,6 +40,9 @@ export class Repulser extends ExternalInteractorBase {
109
40
  }, repulseOpts.duration * millisecondsToSeconds);
110
41
  };
111
42
  }
43
+ get maxDistance() {
44
+ return this._maxDistance;
45
+ }
112
46
  clear() {
113
47
  }
114
48
  init() {
@@ -116,6 +50,7 @@ export class Repulser extends ExternalInteractorBase {
116
50
  if (!repulse) {
117
51
  return;
118
52
  }
53
+ this._maxDistance = repulse.distance;
119
54
  container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
120
55
  }
121
56
  interact(interactivityData) {
@@ -156,4 +91,84 @@ export class Repulser extends ExternalInteractorBase {
156
91
  }
157
92
  reset() {
158
93
  }
94
+ _clickRepulse = interactivityData => {
95
+ const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
96
+ if (!repulseOptions) {
97
+ return;
98
+ }
99
+ const repulse = container.repulse ?? { particles: [] };
100
+ if (!repulse.finish) {
101
+ repulse.count ??= 0;
102
+ repulse.count++;
103
+ if (repulse.count === container.particles.count) {
104
+ repulse.finish = true;
105
+ }
106
+ }
107
+ if (repulse.clicking) {
108
+ const repulseDistance = container.retina.repulseModeDistance;
109
+ if (!repulseDistance || repulseDistance < minDistance) {
110
+ return;
111
+ }
112
+ const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
113
+ if (mouseClickPos === undefined) {
114
+ return;
115
+ }
116
+ const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.grid.query(range, p => this.isEnabled(interactivityData, p));
117
+ for (const particle of query) {
118
+ const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
119
+ if (d <= repulseRadius) {
120
+ repulse.particles.push(particle);
121
+ this._clickVec.x = dx;
122
+ this._clickVec.y = dy;
123
+ this._clickVec.length = force;
124
+ particle.velocity.setTo(this._clickVec);
125
+ }
126
+ }
127
+ }
128
+ else if (repulse.clicking === false) {
129
+ for (const particle of repulse.particles) {
130
+ particle.velocity.setTo(particle.initialVelocity);
131
+ }
132
+ repulse.particles = [];
133
+ }
134
+ };
135
+ _hoverRepulse = interactivityData => {
136
+ const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
137
+ if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
138
+ return;
139
+ }
140
+ this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
141
+ };
142
+ _processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
143
+ const container = this.container, query = container.particles.grid.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
144
+ if (!repulseOptions) {
145
+ return;
146
+ }
147
+ const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
148
+ for (const particle of query) {
149
+ const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
150
+ this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
151
+ this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
152
+ particle.position.addTo(this._normVec);
153
+ }
154
+ };
155
+ _singleSelectorRepulse = (interactivityData, selector, div) => {
156
+ const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
157
+ if (!repulse) {
158
+ return;
159
+ }
160
+ const query = safeDocument().querySelectorAll(selector);
161
+ if (!query.length) {
162
+ return;
163
+ }
164
+ query.forEach(item => {
165
+ const elem = item, pxRatio = container.retina.pixelRatio, pos = {
166
+ x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
167
+ y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
168
+ }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
169
+ ? new Circle(pos.x, pos.y, repulseRadius)
170
+ : new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
171
+ this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
172
+ });
173
+ };
159
174
  }
package/cjs/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export async function loadExternalRepulseInteraction(engine) {
2
- engine.checkVersion("4.0.0-alpha.5");
2
+ engine.checkVersion("4.0.0-beta.0");
3
3
  await engine.register(async (e) => {
4
- const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
5
- await loadInteractivityPlugin(e);
4
+ const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
5
+ ensureInteractivityPluginLoaded(e);
6
6
  e.addInteractor?.("externalRepulse", async (container) => {
7
7
  const { Repulser } = await import("./Repulser.js");
8
8
  return new Repulser(engine, container);
@@ -4,7 +4,7 @@
4
4
  * Demo / Generator : https://particles.js.org/
5
5
  * GitHub : https://www.github.com/matteobruni/tsparticles
6
6
  * How to use? : Check the GitHub README
7
- * v4.0.0-alpha.5
7
+ * v4.0.0-beta.0
8
8
  */
9
9
  "use strict";
10
10
  /*
@@ -23,7 +23,7 @@
23
23
  \**********************************/
24
24
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
25
25
 
26
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Repulser: () => (/* binding */ Repulser)\n/* harmony export */ });\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tsparticles/engine */ \"@tsparticles/engine\");\n/* harmony import */ var _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @tsparticles/plugin-interactivity */ \"@tsparticles/plugin-interactivity\");\n/* harmony import */ var _Options_Classes_Repulse_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Options/Classes/Repulse.js */ \"./dist/browser/Options/Classes/Repulse.js\");\n\n\n\nconst repulseMode = \"repulse\",\n minDistance = 0,\n repulseRadiusFactor = 6,\n repulseRadiusPower = 3,\n squarePower = 2,\n minRadius = 0,\n minSpeed = 0,\n easingOffset = 1;\nclass Repulser extends _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.ExternalInteractorBase {\n constructor(engine, container) {\n super(container);\n this._clickRepulse = interactivityData => {\n const container = this.container,\n repulseOptions = container.actualOptions.interactivity?.modes.repulse;\n if (!repulseOptions) {\n return;\n }\n const repulse = container.repulse ?? {\n particles: []\n };\n if (!repulse.finish) {\n repulse.count ??= 0;\n repulse.count++;\n if (repulse.count === container.particles.count) {\n repulse.finish = true;\n }\n }\n if (repulse.clicking) {\n const repulseDistance = container.retina.repulseModeDistance;\n if (!repulseDistance || repulseDistance < minDistance) {\n return;\n }\n const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower),\n mouseClickPos = interactivityData.mouse.clickPosition;\n if (mouseClickPos === undefined) {\n return;\n }\n const range = new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius),\n query = container.particles.quadTree.query(range, p => this.isEnabled(interactivityData, p));\n for (const particle of query) {\n const {\n dx,\n dy,\n distance\n } = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistances)(mouseClickPos, particle.position),\n d = distance ** squarePower,\n velocity = repulseOptions.speed,\n force = -repulseRadius * velocity / d;\n if (d <= repulseRadius) {\n repulse.particles.push(particle);\n const vect = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Vector.create(dx, dy);\n vect.length = force;\n particle.velocity.setTo(vect);\n }\n }\n } else if (repulse.clicking === false) {\n for (const particle of repulse.particles) {\n particle.velocity.setTo(particle.initialVelocity);\n }\n repulse.particles = [];\n }\n };\n this._hoverRepulse = interactivityData => {\n const container = this.container,\n mousePos = interactivityData.mouse.position,\n repulseRadius = container.retina.repulseModeDistance;\n if (!repulseRadius || repulseRadius < minRadius || !mousePos) {\n return;\n }\n this._processRepulse(interactivityData, mousePos, repulseRadius, new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(mousePos.x, mousePos.y, repulseRadius));\n };\n this._processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {\n const container = this.container,\n query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p)),\n repulseOptions = container.actualOptions.interactivity?.modes.repulse;\n if (!repulseOptions) {\n return;\n }\n const {\n easing,\n speed,\n factor,\n maxSpeed\n } = repulseOptions,\n easingFunc = this._engine.getEasing(easing),\n velocity = (divRepulse?.speed ?? speed) * factor;\n for (const particle of query) {\n const {\n dx,\n dy,\n distance\n } = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistances)(particle.position, position),\n repulseFactor = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.clamp)(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed),\n normVec = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Vector.create(!distance ? velocity : dx / distance * repulseFactor, !distance ? velocity : dy / distance * repulseFactor);\n particle.position.addTo(normVec);\n }\n };\n this._singleSelectorRepulse = (interactivityData, selector, div) => {\n const container = this.container,\n repulse = container.actualOptions.interactivity?.modes.repulse;\n if (!repulse) {\n return;\n }\n const query = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.safeDocument)().querySelectorAll(selector);\n if (!query.length) {\n return;\n }\n query.forEach(item => {\n const elem = item,\n pxRatio = container.retina.pixelRatio,\n pos = {\n x: (elem.offsetLeft + elem.offsetWidth * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) * pxRatio,\n y: (elem.offsetTop + elem.offsetHeight * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) * pxRatio\n },\n repulseRadius = elem.offsetWidth * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half * pxRatio,\n area = div.type === _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.DivType.circle ? new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(pos.x, pos.y, repulseRadius) : new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio),\n divs = repulse.divs,\n divRepulse = (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.divMode)(divs, elem);\n this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);\n });\n };\n this._engine = engine;\n container.repulse ??= {\n particles: []\n };\n this.handleClickMode = (mode, interactivityData) => {\n const options = this.container.actualOptions,\n repulseOpts = options.interactivity?.modes.repulse;\n if (!repulseOpts || mode !== repulseMode) {\n return;\n }\n container.repulse ??= {\n particles: []\n };\n const repulse = container.repulse;\n repulse.clicking = true;\n repulse.count = 0;\n for (const particle of container.repulse.particles) {\n if (!this.isEnabled(interactivityData, particle)) {\n continue;\n }\n particle.velocity.setTo(particle.initialVelocity);\n }\n repulse.particles = [];\n repulse.finish = false;\n setTimeout(() => {\n if (container.destroyed) {\n return;\n }\n repulse.clicking = false;\n }, repulseOpts.duration * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.millisecondsToSeconds);\n };\n }\n clear() {}\n init() {\n const container = this.container,\n repulse = container.actualOptions.interactivity?.modes.repulse;\n if (!repulse) {\n return;\n }\n container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;\n }\n interact(interactivityData) {\n const container = this.container,\n options = container.actualOptions,\n mouseMoveStatus = interactivityData.status === _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.mouseMoveEvent,\n events = options.interactivity?.events;\n if (!events) {\n return;\n }\n const hover = events.onHover,\n hoverEnabled = hover.enable,\n hoverMode = hover.mode,\n click = events.onClick,\n clickEnabled = click.enable,\n clickMode = click.mode,\n divs = events.onDiv;\n if (mouseMoveStatus && hoverEnabled && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, hoverMode)) {\n this._hoverRepulse(interactivityData);\n } else if (clickEnabled && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, clickMode)) {\n this._clickRepulse(interactivityData);\n } else {\n (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.divModeExecute)(repulseMode, divs, (selector, div) => {\n this._singleSelectorRepulse(interactivityData, selector, div);\n });\n }\n }\n isEnabled(interactivityData, particle) {\n const container = this.container,\n options = container.actualOptions,\n mouse = interactivityData.mouse,\n events = (particle?.interactivity ?? options.interactivity)?.events;\n if (!events) {\n return false;\n }\n const divs = events.onDiv,\n hover = events.onHover,\n click = events.onClick,\n divRepulse = (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.isDivModeEnabled)(repulseMode, divs);\n if (!(divRepulse || hover.enable && !!mouse.position || click.enable && mouse.clickPosition)) {\n return false;\n }\n const hoverMode = hover.mode,\n clickMode = click.mode;\n return (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, hoverMode) || (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, clickMode) || divRepulse;\n }\n loadModeOptions(options, ...sources) {\n options.repulse ??= new _Options_Classes_Repulse_js__WEBPACK_IMPORTED_MODULE_2__.Repulse();\n for (const source of sources) {\n options.repulse.load(source?.repulse);\n }\n }\n reset() {}\n}\n\n//# sourceURL=webpack://@tsparticles/interaction-external-repulse/./dist/browser/Repulser.js?\n}");
26
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Repulser: () => (/* binding */ Repulser)\n/* harmony export */ });\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tsparticles/engine */ \"@tsparticles/engine\");\n/* harmony import */ var _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @tsparticles/plugin-interactivity */ \"@tsparticles/plugin-interactivity\");\n/* harmony import */ var _Options_Classes_Repulse_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Options/Classes/Repulse.js */ \"./dist/browser/Options/Classes/Repulse.js\");\n\n\n\nconst repulseMode = \"repulse\", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1;\nclass Repulser extends _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.ExternalInteractorBase {\n handleClickMode;\n _clickVec;\n _engine;\n _maxDistance;\n _normVec;\n constructor(engine, container){\n super(container);\n this._engine = engine;\n this._maxDistance = 0;\n this._normVec = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Vector.origin;\n this._clickVec = _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Vector.origin;\n container.repulse ??= {\n particles: []\n };\n this.handleClickMode = (mode, interactivityData)=>{\n const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;\n if (!repulseOpts || mode !== repulseMode) {\n return;\n }\n container.repulse ??= {\n particles: []\n };\n const repulse = container.repulse;\n repulse.clicking = true;\n repulse.count = 0;\n for (const particle of container.repulse.particles){\n if (!this.isEnabled(interactivityData, particle)) {\n continue;\n }\n particle.velocity.setTo(particle.initialVelocity);\n }\n repulse.particles = [];\n repulse.finish = false;\n setTimeout(()=>{\n if (container.destroyed) {\n return;\n }\n repulse.clicking = false;\n }, repulseOpts.duration * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.millisecondsToSeconds);\n };\n }\n get maxDistance() {\n return this._maxDistance;\n }\n clear() {}\n init() {\n const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;\n if (!repulse) {\n return;\n }\n this._maxDistance = repulse.distance;\n container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;\n }\n interact(interactivityData) {\n const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.mouseMoveEvent, events = options.interactivity?.events;\n if (!events) {\n return;\n }\n const hover = events.onHover, hoverEnabled = hover.enable, hoverMode = hover.mode, click = events.onClick, clickEnabled = click.enable, clickMode = click.mode, divs = events.onDiv;\n if (mouseMoveStatus && hoverEnabled && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, hoverMode)) {\n this._hoverRepulse(interactivityData);\n } else if (clickEnabled && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, clickMode)) {\n this._clickRepulse(interactivityData);\n } else {\n (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.divModeExecute)(repulseMode, divs, (selector, div)=>{\n this._singleSelectorRepulse(interactivityData, selector, div);\n });\n }\n }\n isEnabled(interactivityData, particle) {\n const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;\n if (!events) {\n return false;\n }\n const divs = events.onDiv, hover = events.onHover, click = events.onClick, divRepulse = (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.isDivModeEnabled)(repulseMode, divs);\n if (!(divRepulse || hover.enable && !!mouse.position || click.enable && mouse.clickPosition)) {\n return false;\n }\n const hoverMode = hover.mode, clickMode = click.mode;\n return (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, hoverMode) || (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, clickMode) || divRepulse;\n }\n loadModeOptions(options, ...sources) {\n options.repulse ??= new _Options_Classes_Repulse_js__WEBPACK_IMPORTED_MODULE_2__.Repulse();\n for (const source of sources){\n options.repulse.load(source?.repulse);\n }\n }\n reset() {}\n _clickRepulse = (interactivityData)=>{\n const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;\n if (!repulseOptions) {\n return;\n }\n const repulse = container.repulse ?? {\n particles: []\n };\n if (!repulse.finish) {\n repulse.count ??= 0;\n repulse.count++;\n if (repulse.count === container.particles.count) {\n repulse.finish = true;\n }\n }\n if (repulse.clicking) {\n const repulseDistance = container.retina.repulseModeDistance;\n if (!repulseDistance || repulseDistance < minDistance) {\n return;\n }\n const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;\n if (mouseClickPos === undefined) {\n return;\n }\n const range = new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.grid.query(range, (p)=>this.isEnabled(interactivityData, p));\n for (const particle of query){\n const { dx, dy, distance } = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistances)(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = -repulseRadius * velocity / d;\n if (d <= repulseRadius) {\n repulse.particles.push(particle);\n this._clickVec.x = dx;\n this._clickVec.y = dy;\n this._clickVec.length = force;\n particle.velocity.setTo(this._clickVec);\n }\n }\n } else if (repulse.clicking === false) {\n for (const particle of repulse.particles){\n particle.velocity.setTo(particle.initialVelocity);\n }\n repulse.particles = [];\n }\n };\n _hoverRepulse = (interactivityData)=>{\n const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;\n if (!repulseRadius || repulseRadius < minRadius || !mousePos) {\n return;\n }\n this._processRepulse(interactivityData, mousePos, repulseRadius, new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(mousePos.x, mousePos.y, repulseRadius));\n };\n _processRepulse = (interactivityData, position, repulseRadius, area, divRepulse)=>{\n const container = this.container, query = container.particles.grid.query(area, (p)=>this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;\n if (!repulseOptions) {\n return;\n }\n const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;\n for (const particle of query){\n const { dx, dy, distance } = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistances)(particle.position, position), repulseFactor = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.clamp)(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);\n this._normVec.x = !distance ? velocity : dx / distance * repulseFactor;\n this._normVec.y = !distance ? velocity : dy / distance * repulseFactor;\n particle.position.addTo(this._normVec);\n }\n };\n _singleSelectorRepulse = (interactivityData, selector, div)=>{\n const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;\n if (!repulse) {\n return;\n }\n const query = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.safeDocument)().querySelectorAll(selector);\n if (!query.length) {\n return;\n }\n query.forEach((item)=>{\n const elem = item, pxRatio = container.retina.pixelRatio, pos = {\n x: (elem.offsetLeft + elem.offsetWidth * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) * pxRatio,\n y: (elem.offsetTop + elem.offsetHeight * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half) * pxRatio\n }, repulseRadius = elem.offsetWidth * _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.half * pxRatio, area = div.type === _tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.DivType.circle ? new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(pos.x, pos.y, repulseRadius) : new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = (0,_tsparticles_plugin_interactivity__WEBPACK_IMPORTED_MODULE_1__.divMode)(divs, elem);\n this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);\n });\n };\n}\n\n\n//# sourceURL=webpack://@tsparticles/interaction-external-repulse/./dist/browser/Repulser.js?\n}");
27
27
 
28
28
  /***/ }
29
29
 
@@ -2,6 +2,7 @@ import { executeOnSingleOrMultiple, isNull, } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  import { RepulseDiv } from "./RepulseDiv.js";
4
4
  export class Repulse extends RepulseBase {
5
+ divs;
5
6
  load(data) {
6
7
  super.load(data);
7
8
  if (isNull(data)) {
@@ -1,5 +1,11 @@
1
1
  import { EasingType, isNull } from "@tsparticles/engine";
2
2
  export class RepulseBase {
3
+ distance;
4
+ duration;
5
+ easing;
6
+ factor;
7
+ maxSpeed;
8
+ speed;
3
9
  constructor() {
4
10
  this.distance = 200;
5
11
  this.duration = 0.4;
@@ -1,6 +1,7 @@
1
1
  import { isNull } from "@tsparticles/engine";
2
2
  import { RepulseBase } from "./RepulseBase.js";
3
3
  export class RepulseDiv extends RepulseBase {
4
+ selectors;
4
5
  constructor() {
5
6
  super();
6
7
  this.selectors = [];