UNPKG

tsparticles

Version:

Porting of the abandoned Vincent Garreau's particles.js, converted in TypeScript. Added many new cool features and various bug fixes.

148 lines (147 loc) 6.84 kB
import { ClickMode } from "../../Enums/Modes/ClickMode"; import { HoverMode } from "../../Enums/Modes/HoverMode"; import { OutMode } from "../../Enums/OutMode"; import { Utils } from "../Utils/Utils"; import { DivMode } from "../../Enums/Modes/DivMode"; import { Constants } from "../Utils/Constants"; export class Repulser { constructor(container, particle) { this.container = container; this.particle = particle; } repulse() { const container = this.container; const options = container.options; const hoverEnabled = options.interactivity.events.onHover.enable; const clickEnabled = options.interactivity.events.onClick.enable; const mouseMoveStatus = container.interactivity.status === Constants.mouseMoveEvent; const hoverMode = options.interactivity.events.onHover.mode; const clickMode = options.interactivity.events.onClick.mode; const divMode = options.interactivity.events.onDiv.mode; if (mouseMoveStatus && hoverEnabled && Utils.isInArray(HoverMode.repulse, hoverMode)) { this.hoverRepulse(); } else if (clickEnabled && Utils.isInArray(ClickMode.repulse, clickMode)) { this.clickRepulse(); } else if (options.interactivity.events.onDiv.enable && Utils.isInArray(DivMode.repulse, divMode)) { this.divRepulse(); } } divRepulse() { const container = this.container; const options = container.options; const particle = this.particle; const elem = document.getElementById(options.interactivity.events.onDiv.elementId); const pos = { x: (elem.offsetLeft + elem.offsetWidth / 2), y: (elem.offsetTop + elem.offsetHeight / 2), }; let divWidth = elem.offsetWidth / 2; if (container.retina.isRetina) { pos.x *= container.retina.pxRatio; pos.y *= container.retina.pxRatio; divWidth *= container.retina.pxRatio; } const dxDiv = particle.position.x - pos.x; const dyDiv = particle.position.y - pos.y; const distDiv = Math.sqrt(dxDiv * dxDiv + dyDiv * dyDiv); const normVec = { x: dxDiv / distDiv, y: dyDiv / distDiv, }; const repulseRadius = divWidth; const velocity = 100; const repulseFactor = Utils.clamp((-Math.pow(distDiv / repulseRadius, 4) + 1) * velocity, 0, 50); this.particle.position.x += normVec.x * repulseFactor; this.particle.position.y += normVec.y * repulseFactor; } clickRepulse() { const container = this.container; const particle = this.particle; if (!container.repulse.finish) { if (!container.repulse.count) { container.repulse.count = 0; } container.repulse.count++; if (container.repulse.count === container.particles.count) { container.repulse.finish = true; } } if (container.repulse.clicking) { const repulseDistance = container.retina.repulseModeDistance; const repulseRadius = Math.pow(repulseDistance / 6, 3); const mouseClickPos = container.interactivity.mouse.clickPosition || { x: 0, y: 0 }; const dx = mouseClickPos.x - particle.position.x; const dy = mouseClickPos.y - particle.position.y; const d = dx * dx + dy * dy; const force = -repulseRadius / d; if (d <= repulseRadius) { this.processRepulse(dx, dy, force); } } else if (container.repulse.clicking === false) { particle.velocity.horizontal = particle.initialVelocity.horizontal; particle.velocity.vertical = particle.initialVelocity.vertical; } } hoverRepulse() { const container = this.container; const options = container.options; const particle = this.particle; const mousePos = container.interactivity.mouse.position || { x: 0, y: 0 }; const dxMouse = particle.position.x - mousePos.x; const dyMouse = particle.position.y - mousePos.y; const distMouse = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse); const normVec = { x: dxMouse / distMouse, y: dyMouse / distMouse }; const repulseRadius = container.retina.repulseModeDistance; const velocity = 100; const repulseFactor = Utils.clamp((1 - Math.pow(distMouse / repulseRadius, 2)) * velocity, 0, 50); const pos = { x: particle.position.x + normVec.x * repulseFactor, y: particle.position.y + normVec.y * repulseFactor, }; const outMode = options.particles.move.outMode; if (outMode === OutMode.bounce || outMode === OutMode.bounceVertical || outMode === OutMode.bounceHorizontal) { const isInside = { horizontal: pos.x - particle.radius > 0 && pos.x + particle.radius < container.canvas.dimension.width, vertical: pos.y - particle.radius > 0 && pos.y + particle.radius < container.canvas.dimension.height, }; if (outMode === OutMode.bounceVertical || isInside.horizontal) { particle.position.x = pos.x; } if (outMode === OutMode.bounceHorizontal || isInside.vertical) { particle.position.y = pos.y; } } else { particle.position.x = pos.x; particle.position.y = pos.y; } } processRepulse(dx, dy, force) { const container = this.container; const options = container.options; const particle = this.particle; const f = Math.atan2(dy, dx); particle.velocity.horizontal = force * Math.cos(f); particle.velocity.vertical = force * Math.sin(f); const outMode = options.particles.move.outMode; if (outMode === OutMode.bounce || outMode === OutMode.bounceHorizontal || outMode === OutMode.bounceVertical) { const pos = { x: particle.position.x + particle.velocity.horizontal, y: particle.position.y + particle.velocity.vertical, }; if (outMode !== OutMode.bounceVertical) { if (pos.x + particle.radius > container.canvas.dimension.width || pos.x - particle.radius < 0) { particle.velocity.horizontal = -particle.velocity.horizontal; } } if (outMode !== OutMode.bounceHorizontal) { if (pos.y + particle.radius > container.canvas.dimension.height || pos.y - particle.radius < 0) { particle.velocity.vertical = -particle.velocity.vertical; } } } } }