(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", "../Utils/Utils", "./Canvas", "./Utils/EventListeners", "./Utils/FrameManager", "../Options/Classes/Options", "./Particles", "./Retina", "../Utils/NumberUtils", "../Utils/OptionsUtils"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Container = void 0; const Utils_1 = require("../Utils/Utils"); const Canvas_1 = require("./Canvas"); const EventListeners_1 = require("./Utils/EventListeners"); const FrameManager_1 = require("./Utils/FrameManager"); const Options_1 = require("../Options/Classes/Options"); const Particles_1 = require("./Particles"); const Retina_1 = require("./Retina"); const NumberUtils_1 = require("../Utils/NumberUtils"); const OptionsUtils_1 = require("../Utils/OptionsUtils"); function guardCheck(container) { return container && !container.destroyed; } function loadContainerOptions(engine, container, ...sourceOptionsArr) { const options = new Options_1.Options(engine, container); (0, OptionsUtils_1.loadOptions)(options, ...sourceOptionsArr); return options; } const defaultPathGeneratorKey = "default", defaultPathGenerator = { generate: (p) => { const v = p.velocity.copy(); v.angle += (v.length * Math.PI) / 180; return v; }, init: () => { }, update: () => { }, reset: () => { }, }; class Container { constructor(engine, id, sourceOptions) { this.id = id; this._engine = engine; this.fpsLimit = 120; this.smooth = false; this._delay = 0; this.duration = 0; this.lifeTime = 0; this._firstStart = true; this.started = false; this.destroyed = false; this._paused = true; this.lastFrameTime = 0; this.zLayers = 100; this.pageHidden = false; this._sourceOptions = sourceOptions; this._initialSourceOptions = sourceOptions; this.retina = new Retina_1.Retina(this); this.canvas = new Canvas_1.Canvas(this); this.particles = new Particles_1.Particles(this._engine, this); this.frameManager = new FrameManager_1.FrameManager(this); this.pathGenerators = new Map(); this.interactivity = { mouse: { clicking: false, inside: false, }, }; this.plugins = new Map(); this.drawers = new Map(); this._options = loadContainerOptions(this._engine, this); this.actualOptions = loadContainerOptions(this._engine, this); this._eventListeners = new EventListeners_1.EventListeners(this); if (typeof IntersectionObserver !== "undefined" && IntersectionObserver) { this._intersectionObserver = new IntersectionObserver((entries) => this._intersectionManager(entries)); } this._engine.dispatchEvent("containerBuilt", { container: this }); } get options() { return this._options; } get sourceOptions() { return this._sourceOptions; } addClickHandler(callback) { if (!guardCheck(this)) { return; } const el = this.interactivity.element; if (!el) { return; } const clickOrTouchHandler = (e, pos, radius) => { if (!guardCheck(this)) { return; } const pxRatio = this.retina.pixelRatio, posRetina = { x: pos.x * pxRatio, y: pos.y * pxRatio, }, particles = this.particles.quadTree.queryCircle(posRetina, radius * pxRatio); callback(e, particles); }; const clickHandler = (e) => { if (!guardCheck(this)) { return; } const mouseEvent = e, pos = { x: mouseEvent.offsetX || mouseEvent.clientX, y: mouseEvent.offsetY || mouseEvent.clientY, }; clickOrTouchHandler(e, pos, 1); }; const touchStartHandler = () => { if (!guardCheck(this)) { return; } touched = true; touchMoved = false; }; const touchMoveHandler = () => { if (!guardCheck(this)) { return; } touchMoved = true; }; const touchEndHandler = (e) => { if (!guardCheck(this)) { return; } if (touched && !touchMoved) { const touchEvent = e; let lastTouch = touchEvent.touches[touchEvent.touches.length - 1]; if (!lastTouch) { lastTouch = touchEvent.changedTouches[touchEvent.changedTouches.length - 1]; if (!lastTouch) { return; } } const element = this.canvas.element, canvasRect = element ? element.getBoundingClientRect() : undefined, pos = { x: lastTouch.clientX - (canvasRect ? canvasRect.left : 0), y: lastTouch.clientY - (canvasRect ? canvasRect.top : 0), }; clickOrTouchHandler(e, pos, Math.max(lastTouch.radiusX, lastTouch.radiusY)); } touched = false; touchMoved = false; }; const touchCancelHandler = () => { if (!guardCheck(this)) { return; } touched = false; touchMoved = false; }; let touched = false, touchMoved = false; el.addEventListener("click", clickHandler); el.addEventListener("touchstart", touchStartHandler); el.addEventListener("touchmove", touchMoveHandler); el.addEventListener("touchend", touchEndHandler); el.addEventListener("touchcancel", touchCancelHandler); } addPath(key, generator, override = false) { if (!guardCheck(this) || (!override && this.pathGenerators.has(key))) { return false; } this.pathGenerators.set(key, generator !== null && generator !== void 0 ? generator : defaultPathGenerator); return true; } destroy() { if (!guardCheck(this)) { return; } this.stop(); this.particles.destroy(); this.canvas.destroy(); for (const [, drawer] of this.drawers) { if (drawer.destroy) { drawer.destroy(this); } } for (const key of this.drawers.keys()) { this.drawers.delete(key); } this._engine.plugins.destroy(this); this.destroyed = true; const mainArr = this._engine.dom(), idx = mainArr.findIndex((t) => t === this); if (idx >= 0) { mainArr.splice(idx, 1); } this._engine.dispatchEvent("containerDestroyed", { container: this }); } draw(force) { if (!guardCheck(this)) { return; } let refreshTime = force; this._drawAnimationFrame = (0, Utils_1.animate)()(async (timestamp) => { if (refreshTime) { this.lastFrameTime = undefined; refreshTime = false; } await this.frameManager.nextFrame(timestamp); }); } exportConfiguration() { return JSON.stringify(this.actualOptions, (key, value) => { if (key === "_engine" || key === "_container") { return; } return value; }, 2); } exportImage(callback, type, quality) { const element = this.canvas.element; if (element) { element.toBlob(callback, type !== null && type !== void 0 ? type : "image/png", quality); } } exportImg(callback) { this.exportImage(callback); } getAnimationStatus() { return !this._paused && !this.pageHidden && guardCheck(this); } handleClickMode(mode) { if (!guardCheck(this)) { return; } this.particles.handleClickMode(mode); for (const [, plugin] of this.plugins) { if (plugin.handleClickMode) { plugin.handleClickMode(mode); } } } async init() { if (!guardCheck(this)) { return; } const shapes = this._engine.plugins.getSupportedShapes(); for (const type of shapes) { const drawer = this._engine.plugins.getShapeDrawer(type); if (drawer) { this.drawers.set(type, drawer); } } this._options = loadContainerOptions(this._engine, this, this._initialSourceOptions, this.sourceOptions); this.actualOptions = loadContainerOptions(this._engine, this, this._options); const availablePlugins = this._engine.plugins.getAvailablePlugins(this); for (const [id, plugin] of availablePlugins) { this.plugins.set(id, plugin); } this.retina.init(); await this.canvas.init(); this.updateActualOptions(); this.canvas.initBackground(); this.canvas.resize(); this.zLayers = this.actualOptions.zLayers; this.duration = (0, NumberUtils_1.getRangeValue)(this.actualOptions.duration) * 1000; this._delay = (0, NumberUtils_1.getRangeValue)(this.actualOptions.delay) * 1000; this.lifeTime = 0; this.fpsLimit = this.actualOptions.fpsLimit > 0 ? this.actualOptions.fpsLimit : 120; this.smooth = this.actualOptions.smooth; for (const [, drawer] of this.drawers) { if (drawer.init) { await drawer.init(this); } } for (const [, plugin] of this.plugins) { if (plugin.init) { await plugin.init(); } } this._engine.dispatchEvent("containerInit", { container: this }); this.particles.init(); this.particles.setDensity(); for (const [, plugin] of this.plugins) { if (plugin.particlesSetup) { plugin.particlesSetup(); } } this._engine.dispatchEvent("particlesSetup", { container: this }); } async loadTheme(name) { if (!guardCheck(this)) { return; } this._currentTheme = name; await this.refresh(); } pause() { if (!guardCheck(this)) { return; } if (this._drawAnimationFrame !== undefined) { (0, Utils_1.cancelAnimation)()(this._drawAnimationFrame); delete this._drawAnimationFrame; } if (this._paused) { return; } for (const [, plugin] of this.plugins) { if (plugin.pause) { plugin.pause(); } } if (!this.pageHidden) { this._paused = true; } this._engine.dispatchEvent("containerPaused", { container: this }); } play(force) { if (!guardCheck(this)) { return; } const needsUpdate = this._paused || force; if (this._firstStart && !this.actualOptions.autoPlay) { this._firstStart = false; return; } if (this._paused) { this._paused = false; } if (needsUpdate) { for (const [, plugin] of this.plugins) { if (plugin.play) { plugin.play(); } } } this._engine.dispatchEvent("containerPlay", { container: this }); this.draw(needsUpdate || false); } async refresh() { if (!guardCheck(this)) { return; } this.stop(); return this.start(); } async reset() { if (!guardCheck(this)) { return; } this._options = loadContainerOptions(this._engine, this); return this.refresh(); } setNoise(noiseOrGenerator, init, update) { if (!guardCheck(this)) { return; } this.setPath(noiseOrGenerator, init, update); } setPath(pathOrGenerator, init, update) { if (!pathOrGenerator || !guardCheck(this)) { return; } const pathGenerator = Object.assign({}, defaultPathGenerator); if (typeof pathOrGenerator === "function") { pathGenerator.generate = pathOrGenerator; if (init) { pathGenerator.init = init; } if (update) { pathGenerator.update = update; } } else { const oldGenerator = pathGenerator; pathGenerator.generate = pathOrGenerator.generate || oldGenerator.generate; pathGenerator.init = pathOrGenerator.init || oldGenerator.init; pathGenerator.update = pathOrGenerator.update || oldGenerator.update; } this.addPath(defaultPathGeneratorKey, pathGenerator, true); } async start() { if (!guardCheck(this) || this.started) { return; } await this.init(); this.started = true; await new Promise((resolve) => { this._delayTimeout = setTimeout(async () => { this._eventListeners.addListeners(); if (this.interactivity.element instanceof HTMLElement && this._intersectionObserver) { this._intersectionObserver.observe(this.interactivity.element); } for (const [, plugin] of this.plugins) { if (plugin.start) { await plugin.start(); } } this._engine.dispatchEvent("containerStarted", { container: this }); this.play(); resolve(); }, this._delay); }); } stop() { if (!guardCheck(this) || !this.started) { return; } if (this._delayTimeout) { clearTimeout(this._delayTimeout); delete this._delayTimeout; } this._firstStart = true; this.started = false; this._eventListeners.removeListeners(); this.pause(); this.particles.clear(); this.canvas.clear(); if (this.interactivity.element instanceof HTMLElement && this._intersectionObserver) { this._intersectionObserver.unobserve(this.interactivity.element); } for (const [, plugin] of this.plugins) { if (plugin.stop) { plugin.stop(); } } for (const key of this.plugins.keys()) { this.plugins.delete(key); } this._sourceOptions = this._options; this._engine.dispatchEvent("containerStopped", { container: this }); } updateActualOptions() { this.actualOptions.responsive = []; const newMaxWidth = this.actualOptions.setResponsive(this.canvas.size.width, this.retina.pixelRatio, this._options); this.actualOptions.setTheme(this._currentTheme); if (this.responsiveMaxWidth === newMaxWidth) { return false; } this.responsiveMaxWidth = newMaxWidth; return true; } _intersectionManager(entries) { if (!guardCheck(this) || !this.actualOptions.pauseOnOutsideViewport) { return; } for (const entry of entries) { if (entry.target !== this.interactivity.element) { continue; } (entry.isIntersecting ? this.play : this.pause)(); } } } exports.Container = Container; });