tsparticles
Version:
Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.
72 lines (71 loc) • 3.47 kB
JavaScript
import { Circle, Constants, ExternalInteractorBase, Vector } from "../../../Core";
import { calcEasing, clamp, getDistances, isInArray } from "../../../Utils";
export class Attractor extends ExternalInteractorBase {
constructor(container) {
super(container);
}
isEnabled() {
const container = this.container, options = container.actualOptions, mouse = container.interactivity.mouse, events = options.interactivity.events;
if ((!mouse.position || !events.onHover.enable) && (!mouse.clickPosition || !events.onClick.enable)) {
return false;
}
const hoverMode = events.onHover.mode, clickMode = events.onClick.mode;
return isInArray("attract", hoverMode) || isInArray("attract", clickMode);
}
reset() {
}
async interact() {
const container = this.container, options = container.actualOptions, mouseMoveStatus = container.interactivity.status === Constants.mouseMoveEvent, events = options.interactivity.events, hoverEnabled = events.onHover.enable, hoverMode = events.onHover.mode, clickEnabled = events.onClick.enable, clickMode = events.onClick.mode;
if (mouseMoveStatus && hoverEnabled && isInArray("attract", hoverMode)) {
this.hoverAttract();
}
else if (clickEnabled && isInArray("attract", clickMode)) {
this.clickAttract();
}
}
hoverAttract() {
const container = this.container;
const mousePos = container.interactivity.mouse.position;
if (!mousePos) {
return;
}
const attractRadius = container.retina.attractModeDistance;
this.processAttract(mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius));
}
processAttract(position, attractRadius, area) {
const container = this.container;
const attractOptions = container.actualOptions.interactivity.modes.attract;
const query = container.particles.quadTree.query(area);
for (const particle of query) {
const { dx, dy, distance } = getDistances(particle.position, position);
const velocity = attractOptions.speed * attractOptions.factor;
const attractFactor = clamp(calcEasing(1 - distance / attractRadius, attractOptions.easing) * velocity, 0, attractOptions.maxSpeed);
const normVec = Vector.create(distance === 0 ? velocity : (dx / distance) * attractFactor, distance === 0 ? velocity : (dy / distance) * attractFactor);
particle.position.subFrom(normVec);
}
}
clickAttract() {
const container = this.container;
if (!container.attract.finish) {
if (!container.attract.count) {
container.attract.count = 0;
}
container.attract.count++;
if (container.attract.count === container.particles.count) {
container.attract.finish = true;
}
}
if (container.attract.clicking) {
const mousePos = container.interactivity.mouse.clickPosition;
if (!mousePos) {
return;
}
const attractRadius = container.retina.attractModeDistance;
this.processAttract(mousePos, attractRadius, new Circle(mousePos.x, mousePos.y, attractRadius));
}
else if (container.attract.clicking === false) {
container.attract.particles = [];
}
return;
}
}