UNPKG

@tsparticles/engine

Version:

Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.

151 lines (150 loc) 6 kB
import { canvasFirstIndex, canvasTag, generatedAttribute, generatedFalse, generatedTrue, loadMinIndex, loadRandomFactor, none, one, removeDeleteCount, removeMinIndex, } from "./Utils/Constants.js"; import { itemFromSingleOrMultiple, safeDocument } from "../Utils/Utils.js"; import { EventDispatcher } from "../Utils/EventDispatcher.js"; import { PluginManager } from "./Utils/PluginManager.js"; import { getLogger } from "../Utils/LogUtils.js"; import { getRandom } from "../Utils/MathUtils.js"; const fullPercent = "100%"; async function getDataFromUrl(data) { const url = itemFromSingleOrMultiple(data.url, data.index); if (!url) { return data.fallback; } const response = await fetch(url); if (response.ok) { return (await response.json()); } getLogger().error(`${response.status.toString()} while retrieving config file`); return data.fallback; } const getCanvasFromContainer = (domContainer) => { const documentSafe = safeDocument(); let canvasEl; const isCanvas = domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag; if (isCanvas) { canvasEl = domContainer; canvasEl.dataset[generatedAttribute] ??= generatedFalse; if (canvasEl.dataset[generatedAttribute] === generatedTrue) { canvasEl.style.width ||= fullPercent; canvasEl.style.height ||= fullPercent; canvasEl.style.pointerEvents = "none"; canvasEl.style.setProperty("pointer-events", "none"); } } else { const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases.item(canvasFirstIndex); if (foundCanvas) { canvasEl = foundCanvas; canvasEl.dataset[generatedAttribute] = generatedFalse; } else { canvasEl = documentSafe.createElement(canvasTag); canvasEl.dataset[generatedAttribute] = generatedTrue; domContainer.appendChild(canvasEl); } canvasEl.style.width ||= fullPercent; canvasEl.style.height ||= fullPercent; canvasEl.style.pointerEvents = "none"; canvasEl.style.setProperty("pointer-events", "none"); } return canvasEl; }, getDomContainer = (id, source) => { const documentSafe = safeDocument(); let domContainer = source ?? documentSafe.getElementById(id); if (domContainer) { return domContainer; } domContainer = documentSafe.createElement("canvas"); domContainer.id = id; domContainer.dataset[generatedAttribute] = generatedTrue; documentSafe.body.append(domContainer); return domContainer; }; export class Engine { pluginManager = new PluginManager(this); #domArray = []; #eventDispatcher = new EventDispatcher(); #initialized = false; get items() { return this.#domArray; } get version() { return "4.1.1"; } addEventListener(type, listener) { this.#eventDispatcher.addEventListener(type, listener); } checkVersion(pluginVersion) { if (this.version === pluginVersion) { return; } throw new Error(`The tsParticles version is different from the loaded plugins version. Engine version: ${this.version}. Plugin version: ${pluginVersion}`); } dispatchEvent(type, args) { this.#eventDispatcher.dispatchEvent(type, args); } async init() { if (this.#initialized) { return; } await this.pluginManager.init(); this.#initialized = true; } item(index) { const items = this.items, item = items[index]; if (item?.destroyed) { items.splice(index, removeDeleteCount); return; } return item; } async load(params) { await this.init(); let domSourceElement; if (typeof HTMLElement !== "undefined" && params.element instanceof HTMLElement) { domSourceElement = params.element; } const { Container } = await import("./Container.js"), id = params.id ?? domSourceElement?.id ?? `tsparticles${Math.floor(getRandom() * loadRandomFactor).toString()}`, { index, url } = params, options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options, currentOptions = itemFromSingleOrMultiple(options, index), { items } = this, oldIndex = items.findIndex(v => v.id.description === id), newItem = new Container({ dispatchCallback: (eventType, args) => { this.dispatchEvent(eventType, args); }, id, onDestroy: (remove) => { if (!remove) { return; } const mainArr = this.items, idx = mainArr.indexOf(newItem); if (idx >= removeMinIndex) { mainArr.splice(idx, removeDeleteCount); } }, pluginManager: this.pluginManager, sourceOptions: currentOptions, }); if (oldIndex >= loadMinIndex) { const old = this.item(oldIndex), deleteCount = old ? one : none; if (old && !old.destroyed) { old.destroy(false); } items.splice(oldIndex, deleteCount, newItem); } else { items.push(newItem); } const sourceCanvas = typeof OffscreenCanvas !== "undefined" && params.element instanceof OffscreenCanvas ? params.element : getCanvasFromContainer(getDomContainer(id, domSourceElement)); newItem.canvas.loadCanvas(sourceCanvas); await newItem.start(); return newItem; } async refresh(refresh = true) { if (!refresh) { return; } await Promise.all(this.items.map(t => t.refresh())); } removeEventListener(type, listener) { this.#eventDispatcher.removeEventListener(type, listener); } }