@tsparticles/interaction-external-repulse 4.0.0-beta.9 → 4.0.1
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/browser/Options/Classes/RepulseBase.js +13 -0
- package/browser/Repulser.js +67 -1
- package/browser/browser.js +5 -0
- package/browser/index.js +6 -6
- package/browser/index.lazy.js +15 -0
- package/cjs/Options/Classes/RepulseBase.js +13 -0
- package/cjs/Repulser.js +67 -1
- package/cjs/browser.js +5 -0
- package/cjs/index.js +6 -6
- package/cjs/index.lazy.js +15 -0
- package/esm/Options/Classes/RepulseBase.js +13 -0
- package/esm/Repulser.js +67 -1
- package/esm/browser.js +5 -0
- package/esm/index.js +6 -6
- package/esm/index.lazy.js +15 -0
- package/package.json +10 -3
- package/report.html +4949 -94
- package/tsparticles.interaction.external.repulse.js +346 -379
- package/tsparticles.interaction.external.repulse.min.js +1 -2
- package/types/Options/Classes/RepulseBase.d.ts +2 -1
- package/types/Options/Interfaces/IRepulseBase.d.ts +7 -0
- package/types/Repulser.d.ts +4 -0
- package/types/Types.d.ts +1 -2
- package/types/browser.d.ts +1 -0
- package/types/index.lazy.d.ts +8 -0
- package/456.min.js +0 -1
- package/dist_browser_Repulser_js.js +0 -30
|
@@ -5,6 +5,7 @@ export class RepulseBase {
|
|
|
5
5
|
easing;
|
|
6
6
|
factor;
|
|
7
7
|
maxSpeed;
|
|
8
|
+
restore;
|
|
8
9
|
speed;
|
|
9
10
|
constructor() {
|
|
10
11
|
this.distance = 200;
|
|
@@ -13,6 +14,12 @@ export class RepulseBase {
|
|
|
13
14
|
this.speed = 1;
|
|
14
15
|
this.maxSpeed = 50;
|
|
15
16
|
this.easing = EasingType.easeOutQuad;
|
|
17
|
+
this.restore = {
|
|
18
|
+
enable: false,
|
|
19
|
+
delay: 0,
|
|
20
|
+
speed: 0.08,
|
|
21
|
+
follow: true,
|
|
22
|
+
};
|
|
16
23
|
}
|
|
17
24
|
load(data) {
|
|
18
25
|
if (isNull(data)) {
|
|
@@ -36,5 +43,11 @@ export class RepulseBase {
|
|
|
36
43
|
if (data.maxSpeed !== undefined) {
|
|
37
44
|
this.maxSpeed = data.maxSpeed;
|
|
38
45
|
}
|
|
46
|
+
if (data.restore !== undefined) {
|
|
47
|
+
this.restore.enable = data.restore.enable ?? this.restore.enable;
|
|
48
|
+
this.restore.delay = data.restore.delay ?? this.restore.delay;
|
|
49
|
+
this.restore.speed = data.restore.speed ?? this.restore.speed;
|
|
50
|
+
this.restore.follow = data.restore.follow ?? this.restore.follow;
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
53
|
}
|
package/browser/Repulser.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { Circle, Rectangle, Vector, clamp, getDistances, half, isInArray, millisecondsToSeconds, safeDocument, } from "@tsparticles/engine";
|
|
2
2
|
import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
|
|
3
3
|
import { Repulse } from "./Options/Classes/Repulse.js";
|
|
4
|
-
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, minRestoreSpeed = 0.001, maxRestoreSpeed = 1, restoreEpsilon = 0.5;
|
|
5
5
|
export class Repulser extends ExternalInteractorBase {
|
|
6
6
|
handleClickMode;
|
|
7
7
|
_clickVec;
|
|
8
|
+
_interactedThisFrame;
|
|
8
9
|
_maxDistance;
|
|
9
10
|
_normVec;
|
|
10
11
|
_pluginManager;
|
|
12
|
+
_restoreData;
|
|
11
13
|
constructor(pluginManager, container) {
|
|
12
14
|
super(container);
|
|
13
15
|
this._pluginManager = pluginManager;
|
|
14
16
|
this._maxDistance = 0;
|
|
15
17
|
this._normVec = Vector.origin;
|
|
18
|
+
this._interactedThisFrame = new Set();
|
|
16
19
|
this._clickVec = Vector.origin;
|
|
20
|
+
this._restoreData = new Map();
|
|
17
21
|
container.repulse ??= { particles: [] };
|
|
18
22
|
this.handleClickMode = (mode, interactivityData) => {
|
|
19
23
|
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
|
|
@@ -54,6 +58,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
54
58
|
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
|
|
55
59
|
}
|
|
56
60
|
interact(interactivityData) {
|
|
61
|
+
this._interactedThisFrame.clear();
|
|
57
62
|
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
|
|
58
63
|
if (!events) {
|
|
59
64
|
return;
|
|
@@ -70,6 +75,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
70
75
|
this._singleSelectorRepulse(interactivityData, selector, div);
|
|
71
76
|
});
|
|
72
77
|
}
|
|
78
|
+
this._restoreParticles();
|
|
73
79
|
}
|
|
74
80
|
isEnabled(interactivityData, particle) {
|
|
75
81
|
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
|
|
@@ -117,6 +123,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
117
123
|
for (const particle of query) {
|
|
118
124
|
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
119
125
|
if (d <= repulseRadius) {
|
|
126
|
+
this._trackInteractedParticle(particle);
|
|
120
127
|
repulse.particles.push(particle);
|
|
121
128
|
this._clickVec.x = dx;
|
|
122
129
|
this._clickVec.y = dy;
|
|
@@ -149,9 +156,46 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
149
156
|
const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
|
|
150
157
|
this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
|
|
151
158
|
this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
|
|
159
|
+
this._trackInteractedParticle(particle);
|
|
152
160
|
particle.position.addTo(this._normVec);
|
|
153
161
|
}
|
|
154
162
|
};
|
|
163
|
+
_restoreParticles() {
|
|
164
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
165
|
+
if (!restore?.enable || !this._restoreData.size) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds, restoreSpeed = Math.max(minRestoreSpeed, Math.min(maxRestoreSpeed, restore.speed));
|
|
169
|
+
for (const [particle, restoreData] of this._restoreData) {
|
|
170
|
+
if (this._interactedThisFrame.has(particle)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (particle.destroyed) {
|
|
174
|
+
this._restoreData.delete(particle);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const target = restoreData.target;
|
|
178
|
+
if (now - restoreData.lastInteractionTime < restoreDelay) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (restore.follow && particle.options.move.enable) {
|
|
182
|
+
target.x += particle.velocity.x;
|
|
183
|
+
target.y += particle.velocity.y;
|
|
184
|
+
target.z += particle.velocity.z;
|
|
185
|
+
}
|
|
186
|
+
const dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
|
|
187
|
+
particle.position.x += dx * restoreSpeed;
|
|
188
|
+
particle.position.y += dy * restoreSpeed;
|
|
189
|
+
particle.position.z += dz * restoreSpeed;
|
|
190
|
+
if (Math.abs(dx) <= restoreEpsilon && Math.abs(dy) <= restoreEpsilon) {
|
|
191
|
+
particle.position.x = target.x;
|
|
192
|
+
particle.position.y = target.y;
|
|
193
|
+
particle.position.z = target.z;
|
|
194
|
+
this._restoreData.delete(particle);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
155
199
|
_singleSelectorRepulse = (interactivityData, selector, div) => {
|
|
156
200
|
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
157
201
|
if (!repulse) {
|
|
@@ -171,4 +215,26 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
171
215
|
this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
|
|
172
216
|
});
|
|
173
217
|
};
|
|
218
|
+
_trackInteractedParticle(particle) {
|
|
219
|
+
this._interactedThisFrame.add(particle);
|
|
220
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
221
|
+
if (!restore?.enable) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
let restoreData = this._restoreData.get(particle);
|
|
226
|
+
if (!restoreData) {
|
|
227
|
+
restoreData = {
|
|
228
|
+
target: particle.position.copy(),
|
|
229
|
+
lastInteractionTime: now,
|
|
230
|
+
};
|
|
231
|
+
this._restoreData.set(particle, restoreData);
|
|
232
|
+
}
|
|
233
|
+
restoreData.lastInteractionTime = now;
|
|
234
|
+
if (restore.follow && particle.options.move.enable) {
|
|
235
|
+
restoreData.target.x += particle.velocity.x;
|
|
236
|
+
restoreData.target.y += particle.velocity.y;
|
|
237
|
+
restoreData.target.z += particle.velocity.z;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
174
240
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { loadExternalRepulseInteraction } from "./index.js";
|
|
2
|
+
const globalObject = globalThis;
|
|
3
|
+
globalObject.__tsParticlesInternals = globalObject.__tsParticlesInternals ?? {};
|
|
4
|
+
globalObject.loadExternalRepulseInteraction = loadExternalRepulseInteraction;
|
|
5
|
+
export * from "./index.js";
|
package/browser/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
|
|
2
|
+
import { Repulser } from "./Repulser.js";
|
|
1
3
|
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.
|
|
3
|
-
await engine.pluginManager.register(
|
|
4
|
-
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
|
|
4
|
+
engine.checkVersion("4.0.1");
|
|
5
|
+
await engine.pluginManager.register((e) => {
|
|
5
6
|
ensureInteractivityPluginLoaded(e);
|
|
6
7
|
const pluginManager = e.pluginManager;
|
|
7
|
-
pluginManager.addInteractor?.("externalRepulse",
|
|
8
|
-
|
|
9
|
-
return new Repulser(pluginManager, container);
|
|
8
|
+
pluginManager.addInteractor?.("externalRepulse", container => {
|
|
9
|
+
return Promise.resolve(new Repulser(pluginManager, container));
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
+
engine.checkVersion("4.0.1");
|
|
3
|
+
await engine.pluginManager.register(async (e) => {
|
|
4
|
+
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
|
|
5
|
+
ensureInteractivityPluginLoaded(e);
|
|
6
|
+
const pluginManager = e.pluginManager;
|
|
7
|
+
pluginManager.addInteractor?.("externalRepulse", async (container) => {
|
|
8
|
+
const { Repulser } = await import("./Repulser.js");
|
|
9
|
+
return new Repulser(pluginManager, container);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export * from "./Options/Classes/RepulseBase.js";
|
|
14
|
+
export * from "./Options/Classes/RepulseDiv.js";
|
|
15
|
+
export * from "./Options/Classes/Repulse.js";
|
|
@@ -5,6 +5,7 @@ export class RepulseBase {
|
|
|
5
5
|
easing;
|
|
6
6
|
factor;
|
|
7
7
|
maxSpeed;
|
|
8
|
+
restore;
|
|
8
9
|
speed;
|
|
9
10
|
constructor() {
|
|
10
11
|
this.distance = 200;
|
|
@@ -13,6 +14,12 @@ export class RepulseBase {
|
|
|
13
14
|
this.speed = 1;
|
|
14
15
|
this.maxSpeed = 50;
|
|
15
16
|
this.easing = EasingType.easeOutQuad;
|
|
17
|
+
this.restore = {
|
|
18
|
+
enable: false,
|
|
19
|
+
delay: 0,
|
|
20
|
+
speed: 0.08,
|
|
21
|
+
follow: true,
|
|
22
|
+
};
|
|
16
23
|
}
|
|
17
24
|
load(data) {
|
|
18
25
|
if (isNull(data)) {
|
|
@@ -36,5 +43,11 @@ export class RepulseBase {
|
|
|
36
43
|
if (data.maxSpeed !== undefined) {
|
|
37
44
|
this.maxSpeed = data.maxSpeed;
|
|
38
45
|
}
|
|
46
|
+
if (data.restore !== undefined) {
|
|
47
|
+
this.restore.enable = data.restore.enable ?? this.restore.enable;
|
|
48
|
+
this.restore.delay = data.restore.delay ?? this.restore.delay;
|
|
49
|
+
this.restore.speed = data.restore.speed ?? this.restore.speed;
|
|
50
|
+
this.restore.follow = data.restore.follow ?? this.restore.follow;
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
53
|
}
|
package/cjs/Repulser.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { Circle, Rectangle, Vector, clamp, getDistances, half, isInArray, millisecondsToSeconds, safeDocument, } from "@tsparticles/engine";
|
|
2
2
|
import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
|
|
3
3
|
import { Repulse } from "./Options/Classes/Repulse.js";
|
|
4
|
-
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, minRestoreSpeed = 0.001, maxRestoreSpeed = 1, restoreEpsilon = 0.5;
|
|
5
5
|
export class Repulser extends ExternalInteractorBase {
|
|
6
6
|
handleClickMode;
|
|
7
7
|
_clickVec;
|
|
8
|
+
_interactedThisFrame;
|
|
8
9
|
_maxDistance;
|
|
9
10
|
_normVec;
|
|
10
11
|
_pluginManager;
|
|
12
|
+
_restoreData;
|
|
11
13
|
constructor(pluginManager, container) {
|
|
12
14
|
super(container);
|
|
13
15
|
this._pluginManager = pluginManager;
|
|
14
16
|
this._maxDistance = 0;
|
|
15
17
|
this._normVec = Vector.origin;
|
|
18
|
+
this._interactedThisFrame = new Set();
|
|
16
19
|
this._clickVec = Vector.origin;
|
|
20
|
+
this._restoreData = new Map();
|
|
17
21
|
container.repulse ??= { particles: [] };
|
|
18
22
|
this.handleClickMode = (mode, interactivityData) => {
|
|
19
23
|
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
|
|
@@ -54,6 +58,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
54
58
|
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
|
|
55
59
|
}
|
|
56
60
|
interact(interactivityData) {
|
|
61
|
+
this._interactedThisFrame.clear();
|
|
57
62
|
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
|
|
58
63
|
if (!events) {
|
|
59
64
|
return;
|
|
@@ -70,6 +75,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
70
75
|
this._singleSelectorRepulse(interactivityData, selector, div);
|
|
71
76
|
});
|
|
72
77
|
}
|
|
78
|
+
this._restoreParticles();
|
|
73
79
|
}
|
|
74
80
|
isEnabled(interactivityData, particle) {
|
|
75
81
|
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
|
|
@@ -117,6 +123,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
117
123
|
for (const particle of query) {
|
|
118
124
|
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
119
125
|
if (d <= repulseRadius) {
|
|
126
|
+
this._trackInteractedParticle(particle);
|
|
120
127
|
repulse.particles.push(particle);
|
|
121
128
|
this._clickVec.x = dx;
|
|
122
129
|
this._clickVec.y = dy;
|
|
@@ -149,9 +156,46 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
149
156
|
const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
|
|
150
157
|
this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
|
|
151
158
|
this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
|
|
159
|
+
this._trackInteractedParticle(particle);
|
|
152
160
|
particle.position.addTo(this._normVec);
|
|
153
161
|
}
|
|
154
162
|
};
|
|
163
|
+
_restoreParticles() {
|
|
164
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
165
|
+
if (!restore?.enable || !this._restoreData.size) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds, restoreSpeed = Math.max(minRestoreSpeed, Math.min(maxRestoreSpeed, restore.speed));
|
|
169
|
+
for (const [particle, restoreData] of this._restoreData) {
|
|
170
|
+
if (this._interactedThisFrame.has(particle)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (particle.destroyed) {
|
|
174
|
+
this._restoreData.delete(particle);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const target = restoreData.target;
|
|
178
|
+
if (now - restoreData.lastInteractionTime < restoreDelay) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (restore.follow && particle.options.move.enable) {
|
|
182
|
+
target.x += particle.velocity.x;
|
|
183
|
+
target.y += particle.velocity.y;
|
|
184
|
+
target.z += particle.velocity.z;
|
|
185
|
+
}
|
|
186
|
+
const dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
|
|
187
|
+
particle.position.x += dx * restoreSpeed;
|
|
188
|
+
particle.position.y += dy * restoreSpeed;
|
|
189
|
+
particle.position.z += dz * restoreSpeed;
|
|
190
|
+
if (Math.abs(dx) <= restoreEpsilon && Math.abs(dy) <= restoreEpsilon) {
|
|
191
|
+
particle.position.x = target.x;
|
|
192
|
+
particle.position.y = target.y;
|
|
193
|
+
particle.position.z = target.z;
|
|
194
|
+
this._restoreData.delete(particle);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
155
199
|
_singleSelectorRepulse = (interactivityData, selector, div) => {
|
|
156
200
|
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
157
201
|
if (!repulse) {
|
|
@@ -171,4 +215,26 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
171
215
|
this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
|
|
172
216
|
});
|
|
173
217
|
};
|
|
218
|
+
_trackInteractedParticle(particle) {
|
|
219
|
+
this._interactedThisFrame.add(particle);
|
|
220
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
221
|
+
if (!restore?.enable) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
let restoreData = this._restoreData.get(particle);
|
|
226
|
+
if (!restoreData) {
|
|
227
|
+
restoreData = {
|
|
228
|
+
target: particle.position.copy(),
|
|
229
|
+
lastInteractionTime: now,
|
|
230
|
+
};
|
|
231
|
+
this._restoreData.set(particle, restoreData);
|
|
232
|
+
}
|
|
233
|
+
restoreData.lastInteractionTime = now;
|
|
234
|
+
if (restore.follow && particle.options.move.enable) {
|
|
235
|
+
restoreData.target.x += particle.velocity.x;
|
|
236
|
+
restoreData.target.y += particle.velocity.y;
|
|
237
|
+
restoreData.target.z += particle.velocity.z;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
174
240
|
}
|
package/cjs/browser.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { loadExternalRepulseInteraction } from "./index.js";
|
|
2
|
+
const globalObject = globalThis;
|
|
3
|
+
globalObject.__tsParticlesInternals = globalObject.__tsParticlesInternals ?? {};
|
|
4
|
+
globalObject.loadExternalRepulseInteraction = loadExternalRepulseInteraction;
|
|
5
|
+
export * from "./index.js";
|
package/cjs/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
|
|
2
|
+
import { Repulser } from "./Repulser.js";
|
|
1
3
|
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.
|
|
3
|
-
await engine.pluginManager.register(
|
|
4
|
-
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
|
|
4
|
+
engine.checkVersion("4.0.1");
|
|
5
|
+
await engine.pluginManager.register((e) => {
|
|
5
6
|
ensureInteractivityPluginLoaded(e);
|
|
6
7
|
const pluginManager = e.pluginManager;
|
|
7
|
-
pluginManager.addInteractor?.("externalRepulse",
|
|
8
|
-
|
|
9
|
-
return new Repulser(pluginManager, container);
|
|
8
|
+
pluginManager.addInteractor?.("externalRepulse", container => {
|
|
9
|
+
return Promise.resolve(new Repulser(pluginManager, container));
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
+
engine.checkVersion("4.0.1");
|
|
3
|
+
await engine.pluginManager.register(async (e) => {
|
|
4
|
+
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
|
|
5
|
+
ensureInteractivityPluginLoaded(e);
|
|
6
|
+
const pluginManager = e.pluginManager;
|
|
7
|
+
pluginManager.addInteractor?.("externalRepulse", async (container) => {
|
|
8
|
+
const { Repulser } = await import("./Repulser.js");
|
|
9
|
+
return new Repulser(pluginManager, container);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export * from "./Options/Classes/RepulseBase.js";
|
|
14
|
+
export * from "./Options/Classes/RepulseDiv.js";
|
|
15
|
+
export * from "./Options/Classes/Repulse.js";
|
|
@@ -5,6 +5,7 @@ export class RepulseBase {
|
|
|
5
5
|
easing;
|
|
6
6
|
factor;
|
|
7
7
|
maxSpeed;
|
|
8
|
+
restore;
|
|
8
9
|
speed;
|
|
9
10
|
constructor() {
|
|
10
11
|
this.distance = 200;
|
|
@@ -13,6 +14,12 @@ export class RepulseBase {
|
|
|
13
14
|
this.speed = 1;
|
|
14
15
|
this.maxSpeed = 50;
|
|
15
16
|
this.easing = EasingType.easeOutQuad;
|
|
17
|
+
this.restore = {
|
|
18
|
+
enable: false,
|
|
19
|
+
delay: 0,
|
|
20
|
+
speed: 0.08,
|
|
21
|
+
follow: true,
|
|
22
|
+
};
|
|
16
23
|
}
|
|
17
24
|
load(data) {
|
|
18
25
|
if (isNull(data)) {
|
|
@@ -36,5 +43,11 @@ export class RepulseBase {
|
|
|
36
43
|
if (data.maxSpeed !== undefined) {
|
|
37
44
|
this.maxSpeed = data.maxSpeed;
|
|
38
45
|
}
|
|
46
|
+
if (data.restore !== undefined) {
|
|
47
|
+
this.restore.enable = data.restore.enable ?? this.restore.enable;
|
|
48
|
+
this.restore.delay = data.restore.delay ?? this.restore.delay;
|
|
49
|
+
this.restore.speed = data.restore.speed ?? this.restore.speed;
|
|
50
|
+
this.restore.follow = data.restore.follow ?? this.restore.follow;
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
53
|
}
|
package/esm/Repulser.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { Circle, Rectangle, Vector, clamp, getDistances, half, isInArray, millisecondsToSeconds, safeDocument, } from "@tsparticles/engine";
|
|
2
2
|
import { DivType, ExternalInteractorBase, divMode, divModeExecute, isDivModeEnabled, mouseMoveEvent, } from "@tsparticles/plugin-interactivity";
|
|
3
3
|
import { Repulse } from "./Options/Classes/Repulse.js";
|
|
4
|
-
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, minRestoreSpeed = 0.001, maxRestoreSpeed = 1, restoreEpsilon = 0.5;
|
|
5
5
|
export class Repulser extends ExternalInteractorBase {
|
|
6
6
|
handleClickMode;
|
|
7
7
|
_clickVec;
|
|
8
|
+
_interactedThisFrame;
|
|
8
9
|
_maxDistance;
|
|
9
10
|
_normVec;
|
|
10
11
|
_pluginManager;
|
|
12
|
+
_restoreData;
|
|
11
13
|
constructor(pluginManager, container) {
|
|
12
14
|
super(container);
|
|
13
15
|
this._pluginManager = pluginManager;
|
|
14
16
|
this._maxDistance = 0;
|
|
15
17
|
this._normVec = Vector.origin;
|
|
18
|
+
this._interactedThisFrame = new Set();
|
|
16
19
|
this._clickVec = Vector.origin;
|
|
20
|
+
this._restoreData = new Map();
|
|
17
21
|
container.repulse ??= { particles: [] };
|
|
18
22
|
this.handleClickMode = (mode, interactivityData) => {
|
|
19
23
|
const options = this.container.actualOptions, repulseOpts = options.interactivity?.modes.repulse;
|
|
@@ -54,6 +58,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
54
58
|
container.retina.repulseModeDistance = repulse.distance * container.retina.pixelRatio;
|
|
55
59
|
}
|
|
56
60
|
interact(interactivityData) {
|
|
61
|
+
this._interactedThisFrame.clear();
|
|
57
62
|
const container = this.container, options = container.actualOptions, mouseMoveStatus = interactivityData.status === mouseMoveEvent, events = options.interactivity?.events;
|
|
58
63
|
if (!events) {
|
|
59
64
|
return;
|
|
@@ -70,6 +75,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
70
75
|
this._singleSelectorRepulse(interactivityData, selector, div);
|
|
71
76
|
});
|
|
72
77
|
}
|
|
78
|
+
this._restoreParticles();
|
|
73
79
|
}
|
|
74
80
|
isEnabled(interactivityData, particle) {
|
|
75
81
|
const container = this.container, options = container.actualOptions, mouse = interactivityData.mouse, events = (particle?.interactivity ?? options.interactivity)?.events;
|
|
@@ -117,6 +123,7 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
117
123
|
for (const particle of query) {
|
|
118
124
|
const { dx, dy, distance } = getDistances(mouseClickPos, particle.position), d = distance ** squarePower, velocity = repulseOptions.speed, force = (-repulseRadius * velocity) / d;
|
|
119
125
|
if (d <= repulseRadius) {
|
|
126
|
+
this._trackInteractedParticle(particle);
|
|
120
127
|
repulse.particles.push(particle);
|
|
121
128
|
this._clickVec.x = dx;
|
|
122
129
|
this._clickVec.y = dy;
|
|
@@ -149,9 +156,46 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
149
156
|
const { dx, dy, distance } = getDistances(particle.position, position), repulseFactor = clamp(easingFunc(easingOffset - distance / repulseRadius) * velocity, minSpeed, maxSpeed);
|
|
150
157
|
this._normVec.x = !distance ? velocity : (dx / distance) * repulseFactor;
|
|
151
158
|
this._normVec.y = !distance ? velocity : (dy / distance) * repulseFactor;
|
|
159
|
+
this._trackInteractedParticle(particle);
|
|
152
160
|
particle.position.addTo(this._normVec);
|
|
153
161
|
}
|
|
154
162
|
};
|
|
163
|
+
_restoreParticles() {
|
|
164
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
165
|
+
if (!restore?.enable || !this._restoreData.size) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const now = Date.now(), restoreDelay = restore.delay * millisecondsToSeconds, restoreSpeed = Math.max(minRestoreSpeed, Math.min(maxRestoreSpeed, restore.speed));
|
|
169
|
+
for (const [particle, restoreData] of this._restoreData) {
|
|
170
|
+
if (this._interactedThisFrame.has(particle)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (particle.destroyed) {
|
|
174
|
+
this._restoreData.delete(particle);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const target = restoreData.target;
|
|
178
|
+
if (now - restoreData.lastInteractionTime < restoreDelay) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (restore.follow && particle.options.move.enable) {
|
|
182
|
+
target.x += particle.velocity.x;
|
|
183
|
+
target.y += particle.velocity.y;
|
|
184
|
+
target.z += particle.velocity.z;
|
|
185
|
+
}
|
|
186
|
+
const dx = target.x - particle.position.x, dy = target.y - particle.position.y, dz = target.z - particle.position.z;
|
|
187
|
+
particle.position.x += dx * restoreSpeed;
|
|
188
|
+
particle.position.y += dy * restoreSpeed;
|
|
189
|
+
particle.position.z += dz * restoreSpeed;
|
|
190
|
+
if (Math.abs(dx) <= restoreEpsilon && Math.abs(dy) <= restoreEpsilon) {
|
|
191
|
+
particle.position.x = target.x;
|
|
192
|
+
particle.position.y = target.y;
|
|
193
|
+
particle.position.z = target.z;
|
|
194
|
+
this._restoreData.delete(particle);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
155
199
|
_singleSelectorRepulse = (interactivityData, selector, div) => {
|
|
156
200
|
const container = this.container, repulse = container.actualOptions.interactivity?.modes.repulse;
|
|
157
201
|
if (!repulse) {
|
|
@@ -171,4 +215,26 @@ export class Repulser extends ExternalInteractorBase {
|
|
|
171
215
|
this._processRepulse(interactivityData, pos, repulseRadius, area, divRepulse);
|
|
172
216
|
});
|
|
173
217
|
};
|
|
218
|
+
_trackInteractedParticle(particle) {
|
|
219
|
+
this._interactedThisFrame.add(particle);
|
|
220
|
+
const restore = this.container.actualOptions.interactivity?.modes.repulse?.restore;
|
|
221
|
+
if (!restore?.enable) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
let restoreData = this._restoreData.get(particle);
|
|
226
|
+
if (!restoreData) {
|
|
227
|
+
restoreData = {
|
|
228
|
+
target: particle.position.copy(),
|
|
229
|
+
lastInteractionTime: now,
|
|
230
|
+
};
|
|
231
|
+
this._restoreData.set(particle, restoreData);
|
|
232
|
+
}
|
|
233
|
+
restoreData.lastInteractionTime = now;
|
|
234
|
+
if (restore.follow && particle.options.move.enable) {
|
|
235
|
+
restoreData.target.x += particle.velocity.x;
|
|
236
|
+
restoreData.target.y += particle.velocity.y;
|
|
237
|
+
restoreData.target.z += particle.velocity.z;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
174
240
|
}
|
package/esm/browser.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { loadExternalRepulseInteraction } from "./index.js";
|
|
2
|
+
const globalObject = globalThis;
|
|
3
|
+
globalObject.__tsParticlesInternals = globalObject.__tsParticlesInternals ?? {};
|
|
4
|
+
globalObject.loadExternalRepulseInteraction = loadExternalRepulseInteraction;
|
|
5
|
+
export * from "./index.js";
|
package/esm/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { ensureInteractivityPluginLoaded } from "@tsparticles/plugin-interactivity";
|
|
2
|
+
import { Repulser } from "./Repulser.js";
|
|
1
3
|
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
-
engine.checkVersion("4.0.
|
|
3
|
-
await engine.pluginManager.register(
|
|
4
|
-
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity");
|
|
4
|
+
engine.checkVersion("4.0.1");
|
|
5
|
+
await engine.pluginManager.register((e) => {
|
|
5
6
|
ensureInteractivityPluginLoaded(e);
|
|
6
7
|
const pluginManager = e.pluginManager;
|
|
7
|
-
pluginManager.addInteractor?.("externalRepulse",
|
|
8
|
-
|
|
9
|
-
return new Repulser(pluginManager, container);
|
|
8
|
+
pluginManager.addInteractor?.("externalRepulse", container => {
|
|
9
|
+
return Promise.resolve(new Repulser(pluginManager, container));
|
|
10
10
|
});
|
|
11
11
|
});
|
|
12
12
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function loadExternalRepulseInteraction(engine) {
|
|
2
|
+
engine.checkVersion("4.0.1");
|
|
3
|
+
await engine.pluginManager.register(async (e) => {
|
|
4
|
+
const { ensureInteractivityPluginLoaded } = await import("@tsparticles/plugin-interactivity/lazy");
|
|
5
|
+
ensureInteractivityPluginLoaded(e);
|
|
6
|
+
const pluginManager = e.pluginManager;
|
|
7
|
+
pluginManager.addInteractor?.("externalRepulse", async (container) => {
|
|
8
|
+
const { Repulser } = await import("./Repulser.js");
|
|
9
|
+
return new Repulser(pluginManager, container);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export * from "./Options/Classes/RepulseBase.js";
|
|
14
|
+
export * from "./Options/Classes/RepulseDiv.js";
|
|
15
|
+
export * from "./Options/Classes/Repulse.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsparticles/interaction-external-repulse",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "tsParticles repulse external interaction",
|
|
5
5
|
"homepage": "https://particles.js.org",
|
|
6
6
|
"repository": {
|
|
@@ -83,6 +83,13 @@
|
|
|
83
83
|
"require": "./cjs/index.js",
|
|
84
84
|
"default": "./esm/index.js"
|
|
85
85
|
},
|
|
86
|
+
"./lazy": {
|
|
87
|
+
"types": "./types/index.lazy.d.ts",
|
|
88
|
+
"browser": "./browser/index.lazy.js",
|
|
89
|
+
"import": "./esm/index.lazy.js",
|
|
90
|
+
"require": "./cjs/index.lazy.js",
|
|
91
|
+
"default": "./esm/index.lazy.js"
|
|
92
|
+
},
|
|
86
93
|
"./package.json": "./package.json"
|
|
87
94
|
},
|
|
88
95
|
"peerDepdendencies": {
|
|
@@ -94,7 +101,7 @@
|
|
|
94
101
|
},
|
|
95
102
|
"type": "module",
|
|
96
103
|
"peerDependencies": {
|
|
97
|
-
"@tsparticles/engine": "4.0.
|
|
98
|
-
"@tsparticles/plugin-interactivity": "4.0.
|
|
104
|
+
"@tsparticles/engine": "4.0.1",
|
|
105
|
+
"@tsparticles/plugin-interactivity": "4.0.1"
|
|
99
106
|
}
|
|
100
107
|
}
|