@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 +1 -0
- package/README.md +5 -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 +92 -77
- package/browser/index.js +3 -3
- 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 +92 -77
- package/cjs/index.js +3 -3
- 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 +92 -77
- package/esm/index.js +3 -3
- package/package.json +3 -3
- package/report.html +3 -3
- package/tsparticles.interaction.external.repulse.js +45 -33
- package/tsparticles.interaction.external.repulse.min.js +2 -2
- package/types/Repulser.d.ts +4 -0
- 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 +92 -77
- package/umd/index.js +3 -3
- package/783.min.js +0 -2
- package/783.min.js.LICENSE.txt +0 -1
- package/tsparticles.interaction.external.repulse.min.js.LICENSE.txt +0 -1
package/esm/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/esm/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.0-
|
|
2
|
+
engine.checkVersion("4.0.0-beta.0");
|
|
3
3
|
await engine.register(async (e) => {
|
|
4
|
-
const {
|
|
5
|
-
|
|
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);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsparticles/interaction-external-repulse",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-beta.0",
|
|
4
4
|
"description": "tsParticles repulse external interaction",
|
|
5
5
|
"homepage": "https://particles.js.org",
|
|
6
6
|
"repository": {
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"./package.json": "./package.json"
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"@tsparticles/engine": "4.0.0-
|
|
91
|
-
"@tsparticles/plugin-interactivity": "4.0.0-
|
|
90
|
+
"@tsparticles/engine": "4.0.0-beta.0",
|
|
91
|
+
"@tsparticles/plugin-interactivity": "4.0.0-beta.0"
|
|
92
92
|
},
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public"
|