2023-02-17 11:06:01 +01:00

295 lines
13 KiB
JavaScript

import { Circle, ExternalInteractorBase, Rectangle, clamp, colorMix, divMode, divModeExecute, getDistance, getRangeMax, isDivModeEnabled, isInArray, itemFromSingleOrMultiple, mouseLeaveEvent, mouseMoveEvent, rangeColorToHsl, rgbToHsl, } from "tsparticles-engine";
import { Bubble } from "./Options/Classes/Bubble";
function calculateBubbleValue(particleValue, modeValue, optionsValue, ratio) {
if (modeValue >= optionsValue) {
const value = particleValue + (modeValue - optionsValue) * ratio;
return clamp(value, particleValue, modeValue);
}
else if (modeValue < optionsValue) {
const value = particleValue - (optionsValue - modeValue) * ratio;
return clamp(value, modeValue, particleValue);
}
}
export class Bubbler extends ExternalInteractorBase {
constructor(container) {
super(container);
if (!container.bubble) {
container.bubble = {};
}
this.handleClickMode = (mode) => {
if (mode !== "bubble") {
return;
}
if (!container.bubble) {
container.bubble = {};
}
container.bubble.clicking = true;
};
}
clear(particle, delta, force) {
if (particle.bubble.inRange && !force) {
return;
}
delete particle.bubble.div;
delete particle.bubble.opacity;
delete particle.bubble.radius;
delete particle.bubble.color;
}
init() {
const container = this.container, bubble = container.actualOptions.interactivity.modes.bubble;
if (!bubble) {
return;
}
container.retina.bubbleModeDistance = bubble.distance * container.retina.pixelRatio;
if (bubble.size !== undefined) {
container.retina.bubbleModeSize = bubble.size * container.retina.pixelRatio;
}
}
async interact(delta) {
const options = this.container.actualOptions, events = options.interactivity.events, onHover = events.onHover, onClick = events.onClick, hoverEnabled = onHover.enable, hoverMode = onHover.mode, clickEnabled = onClick.enable, clickMode = onClick.mode, divs = events.onDiv;
if (hoverEnabled && isInArray("bubble", hoverMode)) {
this.hoverBubble(delta);
}
else if (clickEnabled && isInArray("bubble", clickMode)) {
this.clickBubble(delta);
}
else {
divModeExecute("bubble", divs, (selector, div) => this.singleSelectorHover(delta, selector, div));
}
}
isEnabled(particle) {
var _a;
const container = this.container, options = container.actualOptions, mouse = container.interactivity.mouse, events = ((_a = particle === null || particle === void 0 ? void 0 : particle.interactivity) !== null && _a !== void 0 ? _a : options.interactivity).events, divs = events.onDiv, divBubble = isDivModeEnabled("bubble", divs);
if (!(divBubble || (events.onHover.enable && mouse.position) || (events.onClick.enable && mouse.clickPosition))) {
return false;
}
const hoverMode = events.onHover.mode;
const clickMode = events.onClick.mode;
return isInArray("bubble", hoverMode) || isInArray("bubble", clickMode) || divBubble;
}
loadModeOptions(options, ...sources) {
if (!options.bubble) {
options.bubble = new Bubble();
}
for (const source of sources) {
options.bubble.load(source === null || source === void 0 ? void 0 : source.bubble);
}
}
reset(particle) {
particle.bubble.inRange = false;
}
clickBubble(delta) {
var _a, _b;
const container = this.container, options = container.actualOptions, mouseClickPos = container.interactivity.mouse.clickPosition, bubble = options.interactivity.modes.bubble;
if (!bubble || !mouseClickPos) {
return;
}
if (!container.bubble) {
container.bubble = {};
}
const distance = container.retina.bubbleModeDistance;
if (!distance || distance < 0) {
return;
}
const query = container.particles.quadTree.queryCircle(mouseClickPos, distance, (p) => this.isEnabled(p));
for (const particle of query) {
if (!container.bubble.clicking) {
continue;
}
particle.bubble.inRange = !container.bubble.durationEnd;
const pos = particle.getPosition(), distMouse = getDistance(pos, mouseClickPos), timeSpent = (new Date().getTime() - (container.interactivity.mouse.clickTime || 0)) / 1000;
if (timeSpent > bubble.duration) {
container.bubble.durationEnd = true;
}
if (timeSpent > bubble.duration * 2) {
container.bubble.clicking = false;
container.bubble.durationEnd = false;
}
const sizeData = {
bubbleObj: {
optValue: container.retina.bubbleModeSize,
value: particle.bubble.radius,
},
particlesObj: {
optValue: getRangeMax(particle.options.size.value) * container.retina.pixelRatio,
value: particle.size.value,
},
type: "size",
};
this.process(particle, distMouse, timeSpent, sizeData);
const opacityData = {
bubbleObj: {
optValue: bubble.opacity,
value: particle.bubble.opacity,
},
particlesObj: {
optValue: getRangeMax(particle.options.opacity.value),
value: (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1,
},
type: "opacity",
};
this.process(particle, distMouse, timeSpent, opacityData);
if (!container.bubble.durationEnd) {
if (distMouse <= distance) {
this.hoverBubbleColor(particle, distMouse);
}
else {
delete particle.bubble.color;
}
}
else {
delete particle.bubble.color;
}
}
}
hoverBubble(delta) {
const container = this.container, mousePos = container.interactivity.mouse.position, distance = container.retina.bubbleModeDistance;
if (!distance || distance < 0 || mousePos === undefined) {
return;
}
const query = container.particles.quadTree.queryCircle(mousePos, distance, (p) => this.isEnabled(p));
for (const particle of query) {
particle.bubble.inRange = true;
const pos = particle.getPosition(), pointDistance = getDistance(pos, mousePos), ratio = 1 - pointDistance / distance;
if (pointDistance <= distance) {
if (ratio >= 0 && container.interactivity.status === mouseMoveEvent) {
this.hoverBubbleSize(particle, ratio);
this.hoverBubbleOpacity(particle, ratio);
this.hoverBubbleColor(particle, ratio);
}
}
else {
this.reset(particle);
}
if (container.interactivity.status === mouseLeaveEvent) {
this.reset(particle);
}
}
}
hoverBubbleColor(particle, ratio, divBubble) {
const options = this.container.actualOptions;
const bubbleOptions = divBubble !== null && divBubble !== void 0 ? divBubble : options.interactivity.modes.bubble;
if (!bubbleOptions) {
return;
}
if (!particle.bubble.finalColor) {
const modeColor = bubbleOptions.color;
if (!modeColor) {
return;
}
const bubbleColor = itemFromSingleOrMultiple(modeColor);
particle.bubble.finalColor = rangeColorToHsl(bubbleColor);
}
if (!particle.bubble.finalColor) {
return;
}
if (bubbleOptions.mix) {
particle.bubble.color = undefined;
const pColor = particle.getFillColor();
particle.bubble.color = pColor
? rgbToHsl(colorMix(pColor, particle.bubble.finalColor, 1 - ratio, ratio))
: particle.bubble.finalColor;
}
else {
particle.bubble.color = particle.bubble.finalColor;
}
}
hoverBubbleOpacity(particle, ratio, divBubble) {
var _a, _b, _c, _d;
const container = this.container, options = container.actualOptions, modeOpacity = (_a = divBubble === null || divBubble === void 0 ? void 0 : divBubble.opacity) !== null && _a !== void 0 ? _a : (_b = options.interactivity.modes.bubble) === null || _b === void 0 ? void 0 : _b.opacity;
if (!modeOpacity) {
return;
}
const optOpacity = particle.options.opacity.value;
const pOpacity = (_d = (_c = particle.opacity) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : 1;
const opacity = calculateBubbleValue(pOpacity, modeOpacity, getRangeMax(optOpacity), ratio);
if (opacity !== undefined) {
particle.bubble.opacity = opacity;
}
}
hoverBubbleSize(particle, ratio, divBubble) {
const container = this.container, modeSize = (divBubble === null || divBubble === void 0 ? void 0 : divBubble.size) ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize;
if (modeSize === undefined) {
return;
}
const optSize = getRangeMax(particle.options.size.value) * container.retina.pixelRatio;
const pSize = particle.size.value;
const size = calculateBubbleValue(pSize, modeSize, optSize, ratio);
if (size !== undefined) {
particle.bubble.radius = size;
}
}
process(particle, distMouse, timeSpent, data) {
const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubble = options.interactivity.modes.bubble;
if (!bubble || bubbleParam === undefined) {
return;
}
const bubbleDuration = bubble.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value || 0, type = data.type;
if (!bubbleDistance || bubbleDistance < 0 || bubbleParam === particlesParam) {
return;
}
if (!container.bubble) {
container.bubble = {};
}
if (!container.bubble.durationEnd) {
if (distMouse <= bubbleDistance) {
const obj = pObjBubble !== null && pObjBubble !== void 0 ? pObjBubble : pObj;
if (obj !== bubbleParam) {
const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration;
if (type === "size") {
particle.bubble.radius = value;
}
if (type === "opacity") {
particle.bubble.opacity = value;
}
}
}
else {
if (type === "size") {
delete particle.bubble.radius;
}
if (type === "opacity") {
delete particle.bubble.opacity;
}
}
}
else if (pObjBubble) {
if (type === "size") {
delete particle.bubble.radius;
}
if (type === "opacity") {
delete particle.bubble.opacity;
}
}
}
singleSelectorHover(delta, selector, div) {
const container = this.container, selectors = document.querySelectorAll(selector), bubble = container.actualOptions.interactivity.modes.bubble;
if (!bubble || !selectors.length) {
return;
}
selectors.forEach((item) => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth / 2) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight / 2) * pxRatio,
}, repulseRadius = (elem.offsetWidth / 2) * pxRatio, area = div.type === "circle"
? new Circle(pos.x, pos.y, repulseRadius)
: new Rectangle(elem.offsetLeft * pxRatio, elem.offsetTop * pxRatio, elem.offsetWidth * pxRatio, elem.offsetHeight * pxRatio), query = container.particles.quadTree.query(area, (p) => this.isEnabled(p));
for (const particle of query) {
if (!area.contains(particle.getPosition())) {
continue;
}
particle.bubble.inRange = true;
const divs = bubble.divs;
const divBubble = divMode(divs, elem);
if (!particle.bubble.div || particle.bubble.div !== elem) {
this.clear(particle, delta, true);
particle.bubble.div = elem;
}
this.hoverBubbleSize(particle, 1, divBubble);
this.hoverBubbleOpacity(particle, 1, divBubble);
this.hoverBubbleColor(particle, 1, divBubble);
}
});
}
}