@tsparticles/interaction-external-bounce
Version:
tsParticles bounce external interaction
123 lines (122 loc) • 4.95 kB
JavaScript
import { Circle, Rectangle, Vector, calculateBounds, circleBounce, circleBounceDataFromParticle, double, getRangeValue, half, minRadius, minVelocity, safeDocument, squareExp, } from "@tsparticles/engine";
import { DivType, divModeExecute } from "@tsparticles/plugin-interactivity";
const halfPI = Math.PI * half, toleranceFactor = 10;
function processBounce(container, position, radius, area, enabledCb) {
const query = container.particles.grid.query(area, enabledCb);
for (const particle of query) {
if (area instanceof Circle) {
circleBounce(circleBounceDataFromParticle(particle), {
position,
radius,
mass: radius ** squareExp * halfPI,
velocity: Vector.origin,
factor: Vector.origin,
});
}
else if (area instanceof Rectangle) {
rectBounce(particle, calculateBounds(position, radius));
}
}
}
function singleSelectorBounce(container, selector, div, bounceCb) {
const query = safeDocument().querySelectorAll(selector);
if (!query.length) {
return;
}
query.forEach(item => {
const elem = item, pxRatio = container.retina.pixelRatio, pos = {
x: (elem.offsetLeft + elem.offsetWidth * half) * pxRatio,
y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio,
}, radius = elem.offsetWidth * half * pxRatio, tolerance = toleranceFactor * pxRatio, area = div.type === DivType.circle
? new Circle(pos.x, pos.y, radius + tolerance)
: new Rectangle(elem.offsetLeft * pxRatio - tolerance, elem.offsetTop * pxRatio - tolerance, elem.offsetWidth * pxRatio + tolerance * double, elem.offsetHeight * pxRatio + tolerance * double);
bounceCb(pos, radius, area);
});
}
export function divBounce(container, divs, bounceMode, enabledCb) {
divModeExecute(bounceMode, divs, (selector, div) => {
singleSelectorBounce(container, selector, div, (pos, radius, area) => {
processBounce(container, pos, radius, area, enabledCb);
});
});
}
export function mouseBounce(container, interactivityData, enabledCb) {
const pxRatio = container.retina.pixelRatio, tolerance = toleranceFactor * pxRatio, mousePos = interactivityData.mouse.position, radius = container.retina.bounceModeDistance;
if (!radius || radius < minRadius || !mousePos) {
return;
}
processBounce(container, mousePos, radius, new Circle(mousePos.x, mousePos.y, radius + tolerance), enabledCb);
}
function rectSideBounce(data) {
const res = { bounced: false }, { pSide, pOtherSide, rectSide, rectOtherSide, velocity, factor } = data;
if (pOtherSide.min < rectOtherSide.min ||
pOtherSide.min > rectOtherSide.max ||
pOtherSide.max < rectOtherSide.min ||
pOtherSide.max > rectOtherSide.max) {
return res;
}
if ((pSide.max >= rectSide.min && pSide.max <= (rectSide.max + rectSide.min) * half && velocity > minVelocity) ||
(pSide.min <= rectSide.max && pSide.min > (rectSide.max + rectSide.min) * half && velocity < minVelocity)) {
res.velocity = velocity * -factor;
res.bounced = true;
}
return res;
}
export function rectBounce(particle, divBounds) {
const pPos = particle.getPosition(), size = particle.getRadius(), bounds = calculateBounds(pPos, size), bounceOptions = particle.options.bounce, resH = rectSideBounce({
pSide: {
min: bounds.left,
max: bounds.right,
},
pOtherSide: {
min: bounds.top,
max: bounds.bottom,
},
rectSide: {
min: divBounds.left,
max: divBounds.right,
},
rectOtherSide: {
min: divBounds.top,
max: divBounds.bottom,
},
velocity: particle.velocity.x,
factor: getRangeValue(bounceOptions.horizontal.value),
});
if (resH.bounced) {
if (resH.velocity !== undefined) {
particle.velocity.x = resH.velocity;
}
if (resH.position !== undefined) {
particle.position.x = resH.position;
}
}
const resV = rectSideBounce({
pSide: {
min: bounds.top,
max: bounds.bottom,
},
pOtherSide: {
min: bounds.left,
max: bounds.right,
},
rectSide: {
min: divBounds.top,
max: divBounds.bottom,
},
rectOtherSide: {
min: divBounds.left,
max: divBounds.right,
},
velocity: particle.velocity.y,
factor: getRangeValue(bounceOptions.vertical.value),
});
if (resV.bounced) {
if (resV.velocity !== undefined) {
particle.velocity.y = resV.velocity;
}
if (resV.position !== undefined) {
particle.position.y = resV.position;
}
}
}