@tsparticles/interaction-external-repulse 4.0.0-alpha.0 → 4.0.0-alpha.14
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/224.min.js +1 -0
- package/browser/Options/Classes/Repulse.js +1 -0
- package/browser/Options/Classes/RepulseBase.js +6 -0
- package/browser/Options/Classes/RepulseDiv.js +1 -0
- package/browser/Repulser.js +101 -90
- package/browser/index.js +6 -4
- package/cjs/Options/Classes/Repulse.js +1 -0
- package/cjs/Options/Classes/RepulseBase.js +6 -0
- package/cjs/Options/Classes/RepulseDiv.js +1 -0
- package/cjs/Repulser.js +101 -90
- package/cjs/index.js +6 -4
- package/dist_browser_Repulser_js.js +2 -2
- package/esm/Options/Classes/Repulse.js +1 -0
- package/esm/Options/Classes/RepulseBase.js +6 -0
- package/esm/Options/Classes/RepulseDiv.js +1 -0
- package/esm/Repulser.js +101 -90
- package/esm/index.js +6 -4
- package/package.json +3 -2
- package/report.html +3 -3
- package/tsparticles.interaction.external.repulse.js +49 -9
- package/tsparticles.interaction.external.repulse.min.js +2 -2
- package/types/Options/Classes/RepulseOptions.d.ts +3 -3
- package/types/Options/Interfaces/IRepulseDiv.d.ts +1 -1
- package/types/Repulser.d.ts +5 -4
- package/types/Types.d.ts +3 -2
- package/types/index.d.ts +1 -1
- package/umd/Options/Classes/Repulse.js +1 -0
- package/umd/Options/Classes/RepulseBase.js +6 -0
- package/umd/Options/Classes/RepulseDiv.js +1 -0
- package/umd/Repulser.js +103 -92
- package/umd/index.js +7 -5
- package/176.min.js +0 -2
- package/176.min.js.LICENSE.txt +0 -1
- package/tsparticles.interaction.external.repulse.min.js.LICENSE.txt +0 -1
package/224.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(this.webpackChunk_tsparticles_interaction_external_repulse=this.webpackChunk_tsparticles_interaction_external_repulse||[]).push([[224],{224(e,t,i){i.d(t,{Repulser:()=>o});var s=i(303),l=i(702),n=i(422);let r="repulse";class o extends l.ExternalInteractorBase{handleClickMode;_engine;constructor(e,t){super(t),this._engine=e,t.repulse??={particles:[]},this.handleClickMode=(e,i)=>{let l=this.container.actualOptions,n=l.interactivity?.modes.repulse;if(!n||e!==r)return;t.repulse??={particles:[]};let o=t.repulse;for(let e of(o.clicking=!0,o.count=0,t.repulse.particles))this.isEnabled(i,e)&&e.velocity.setTo(e.initialVelocity);o.particles=[],o.finish=!1,setTimeout(()=>{t.destroyed||(o.clicking=!1)},n.duration*s.millisecondsToSeconds)}}clear(){}init(){let e=this.container,t=e.actualOptions.interactivity?.modes.repulse;t&&(e.retina.repulseModeDistance=t.distance*e.retina.pixelRatio)}interact(e){let t=this.container.actualOptions,i=e.status===l.mouseMoveEvent,n=t.interactivity?.events;if(!n)return;let o=n.onHover,c=o.enable,a=o.mode,p=n.onClick,u=p.enable,f=p.mode,d=n.onDiv;i&&c&&(0,s.isInArray)(r,a)?this._hoverRepulse(e):u&&(0,s.isInArray)(r,f)?this._clickRepulse(e):(0,l.divModeExecute)(r,d,(t,i)=>{this._singleSelectorRepulse(e,t,i)})}isEnabled(e,t){let i=this.container.actualOptions,n=e.mouse,o=(t?.interactivity??i.interactivity)?.events;if(!o)return!1;let c=o.onDiv,a=o.onHover,p=o.onClick,u=(0,l.isDivModeEnabled)(r,c);if(!(u||a.enable&&n.position||p.enable&&n.clickPosition))return!1;let f=a.mode,d=p.mode;return(0,s.isInArray)(r,f)||(0,s.isInArray)(r,d)||u}loadModeOptions(e,...t){for(let i of(e.repulse??=new n.Z,t))e.repulse.load(i?.repulse)}reset(){}_clickRepulse=e=>{let t=this.container,i=t.actualOptions.interactivity?.modes.repulse;if(!i)return;let l=t.repulse??{particles:[]};if(l.finish||(l.count??=0,l.count++,l.count===t.particles.count&&(l.finish=!0)),l.clicking){let n=t.retina.repulseModeDistance;if(!n||n<0)return;let r=Math.pow(n/6,3),o=e.mouse.clickPosition;if(void 0===o)return;let c=new s.Circle(o.x,o.y,r);for(let n of t.particles.quadTree.query(c,t=>this.isEnabled(e,t))){let{dx:e,dy:t,distance:c}=(0,s.getDistances)(o,n.position),a=c**2,p=-r*i.speed/a;if(a<=r){l.particles.push(n);let i=s.Vector.create(e,t);i.length=p,n.velocity.setTo(i)}}}else if(!1===l.clicking){for(let e of l.particles)e.velocity.setTo(e.initialVelocity);l.particles=[]}};_hoverRepulse=e=>{let t=this.container,i=e.mouse.position,l=t.retina.repulseModeDistance;l&&!(l<0)&&i&&this._processRepulse(e,i,l,new s.Circle(i.x,i.y,l))};_processRepulse=(e,t,i,l,n)=>{let r=this.container,o=r.particles.quadTree.query(l,t=>this.isEnabled(e,t)),c=r.actualOptions.interactivity?.modes.repulse;if(!c)return;let{easing:a,speed:p,factor:u,maxSpeed:f}=c,d=this._engine.getEasing(a),h=(n?.speed??p)*u;for(let e of o){let{dx:l,dy:n,distance:r}=(0,s.getDistances)(e.position,t),o=(0,s.clamp)(d(1-r/i)*h,0,f),c=s.Vector.create(r?l/r*o:h,r?n/r*o:h);e.position.addTo(c)}};_singleSelectorRepulse=(e,t,i)=>{let n=this.container,r=n.actualOptions.interactivity?.modes.repulse;if(!r)return;let o=(0,s.safeDocument)().querySelectorAll(t);o.length&&o.forEach(t=>{let o=n.retina.pixelRatio,c={x:(t.offsetLeft+t.offsetWidth*s.half)*o,y:(t.offsetTop+t.offsetHeight*s.half)*o},a=t.offsetWidth*s.half*o,p=i.type===l.DivType.circle?new s.Circle(c.x,c.y,a):new s.Rectangle(t.offsetLeft*o,t.offsetTop*o,t.offsetWidth*o,t.offsetHeight*o),u=r.divs,f=(0,l.divMode)(u,t);this._processRepulse(e,c,a,p,f)})}}}}]);
|
|
@@ -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)) {
|
package/browser/Repulser.js
CHANGED
|
@@ -1,90 +1,16 @@
|
|
|
1
|
-
import { Circle,
|
|
1
|
+
import { Circle, Rectangle, Vector, clamp, getDistances, half, isInArray, millisecondsToSeconds, safeDocument, } from "@tsparticles/engine";
|
|
2
|
+
import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
|
|
2
3
|
import { Repulse } from "./Options/Classes/Repulse.js";
|
|
3
|
-
const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1
|
|
4
|
+
const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1;
|
|
4
5
|
export class Repulser extends ExternalInteractorBase {
|
|
6
|
+
handleClickMode;
|
|
7
|
+
_engine;
|
|
5
8
|
constructor(engine, container) {
|
|
6
9
|
super(container);
|
|
7
|
-
this._clickRepulse = () => {
|
|
8
|
-
const container = this.container, repulseOptions = container.actualOptions.interactivity.modes.repulse;
|
|
9
|
-
if (!repulseOptions) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const repulse = container.repulse ?? { particles: [] };
|
|
13
|
-
if (!repulse.finish) {
|
|
14
|
-
repulse.count ??= 0;
|
|
15
|
-
repulse.count++;
|
|
16
|
-
if (repulse.count === container.particles.count) {
|
|
17
|
-
repulse.finish = true;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (repulse.clicking) {
|
|
21
|
-
const repulseDistance = container.retina.repulseModeDistance;
|
|
22
|
-
if (!repulseDistance || repulseDistance < minDistance) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = container.interactivity.mouse.clickPosition;
|
|
26
|
-
if (mouseClickPos === undefined) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(p));
|
|
30
|
-
for (const particle of query) {
|
|
31
|
-
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
32
|
-
if (d <= repulseRadius) {
|
|
33
|
-
repulse.particles.push(particle);
|
|
34
|
-
const vect = Vector.create(dx, dy);
|
|
35
|
-
vect.length = force;
|
|
36
|
-
particle.velocity.setTo(vect);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (repulse.clicking === false) {
|
|
41
|
-
for (const particle of repulse.particles) {
|
|
42
|
-
particle.velocity.setTo(particle.initialVelocity);
|
|
43
|
-
}
|
|
44
|
-
repulse.particles = [];
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
this._hoverRepulse = () => {
|
|
48
|
-
const container = this.container, mousePos = container.interactivity.mouse.position, repulseRadius = container.retina.repulseModeDistance;
|
|
49
|
-
if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
this._processRepulse(mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
|
|
53
|
-
};
|
|
54
|
-
this._processRepulse = (position, repulseRadius, area, divRepulse) => {
|
|
55
|
-
const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(p)), repulseOptions = container.actualOptions.interactivity.modes.repulse;
|
|
56
|
-
if (!repulseOptions) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
|
|
60
|
-
for (const particle of query) {
|
|
61
|
-
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);
|
|
62
|
-
particle.position.addTo(normVec);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
this._singleSelectorRepulse = (selector, div) => {
|
|
66
|
-
const container = this.container, repulse = container.actualOptions.interactivity.modes.repulse;
|
|
67
|
-
if (!repulse) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const query = safeDocument().querySelectorAll(selector);
|
|
71
|
-
if (!query.length) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
query.forEach(item => {
|
|
75
|
-
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
|
|
76
|
-
x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
|
|
77
|
-
y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
|
|
78
|
-
}, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
|
|
79
|
-
? new Circle(pos.x, pos.y, repulseRadius)
|
|
80
|
-
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
|
|
81
|
-
this._processRepulse(pos, repulseRadius, area, divRepulse);
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
10
|
this._engine = engine;
|
|
85
11
|
container.repulse ??= { particles: [] };
|
|
86
|
-
this.handleClickMode = (mode) => {
|
|
87
|
-
const options = this.container.actualOptions, repulseOpts = options.interactivity
|
|
12
|
+
this.handleClickMode = (mode, interactivityData) => {
|
|
13
|
+
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
|
|
88
14
|
if (!repulseOpts || mode !== repulseMode) {
|
|
89
15
|
return;
|
|
90
16
|
}
|
|
@@ -93,7 +19,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
93
19
|
repulse.clicking = true;
|
|
94
20
|
repulse.count = 0;
|
|
95
21
|
for (const particle of container.repulse.particles) {
|
|
96
|
-
if (!this.isEnabled(particle)) {
|
|
22
|
+
if (!this.isEnabled(interactivityData, particle)) {
|
|
97
23
|
continue;
|
|
98
24
|
}
|
|
99
25
|
particle.velocity.setTo(particle.initialVelocity);
|
|
@@ -111,28 +37,36 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
111
37
|
clear() {
|
|
112
38
|
}
|
|
113
39
|
init() {
|
|
114
|
-
const container = this.container, repulse = container.actualOptions.interactivity
|
|
40
|
+
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
115
41
|
if (!repulse) {
|
|
116
42
|
return;
|
|
117
43
|
}
|
|
118
44
|
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
|
|
119
45
|
}
|
|
120
|
-
interact() {
|
|
121
|
-
const container = this.container, options = container.actualOptions, mouseMoveStatus =
|
|
46
|
+
interact(interactivityData) {
|
|
47
|
+
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
|
|
48
|
+
if (!events) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const hover = events.onHover, hoverEnabled = hover.enable, hoverMode = hover.mode, click = events.onClick, clickEnabled = click.enable, clickMode = click.mode, divs = events.onDiv;
|
|
122
52
|
if (mouseMoveStatus && hoverEnabled && isInArray(repulseMode, hoverMode)) {
|
|
123
|
-
this._hoverRepulse();
|
|
53
|
+
this._hoverRepulse(interactivityData);
|
|
124
54
|
}
|
|
125
55
|
else if (clickEnabled && isInArray(repulseMode, clickMode)) {
|
|
126
|
-
this._clickRepulse();
|
|
56
|
+
this._clickRepulse(interactivityData);
|
|
127
57
|
}
|
|
128
58
|
else {
|
|
129
59
|
divModeExecute(repulseMode, divs, (selector, div) => {
|
|
130
|
-
this._singleSelectorRepulse(selector, div);
|
|
60
|
+
this._singleSelectorRepulse(interactivityData, selector, div);
|
|
131
61
|
});
|
|
132
62
|
}
|
|
133
63
|
}
|
|
134
|
-
isEnabled(particle) {
|
|
135
|
-
const container = this.container, options = container.actualOptions, mouse =
|
|
64
|
+
isEnabled(interactivityData, particle) {
|
|
65
|
+
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
|
|
66
|
+
if (!events) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const divs = events.onDiv, hover = events.onHover, click = events.onClick, divRepulse = isDivModeEnabled(repulseMode, divs);
|
|
136
70
|
if (!(divRepulse || (hover.enable && !!mouse.position) || (click.enable && mouse.clickPosition))) {
|
|
137
71
|
return false;
|
|
138
72
|
}
|
|
@@ -147,4 +81,81 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
147
81
|
}
|
|
148
82
|
reset() {
|
|
149
83
|
}
|
|
84
|
+
_clickRepulse = interactivityData => {
|
|
85
|
+
const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
|
|
86
|
+
if (!repulseOptions) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const repulse = container.repulse ?? { particles: [] };
|
|
90
|
+
if (!repulse.finish) {
|
|
91
|
+
repulse.count ??= 0;
|
|
92
|
+
repulse.count++;
|
|
93
|
+
if (repulse.count === container.particles.count) {
|
|
94
|
+
repulse.finish = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (repulse.clicking) {
|
|
98
|
+
const repulseDistance = container.retina.repulseModeDistance;
|
|
99
|
+
if (!repulseDistance || repulseDistance < minDistance) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
|
|
103
|
+
if (mouseClickPos === undefined) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(interactivityData, p));
|
|
107
|
+
for (const particle of query) {
|
|
108
|
+
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
109
|
+
if (d <= repulseRadius) {
|
|
110
|
+
repulse.particles.push(particle);
|
|
111
|
+
const vect = Vector.create(dx, dy);
|
|
112
|
+
vect.length = force;
|
|
113
|
+
particle.velocity.setTo(vect);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (repulse.clicking === false) {
|
|
118
|
+
for (const particle of repulse.particles) {
|
|
119
|
+
particle.velocity.setTo(particle.initialVelocity);
|
|
120
|
+
}
|
|
121
|
+
repulse.particles = [];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
_hoverRepulse = interactivityData => {
|
|
125
|
+
const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
|
|
126
|
+
if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
|
|
130
|
+
};
|
|
131
|
+
_processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
|
|
132
|
+
const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
|
|
133
|
+
if (!repulseOptions) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
|
|
137
|
+
for (const particle of query) {
|
|
138
|
+
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);
|
|
139
|
+
particle.position.addTo(normVec);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
_singleSelectorRepulse = (interactivityData, selector, div) => {
|
|
143
|
+
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
144
|
+
if (!repulse) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const query = safeDocument().querySelectorAll(selector);
|
|
148
|
+
if (!query.length) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
query.forEach(item => {
|
|
152
|
+
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
|
|
153
|
+
x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
|
|
154
|
+
y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
|
|
155
|
+
}, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
|
|
156
|
+
? new Circle(pos.x, pos.y, repulseRadius)
|
|
157
|
+
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
|
|
158
|
+
this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
150
161
|
}
|
package/browser/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.0-alpha.
|
|
3
|
-
engine.register(e => {
|
|
4
|
-
|
|
1
|
+
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
+
engine.checkVersion("4.0.0-alpha.14");
|
|
3
|
+
await engine.register(async (e) => {
|
|
4
|
+
const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
|
|
5
|
+
await loadInteractivityPlugin(e);
|
|
6
|
+
e.addInteractor?.("externalRepulse", async (container) => {
|
|
5
7
|
const { Repulser } = await import("./Repulser.js");
|
|
6
8
|
return new Repulser(engine, container);
|
|
7
9
|
});
|
|
@@ -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)) {
|
package/cjs/Repulser.js
CHANGED
|
@@ -1,90 +1,16 @@
|
|
|
1
|
-
import { Circle,
|
|
1
|
+
import { Circle, Rectangle, Vector, clamp, getDistances, half, isInArray, millisecondsToSeconds, safeDocument, } from "@tsparticles/engine";
|
|
2
|
+
import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
|
|
2
3
|
import { Repulse } from "./Options/Classes/Repulse.js";
|
|
3
|
-
const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1
|
|
4
|
+
const repulseMode = "repulse", minDistance = 0, repulseRadiusFactor = 6, repulseRadiusPower = 3, squarePower = 2, minRadius = 0, minSpeed = 0, easingOffset = 1;
|
|
4
5
|
export class Repulser extends ExternalInteractorBase {
|
|
6
|
+
handleClickMode;
|
|
7
|
+
_engine;
|
|
5
8
|
constructor(engine, container) {
|
|
6
9
|
super(container);
|
|
7
|
-
this._clickRepulse = () => {
|
|
8
|
-
const container = this.container, repulseOptions = container.actualOptions.interactivity.modes.repulse;
|
|
9
|
-
if (!repulseOptions) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const repulse = container.repulse ?? { particles: [] };
|
|
13
|
-
if (!repulse.finish) {
|
|
14
|
-
repulse.count ??= 0;
|
|
15
|
-
repulse.count++;
|
|
16
|
-
if (repulse.count === container.particles.count) {
|
|
17
|
-
repulse.finish = true;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
if (repulse.clicking) {
|
|
21
|
-
const repulseDistance = container.retina.repulseModeDistance;
|
|
22
|
-
if (!repulseDistance || repulseDistance < minDistance) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = container.interactivity.mouse.clickPosition;
|
|
26
|
-
if (mouseClickPos === undefined) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(p));
|
|
30
|
-
for (const particle of query) {
|
|
31
|
-
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
32
|
-
if (d <= repulseRadius) {
|
|
33
|
-
repulse.particles.push(particle);
|
|
34
|
-
const vect = Vector.create(dx, dy);
|
|
35
|
-
vect.length = force;
|
|
36
|
-
particle.velocity.setTo(vect);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (repulse.clicking === false) {
|
|
41
|
-
for (const particle of repulse.particles) {
|
|
42
|
-
particle.velocity.setTo(particle.initialVelocity);
|
|
43
|
-
}
|
|
44
|
-
repulse.particles = [];
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
this._hoverRepulse = () => {
|
|
48
|
-
const container = this.container, mousePos = container.interactivity.mouse.position, repulseRadius = container.retina.repulseModeDistance;
|
|
49
|
-
if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
this._processRepulse(mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
|
|
53
|
-
};
|
|
54
|
-
this._processRepulse = (position, repulseRadius, area, divRepulse) => {
|
|
55
|
-
const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(p)), repulseOptions = container.actualOptions.interactivity.modes.repulse;
|
|
56
|
-
if (!repulseOptions) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
|
|
60
|
-
for (const particle of query) {
|
|
61
|
-
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);
|
|
62
|
-
particle.position.addTo(normVec);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
this._singleSelectorRepulse = (selector, div) => {
|
|
66
|
-
const container = this.container, repulse = container.actualOptions.interactivity.modes.repulse;
|
|
67
|
-
if (!repulse) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const query = safeDocument().querySelectorAll(selector);
|
|
71
|
-
if (!query.length) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
query.forEach(item => {
|
|
75
|
-
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
|
|
76
|
-
x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
|
|
77
|
-
y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
|
|
78
|
-
}, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
|
|
79
|
-
? new Circle(pos.x, pos.y, repulseRadius)
|
|
80
|
-
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
|
|
81
|
-
this._processRepulse(pos, repulseRadius, area, divRepulse);
|
|
82
|
-
});
|
|
83
|
-
};
|
|
84
10
|
this._engine = engine;
|
|
85
11
|
container.repulse ??= { particles: [] };
|
|
86
|
-
this.handleClickMode = (mode) => {
|
|
87
|
-
const options = this.container.actualOptions, repulseOpts = options.interactivity
|
|
12
|
+
this.handleClickMode = (mode, interactivityData) => {
|
|
13
|
+
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
|
|
88
14
|
if (!repulseOpts || mode !== repulseMode) {
|
|
89
15
|
return;
|
|
90
16
|
}
|
|
@@ -93,7 +19,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
93
19
|
repulse.clicking = true;
|
|
94
20
|
repulse.count = 0;
|
|
95
21
|
for (const particle of container.repulse.particles) {
|
|
96
|
-
if (!this.isEnabled(particle)) {
|
|
22
|
+
if (!this.isEnabled(interactivityData, particle)) {
|
|
97
23
|
continue;
|
|
98
24
|
}
|
|
99
25
|
particle.velocity.setTo(particle.initialVelocity);
|
|
@@ -111,28 +37,36 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
111
37
|
clear() {
|
|
112
38
|
}
|
|
113
39
|
init() {
|
|
114
|
-
const container = this.container, repulse = container.actualOptions.interactivity
|
|
40
|
+
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
115
41
|
if (!repulse) {
|
|
116
42
|
return;
|
|
117
43
|
}
|
|
118
44
|
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
|
|
119
45
|
}
|
|
120
|
-
interact() {
|
|
121
|
-
const container = this.container, options = container.actualOptions, mouseMoveStatus =
|
|
46
|
+
interact(interactivityData) {
|
|
47
|
+
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
|
|
48
|
+
if (!events) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const hover = events.onHover, hoverEnabled = hover.enable, hoverMode = hover.mode, click = events.onClick, clickEnabled = click.enable, clickMode = click.mode, divs = events.onDiv;
|
|
122
52
|
if (mouseMoveStatus && hoverEnabled && isInArray(repulseMode, hoverMode)) {
|
|
123
|
-
this._hoverRepulse();
|
|
53
|
+
this._hoverRepulse(interactivityData);
|
|
124
54
|
}
|
|
125
55
|
else if (clickEnabled && isInArray(repulseMode, clickMode)) {
|
|
126
|
-
this._clickRepulse();
|
|
56
|
+
this._clickRepulse(interactivityData);
|
|
127
57
|
}
|
|
128
58
|
else {
|
|
129
59
|
divModeExecute(repulseMode, divs, (selector, div) => {
|
|
130
|
-
this._singleSelectorRepulse(selector, div);
|
|
60
|
+
this._singleSelectorRepulse(interactivityData, selector, div);
|
|
131
61
|
});
|
|
132
62
|
}
|
|
133
63
|
}
|
|
134
|
-
isEnabled(particle) {
|
|
135
|
-
const container = this.container, options = container.actualOptions, mouse =
|
|
64
|
+
isEnabled(interactivityData, particle) {
|
|
65
|
+
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
|
|
66
|
+
if (!events) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const divs = events.onDiv, hover = events.onHover, click = events.onClick, divRepulse = isDivModeEnabled(repulseMode, divs);
|
|
136
70
|
if (!(divRepulse || (hover.enable && !!mouse.position) || (click.enable && mouse.clickPosition))) {
|
|
137
71
|
return false;
|
|
138
72
|
}
|
|
@@ -147,4 +81,81 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
147
81
|
}
|
|
148
82
|
reset() {
|
|
149
83
|
}
|
|
84
|
+
_clickRepulse = interactivityData => {
|
|
85
|
+
const container = this.container, repulseOptions = container.actualOptions.interactivity?.modes.repulse;
|
|
86
|
+
if (!repulseOptions) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const repulse = container.repulse ?? { particles: [] };
|
|
90
|
+
if (!repulse.finish) {
|
|
91
|
+
repulse.count ??= 0;
|
|
92
|
+
repulse.count++;
|
|
93
|
+
if (repulse.count === container.particles.count) {
|
|
94
|
+
repulse.finish = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (repulse.clicking) {
|
|
98
|
+
const repulseDistance = container.retina.repulseModeDistance;
|
|
99
|
+
if (!repulseDistance || repulseDistance < minDistance) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const repulseRadius = Math.pow(repulseDistance / repulseRadiusFactor, repulseRadiusPower), mouseClickPos = interactivityData.mouse.clickPosition;
|
|
103
|
+
if (mouseClickPos === undefined) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const range = new Circle(mouseClickPos.x, mouseClickPos.y, repulseRadius), query = container.particles.quadTree.query(range, p => this.isEnabled(interactivityData, p));
|
|
107
|
+
for (const particle of query) {
|
|
108
|
+
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
109
|
+
if (d <= repulseRadius) {
|
|
110
|
+
repulse.particles.push(particle);
|
|
111
|
+
const vect = Vector.create(dx, dy);
|
|
112
|
+
vect.length = force;
|
|
113
|
+
particle.velocity.setTo(vect);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (repulse.clicking === false) {
|
|
118
|
+
for (const particle of repulse.particles) {
|
|
119
|
+
particle.velocity.setTo(particle.initialVelocity);
|
|
120
|
+
}
|
|
121
|
+
repulse.particles = [];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
_hoverRepulse = interactivityData => {
|
|
125
|
+
const container = this.container, mousePos = interactivityData.mouse.position, repulseRadius = container.retina.repulseModeDistance;
|
|
126
|
+
if (!repulseRadius || repulseRadius < minRadius || !mousePos) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this._processRepulse(interactivityData, mousePos, repulseRadius, new Circle(mousePos.x, mousePos.y, repulseRadius));
|
|
130
|
+
};
|
|
131
|
+
_processRepulse = (interactivityData, position, repulseRadius, area, divRepulse) => {
|
|
132
|
+
const container = this.container, query = container.particles.quadTree.query(area, p => this.isEnabled(interactivityData, p)), repulseOptions = container.actualOptions.interactivity?.modes.repulse;
|
|
133
|
+
if (!repulseOptions) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const { easing, speed, factor, maxSpeed } = repulseOptions, easingFunc = this._engine.getEasing(easing), velocity = (divRepulse?.speed ?? speed) * factor;
|
|
137
|
+
for (const particle of query) {
|
|
138
|
+
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);
|
|
139
|
+
particle.position.addTo(normVec);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
_singleSelectorRepulse = (interactivityData, selector, div) => {
|
|
143
|
+
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
144
|
+
if (!repulse) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const query = safeDocument().querySelectorAll(selector);
|
|
148
|
+
if (!query.length) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
query.forEach(item => {
|
|
152
|
+
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
|
|
153
|
+
x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
|
|
154
|
+
y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
|
|
155
|
+
}, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === DivType.circle
|
|
156
|
+
? new Circle(pos.x, pos.y, repulseRadius)
|
|
157
|
+
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), divs = repulse.divs, divRepulse = divMode(divs, elem);
|
|
158
|
+
this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
150
161
|
}
|
package/cjs/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.0-alpha.
|
|
3
|
-
engine.register(e => {
|
|
4
|
-
|
|
1
|
+
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
+
engine.checkVersion("4.0.0-alpha.14");
|
|
3
|
+
await engine.register(async (e) => {
|
|
4
|
+
const { loadInteractivityPlugin } = await import("@tsparticles/plugin-interactivity");
|
|
5
|
+
await loadInteractivityPlugin(e);
|
|
6
|
+
e.addInteractor?.("externalRepulse", async (container) => {
|
|
5
7
|
const { Repulser } = await import("./Repulser.js");
|
|
6
8
|
return new Repulser(engine, container);
|
|
7
9
|
});
|
|
@@ -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.
|
|
7
|
+
* v4.0.0-alpha.14
|
|
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 _Options_Classes_Repulse_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Options/Classes/Repulse.js */ \"./dist/browser/Options/Classes/Repulse.js\");\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,\n half = 0.5;\nclass Repulser extends _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.ExternalInteractorBase {\n constructor(engine, container) {\n super(container);\n this._clickRepulse = () => {\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 = container.interactivity.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(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 = () => {\n const container = this.container,\n mousePos = container.interactivity.mouse.position,\n repulseRadius = container.retina.repulseModeDistance;\n if (!repulseRadius || repulseRadius < minRadius || !mousePos) {\n return;\n }\n this._processRepulse(mousePos, repulseRadius, new _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.Circle(mousePos.x, mousePos.y, repulseRadius));\n };\n this._processRepulse = (position, repulseRadius, area, divRepulse) => {\n const container = this.container,\n query = container.particles.quadTree.query(area, p => this.isEnabled(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 = (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 * half) * pxRatio,\n y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio\n },\n repulseRadius = elem.offsetWidth * half * pxRatio,\n area = div.type === _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.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_engine__WEBPACK_IMPORTED_MODULE_0__.divMode)(divs, elem);\n this._processRepulse(pos, repulseRadius, area, divRepulse);\n });\n };\n this._engine = engine;\n container.repulse ??= {\n particles: []\n };\n this.handleClickMode = mode => {\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(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() {\n const container = this.container,\n options = container.actualOptions,\n mouseMoveStatus = container.interactivity.status === _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.mouseMoveEvent,\n events = options.interactivity.events,\n 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();\n } else if (clickEnabled && (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isInArray)(repulseMode, clickMode)) {\n this._clickRepulse();\n } else {\n (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.divModeExecute)(repulseMode, divs, (selector, div) => {\n this._singleSelectorRepulse(selector, div);\n });\n }\n }\n isEnabled(particle) {\n const container = this.container,\n options = container.actualOptions,\n mouse = container.interactivity.mouse,\n events = (particle?.interactivity ?? options.interactivity).events,\n divs = events.onDiv,\n hover = events.onHover,\n click = events.onClick,\n divRepulse = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.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_1__.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 _engine;\n constructor(engine, container){\n super(container);\n this._engine = engine;\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 clear() {}\n init() {\n const container = this.container, 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, 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.quadTree.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 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 _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.quadTree.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), 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 _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)) {
|