UNPKG

@tsparticles/interaction-external-bubble

Version:

tsParticles bubble external interaction

290 lines (289 loc) 15.6 kB
(function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "@tsparticles/engine", "./Options/Classes/Bubble.js", "./Enums.js", "./Utils.js"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Bubbler = void 0; const engine_1 = require("@tsparticles/engine"); const Bubble_js_1 = require("./Options/Classes/Bubble.js"); const Enums_js_1 = require("./Enums.js"); const Utils_js_1 = require("./Utils.js"); const bubbleMode = "bubble", minDistance = 0, defaultClickTime = 0, double = 2, defaultOpacity = 1, ratioOffset = 1, defaultBubbleValue = 0, minRatio = 0, half = 0.5, defaultRatio = 1; class Bubbler extends engine_1.ExternalInteractorBase { constructor(container, engine) { super(container); this._clickBubble = () => { const container = this.container, options = container.actualOptions, mouseClickPos = container.interactivity.mouse.clickPosition, bubbleOptions = options.interactivity.modes.bubble; if (!bubbleOptions || !mouseClickPos) { return; } if (!container.bubble) { container.bubble = {}; } const distance = container.retina.bubbleModeDistance; if (!distance || distance < minDistance) { return; } const query = container.particles.quadTree.queryCircle(mouseClickPos, distance, p => this.isEnabled(p)), { bubble } = container; for (const particle of query) { if (!bubble.clicking) { continue; } particle.bubble.inRange = !bubble.durationEnd; const pos = particle.getPosition(), distMouse = (0, engine_1.getDistance)(pos, mouseClickPos), timeSpent = (new Date().getTime() - (container.interactivity.mouse.clickTime ?? defaultClickTime)) / engine_1.millisecondsToSeconds; if (timeSpent > bubbleOptions.duration) { bubble.durationEnd = true; } if (timeSpent > bubbleOptions.duration * double) { bubble.clicking = false; bubble.durationEnd = false; } const sizeData = { bubbleObj: { optValue: container.retina.bubbleModeSize, value: particle.bubble.radius, }, particlesObj: { optValue: (0, engine_1.getRangeMax)(particle.options.size.value) * container.retina.pixelRatio, value: particle.size.value, }, type: Enums_js_1.ProcessBubbleType.size, }; this._process(particle, distMouse, timeSpent, sizeData); const opacityData = { bubbleObj: { optValue: bubbleOptions.opacity, value: particle.bubble.opacity, }, particlesObj: { optValue: (0, engine_1.getRangeMax)(particle.options.opacity.value), value: particle.opacity?.value ?? defaultOpacity, }, type: Enums_js_1.ProcessBubbleType.opacity, }; this._process(particle, distMouse, timeSpent, opacityData); if (!bubble.durationEnd && distMouse <= distance) { this._hoverBubbleColor(particle, distMouse); } else { delete particle.bubble.color; } } }; this._hoverBubble = () => { const container = this.container, mousePos = container.interactivity.mouse.position, distance = container.retina.bubbleModeDistance; if (!distance || distance < minDistance || !mousePos) { 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 = (0, engine_1.getDistance)(pos, mousePos), ratio = ratioOffset - pointDistance / distance; if (pointDistance <= distance) { if (ratio >= minRatio && container.interactivity.status === engine_1.mouseMoveEvent) { this._hoverBubbleSize(particle, ratio); this._hoverBubbleOpacity(particle, ratio); this._hoverBubbleColor(particle, ratio); } } else { this.reset(particle); } if (container.interactivity.status === engine_1.mouseLeaveEvent) { this.reset(particle); } } }; this._hoverBubbleColor = (particle, ratio, divBubble) => { const options = this.container.actualOptions, bubbleOptions = divBubble ?? options.interactivity.modes.bubble; if (!bubbleOptions) { return; } if (!particle.bubble.finalColor) { const modeColor = bubbleOptions.color; if (!modeColor) { return; } const bubbleColor = (0, engine_1.itemFromSingleOrMultiple)(modeColor); particle.bubble.finalColor = (0, engine_1.rangeColorToHsl)(this._engine, bubbleColor); } if (!particle.bubble.finalColor) { return; } if (bubbleOptions.mix) { particle.bubble.color = undefined; const pColor = particle.getFillColor(); particle.bubble.color = pColor ? (0, engine_1.rgbToHsl)((0, engine_1.colorMix)(pColor, particle.bubble.finalColor, ratioOffset - ratio, ratio)) : particle.bubble.finalColor; } else { particle.bubble.color = particle.bubble.finalColor; } }; this._hoverBubbleOpacity = (particle, ratio, divBubble) => { const container = this.container, options = container.actualOptions, modeOpacity = divBubble?.opacity ?? options.interactivity.modes.bubble?.opacity; if (!modeOpacity) { return; } const optOpacity = particle.options.opacity.value, pOpacity = particle.opacity?.value ?? defaultOpacity, opacity = (0, Utils_js_1.calculateBubbleValue)(pOpacity, modeOpacity, (0, engine_1.getRangeMax)(optOpacity), ratio); if (opacity !== undefined) { particle.bubble.opacity = opacity; } }; this._hoverBubbleSize = (particle, ratio, divBubble) => { const container = this.container, modeSize = divBubble?.size ? divBubble.size * container.retina.pixelRatio : container.retina.bubbleModeSize; if (modeSize === undefined) { return; } const optSize = (0, engine_1.getRangeMax)(particle.options.size.value) * container.retina.pixelRatio, pSize = particle.size.value, size = (0, Utils_js_1.calculateBubbleValue)(pSize, modeSize, optSize, ratio); if (size !== undefined) { particle.bubble.radius = size; } }; this._process = (particle, distMouse, timeSpent, data) => { const container = this.container, bubbleParam = data.bubbleObj.optValue, options = container.actualOptions, bubbleOptions = options.interactivity.modes.bubble; if (!bubbleOptions || bubbleParam === undefined) { return; } const bubbleDuration = bubbleOptions.duration, bubbleDistance = container.retina.bubbleModeDistance, particlesParam = data.particlesObj.optValue, pObjBubble = data.bubbleObj.value, pObj = data.particlesObj.value ?? defaultBubbleValue, type = data.type; if (!bubbleDistance || bubbleDistance < minDistance || bubbleParam === particlesParam) { return; } if (!container.bubble) { container.bubble = {}; } if (container.bubble.durationEnd) { if (pObjBubble) { if (type === Enums_js_1.ProcessBubbleType.size) { delete particle.bubble.radius; } if (type === Enums_js_1.ProcessBubbleType.opacity) { delete particle.bubble.opacity; } } } else { if (distMouse <= bubbleDistance) { const obj = pObjBubble ?? pObj; if (obj !== bubbleParam) { const value = pObj - (timeSpent * (pObj - bubbleParam)) / bubbleDuration; if (type === Enums_js_1.ProcessBubbleType.size) { particle.bubble.radius = value; } if (type === Enums_js_1.ProcessBubbleType.opacity) { particle.bubble.opacity = value; } } } else { if (type === Enums_js_1.ProcessBubbleType.size) { delete particle.bubble.radius; } if (type === Enums_js_1.ProcessBubbleType.opacity) { delete particle.bubble.opacity; } } } }; this._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 * half) * pxRatio, y: (elem.offsetTop + elem.offsetHeight * half) * pxRatio, }, repulseRadius = elem.offsetWidth * half * pxRatio, area = div.type === engine_1.DivType.circle ? new engine_1.Circle(pos.x, pos.y, repulseRadius) : new engine_1.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, divBubble = (0, engine_1.divMode)(divs, elem); if (!particle.bubble.div || particle.bubble.div !== elem) { this.clear(particle, delta, true); particle.bubble.div = elem; } this._hoverBubbleSize(particle, defaultRatio, divBubble); this._hoverBubbleOpacity(particle, defaultRatio, divBubble); this._hoverBubbleColor(particle, defaultRatio, divBubble); } }); }; this._engine = engine; if (!container.bubble) { container.bubble = {}; } this.handleClickMode = (mode) => { if (mode !== bubbleMode) { 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; } } 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 && (0, engine_1.isInArray)(bubbleMode, hoverMode)) { this._hoverBubble(); } else if (clickEnabled && (0, engine_1.isInArray)(bubbleMode, clickMode)) { this._clickBubble(); } else { (0, engine_1.divModeExecute)(bubbleMode, divs, (selector, div) => this._singleSelectorHover(delta, selector, div)); } } isEnabled(particle) { const container = this.container, options = container.actualOptions, mouse = container.interactivity.mouse, events = (particle?.interactivity ?? options.interactivity).events, { onClick, onDiv, onHover } = events, divBubble = (0, engine_1.isDivModeEnabled)(bubbleMode, onDiv); if (!(divBubble || (onHover.enable && !!mouse.position) || (onClick.enable && mouse.clickPosition))) { return false; } return (0, engine_1.isInArray)(bubbleMode, onHover.mode) || (0, engine_1.isInArray)(bubbleMode, onClick.mode) || divBubble; } loadModeOptions(options, ...sources) { if (!options.bubble) { options.bubble = new Bubble_js_1.Bubble(); } for (const source of sources) { options.bubble.load(source?.bubble); } } reset(particle) { particle.bubble.inRange = false; } } exports.Bubbler = Bubbler; });