UNPKG

@tsparticles/particles

Version:

Minimal tsParticles particles bundle — lightweight particle engine without confetti or fireworks extras. Perfect for pure particle backgrounds. Ready to use components available for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, In

1,277 lines (1,243 loc) 300 kB
(function(g){g.__tsParticlesInternals=g.__tsParticlesInternals||{};g.__tsParticlesInternals.bundles=g.__tsParticlesInternals.bundles||{};g.__tsParticlesInternals.effects=g.__tsParticlesInternals.effects||{};g.__tsParticlesInternals.engine=g.__tsParticlesInternals.engine||{};g.__tsParticlesInternals.interactions=g.__tsParticlesInternals.interactions||{};g.__tsParticlesInternals.palettes=g.__tsParticlesInternals.palettes||{};g.__tsParticlesInternals.paths=g.__tsParticlesInternals.paths||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins=g.__tsParticlesInternals.plugins||{};g.__tsParticlesInternals.plugins.emittersShapes=g.__tsParticlesInternals.plugins.emittersShapes||{};g.__tsParticlesInternals.presets=g.__tsParticlesInternals.presets||{};g.__tsParticlesInternals.shapes=g.__tsParticlesInternals.shapes||{};g.__tsParticlesInternals.updaters=g.__tsParticlesInternals.updaters||{};g.__tsParticlesInternals.utils=g.__tsParticlesInternals.utils||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas=g.__tsParticlesInternals.canvas||{};g.__tsParticlesInternals.canvas.utils=g.__tsParticlesInternals.canvas.utils||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path=g.__tsParticlesInternals.path||{};g.__tsParticlesInternals.path.utils=g.__tsParticlesInternals.path.utils||{};var __tsProxyFactory=typeof Proxy!=="undefined"?function(obj){return new Proxy(obj,{get:function(target,key){if(!(key in target)){target[key]={};}return target[key];}});}:function(obj){return obj;};g.__tsParticlesInternals.bundles=__tsProxyFactory(g.__tsParticlesInternals.bundles);g.__tsParticlesInternals.effects=__tsProxyFactory(g.__tsParticlesInternals.effects);g.__tsParticlesInternals.interactions=__tsProxyFactory(g.__tsParticlesInternals.interactions);g.__tsParticlesInternals.palettes=__tsProxyFactory(g.__tsParticlesInternals.palettes);g.__tsParticlesInternals.paths=__tsProxyFactory(g.__tsParticlesInternals.paths);g.__tsParticlesInternals.plugins=__tsProxyFactory(g.__tsParticlesInternals.plugins);g.__tsParticlesInternals.plugins.emittersShapes=__tsProxyFactory(g.__tsParticlesInternals.plugins.emittersShapes);g.__tsParticlesInternals.presets=__tsProxyFactory(g.__tsParticlesInternals.presets);g.__tsParticlesInternals.shapes=__tsProxyFactory(g.__tsParticlesInternals.shapes);g.__tsParticlesInternals.updaters=__tsProxyFactory(g.__tsParticlesInternals.updaters);g.__tsParticlesInternals.utils=__tsProxyFactory(g.__tsParticlesInternals.utils);g.__tsParticlesInternals.canvas=__tsProxyFactory(g.__tsParticlesInternals.canvas);g.__tsParticlesInternals.path=__tsProxyFactory(g.__tsParticlesInternals.path);g.tsparticlesInternalExports=g.tsparticlesInternalExports||{};})(typeof globalThis!=="undefined"?globalThis:typeof window!=="undefined"?window:this); /* tsParticles v4.2.1 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.__tsParticlesInternals = global.__tsParticlesInternals || {}, global.__tsParticlesInternals.bundles = global.__tsParticlesInternals.bundles || {}, global.__tsParticlesInternals.bundles.particles = global.__tsParticlesInternals.bundles.particles || {}))); })(this, (function (exports) { 'use strict'; const generatedAttribute = "generated", defaultCompositeValue = "source-over", resizeEvent = "resize", visibilityChangeEvent = "visibilitychange", percentDenominator = 100, half = 0.5, millisecondsToSeconds = 1000, originPoint = { x: 0, y: 0, z: 0, }, defaultTransform = { a: 1, b: 0, c: 0, d: 1, }, randomColorValue = "random", midColorValue = "mid", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha$1 = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, spatialHashGridCellSize = 100, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, minDistance$2 = 0, minRadius$1 = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$2 = 1, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minZ = 0, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, defaultAngle = 0, identity = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, maxNits = 400, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1; exports.MoveDirection = void 0; (function (MoveDirection) { MoveDirection["bottom"] = "bottom"; MoveDirection["bottomLeft"] = "bottom-left"; MoveDirection["bottomRight"] = "bottom-right"; MoveDirection["left"] = "left"; MoveDirection["none"] = "none"; MoveDirection["right"] = "right"; MoveDirection["top"] = "top"; MoveDirection["topLeft"] = "top-left"; MoveDirection["topRight"] = "top-right"; MoveDirection["outside"] = "outside"; MoveDirection["inside"] = "inside"; })(exports.MoveDirection || (exports.MoveDirection = {})); function getZ(source) { return "z" in source ? source.z : originPoint.z; } class Vector3d { x; y; z; constructor(x = originPoint.x, y = originPoint.y, z = originPoint.z) { this.x = x; this.y = y; this.z = z; } static get origin() { return Vector3d.create(originPoint.x, originPoint.y, originPoint.z); } get angle() { return Math.atan2(this.y, this.x); } set angle(angle) { this.#updateFromAngle(angle, this.length); } get length() { return Math.sqrt(this.getLengthSq()); } set length(length) { this.#updateFromAngle(this.angle, length); } static clone(source) { return Vector3d.create(source.x, source.y, getZ(source)); } static create(x, y, z) { if (typeof x === "number") { return new Vector3d(x, y ?? originPoint.y, z ?? originPoint.z); } return new Vector3d(x.x, x.y, getZ(x)); } add(v) { return Vector3d.create(this.x + v.x, this.y + v.y, this.z + getZ(v)); } addTo(v) { this.x += v.x; this.y += v.y; this.z += getZ(v); } copy() { return Vector3d.clone(this); } div(n) { return Vector3d.create(this.x / n, this.y / n, this.z / n); } getLengthSq() { return this.x ** squareExp + this.y ** squareExp; } mult(n) { return Vector3d.create(this.x * n, this.y * n, this.z * n); } multTo(n) { this.x *= n; this.y *= n; this.z *= n; } normalize() { const length = this.length; if (length != none) { this.multTo(inverseFactorNumerator / length); } } rotate(angle) { return Vector3d.create(this.x * Math.cos(angle) - this.y * Math.sin(angle), this.x * Math.sin(angle) + this.y * Math.cos(angle), originPoint.z); } setTo(c) { this.x = c.x; this.y = c.y; this.z = getZ(c); } sub(v) { return Vector3d.create(this.x - v.x, this.y - v.y, this.z - getZ(v)); } subFrom(v) { this.x -= v.x; this.y -= v.y; this.z -= getZ(v); } #updateFromAngle(angle, length) { this.x = Math.cos(angle) * length; this.y = Math.sin(angle) * length; } } class Vector extends Vector3d { constructor(x = originPoint.x, y = originPoint.y) { super(x, y, originPoint.z); } static get origin() { return Vector.create(originPoint.x, originPoint.y); } static clone(source) { return Vector.create(source.x, source.y); } static create(x, y) { if (typeof x === "number") { return new Vector(x, y ?? originPoint.y); } return new Vector(x.x, x.y); } } function isBoolean(arg) { return typeof arg === "boolean"; } function isString(arg) { return typeof arg === "string"; } function isNumber(arg) { return typeof arg === "number"; } function isFunction(arg) { return typeof arg === "function"; } function isObject(arg) { return typeof arg === "object" && arg !== null; } function isArray(arg) { return Array.isArray(arg); } function isNull(arg) { return arg === null || arg === undefined; } const piDeg = 180, degToRadFactor = Math.PI / piDeg; let _random = Math.random; const _animationLoop = { nextFrame: (cb) => requestAnimationFrame(cb), cancel: (idx) => { cancelAnimationFrame(idx); }, }; function setRandom(rnd = Math.random) { _random = rnd; } function getRandomFn() { return _random; } function getRandom() { const min = 0, max = 1; return clamp(_random(), min, max - Number.EPSILON); } function getRandomInRange(min, max) { return getRandom() * (max - min) + min; } function setAnimationFunctions(nextFrame, cancel) { _animationLoop.nextFrame = nextFrame; _animationLoop.cancel = cancel; } function animate(fn) { return _animationLoop.nextFrame(fn); } function cancelAnimation(handle) { _animationLoop.cancel(handle); } function clamp(num, min, max) { return Math.min(Math.max(num, min), max); } function mix(comp1, comp2, weight1, weight2) { return Math.floor((comp1 * weight1 + comp2 * weight2) / (weight1 + weight2)); } function randomInRangeValue(r) { const max = getRangeMax(r), minOffset = 0; let min = getRangeMin(r); if (max === min) { min = minOffset; } return getRandomInRange(min, max); } function getRangeValue(value) { return isNumber(value) ? value : randomInRangeValue(value); } function getRangeMin(value) { return isNumber(value) ? value : value.min; } function getRangeMax(value) { return isNumber(value) ? value : value.max; } function setRangeValue(source, value) { if (source === value || (value === undefined && isNumber(source))) { return source; } const min = getRangeMin(source), max = getRangeMax(source); return value !== undefined ? { min: Math.min(min, value), max: Math.max(max, value), } : setRangeValue(min, max); } function getDistances(pointA, pointB) { const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y; return { dx: dx, dy: dy, distance: Math.hypot(dx, dy) }; } function getDistanceSq(pointA, pointB) { const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y; return dx * dx + dy * dy; } function getDistance(pointA, pointB) { return Math.sqrt(getDistanceSq(pointA, pointB)); } function checkDistance(pointA, pointB, distance) { return getDistanceSq(pointA, pointB) <= distance * distance; } function degToRad(degrees) { return degrees * degToRadFactor; } function getParticleDirectionAngle(direction, position, center) { if (isNumber(direction)) { return degToRad(direction); } switch (direction) { case exports.MoveDirection.top: return -Math.PI * half; case exports.MoveDirection.topRight: return -Math.PI * quarter; case exports.MoveDirection.right: return empty; case exports.MoveDirection.bottomRight: return Math.PI * quarter; case exports.MoveDirection.bottom: return Math.PI * half; case exports.MoveDirection.bottomLeft: return Math.PI * threeQuarter; case exports.MoveDirection.left: return Math.PI; case exports.MoveDirection.topLeft: return -Math.PI * threeQuarter; case exports.MoveDirection.inside: return Math.atan2(center.y - position.y, center.x - position.x); case exports.MoveDirection.outside: return Math.atan2(position.y - center.y, position.x - center.x); default: return getRandom() * doublePI; } } function getParticleBaseVelocity(direction) { const baseVelocity = Vector.origin; baseVelocity.length = 1; baseVelocity.angle = direction; return baseVelocity; } function collisionVelocity(v1, v2, m1, m2) { return Vector.create((v1.x * (m1 - m2)) / (m1 + m2) + (v2.x * double * m2) / (m1 + m2), v1.y); } function calcPositionFromSize(data) { return data.position?.x !== undefined && data.position.y !== undefined ? { x: (data.position.x * data.size.width) / percentDenominator, y: (data.position.y * data.size.height) / percentDenominator, } : undefined; } function calcPositionOrRandomFromSize(data) { return { x: ((data.position?.x ?? getRandom() * percentDenominator) * data.size.width) / percentDenominator, y: ((data.position?.y ?? getRandom() * percentDenominator) * data.size.height) / percentDenominator, }; } function calcPositionOrRandomFromSizeRanged(data) { const position = { x: data.position?.x !== undefined ? getRangeValue(data.position.x) : undefined, y: data.position?.y !== undefined ? getRangeValue(data.position.y) : undefined, }; return calcPositionOrRandomFromSize({ size: data.size, position }); } function calcExactPositionOrRandomFromSize(data) { const { position, size } = data; return { x: position?.x ?? getRandom() * size.width, y: position?.y ?? getRandom() * size.height, }; } function parseAlpha(input) { const defaultAlpha = 1; if (!input) { return defaultAlpha; } return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input); } exports.OutModeDirection = void 0; (function (OutModeDirection) { OutModeDirection["bottom"] = "bottom"; OutModeDirection["left"] = "left"; OutModeDirection["right"] = "right"; OutModeDirection["top"] = "top"; })(exports.OutModeDirection || (exports.OutModeDirection = {})); exports.PixelMode = void 0; (function (PixelMode) { PixelMode["precise"] = "precise"; PixelMode["percent"] = "percent"; })(exports.PixelMode || (exports.PixelMode = {})); const minRadius = 0; function hasMatchMedia() { return typeof matchMedia !== "undefined"; } function safeDocument() { return globalThis.document; } function safeMatchMedia(query) { if (!hasMatchMedia()) { return; } return matchMedia(query); } function safeMutationObserver(callback) { if (typeof MutationObserver === "undefined") { return; } return new MutationObserver(callback); } function isInArray(value, array) { return value === array || (isArray(array) && array.includes(value)); } function itemFromArray(array, index, useIndex = true) { return array[index !== undefined && useIndex ? index % array.length : Math.floor(getRandom() * array.length)]; } function isPointInside(point, size, offset, radius, direction) { return areBoundsInside(calculateBounds(point, radius ?? minRadius), size, offset, direction); } function areBoundsInside(bounds, size, offset, direction) { let inside = true; if (!direction || direction === exports.OutModeDirection.bottom) { inside = bounds.top < size.height + offset.x; } if (inside && (!direction || direction === exports.OutModeDirection.left)) { inside = bounds.right > offset.x; } if (inside && (!direction || direction === exports.OutModeDirection.right)) { inside = bounds.left < size.width + offset.y; } if (inside && (!direction || direction === exports.OutModeDirection.top)) { inside = bounds.bottom > offset.y; } return inside; } function calculateBounds(point, radius) { return { bottom: point.y + radius, left: point.x - radius, right: point.x + radius, top: point.y - radius, }; } function deepExtend(destination, ...sources) { for (const source of sources) { if (isNull(source)) { continue; } if (!isObject(source)) { destination = source; continue; } if (Array.isArray(source)) { if (!Array.isArray(destination)) { destination = []; } } else if (!isObject(destination) || Array.isArray(destination)) { destination = {}; } const sourceKeys = Object.keys(source), dangerousKeys = new Set(["__proto__", "constructor", "prototype"]), hasNested = sourceKeys.some(k => { const v = source[k]; return isObject(v) || Array.isArray(v); }); if (!hasNested) { const sourceDict = source, destDict = destination; for (const key of sourceKeys) { if (dangerousKeys.has(key)) { continue; } if (key in sourceDict) { const v = sourceDict[key]; if (v !== undefined) { destDict[key] = v; } } } continue; } for (const key of sourceKeys) { if (dangerousKeys.has(key)) { continue; } const sourceDict = source, destDict = destination, value = sourceDict[key]; destDict[key] = Array.isArray(value) ? value.map(v => deepExtend(undefined, v)) : deepExtend(destDict[key], value); } } return destination; } function circleBounceDataFromParticle(p) { return { position: p.getPosition(), radius: p.getRadius(), mass: p.getMass(), velocity: p.velocity, factor: Vector.create(getRangeValue(p.options.bounce.horizontal.value), getRangeValue(p.options.bounce.vertical.value)), }; } function circleBounce(p1, p2) { const { x: xVelocityDiff, y: yVelocityDiff } = p1.velocity.sub(p2.velocity), [pos1, pos2] = [p1.position, p2.position], { dx: xDist, dy: yDist } = getDistances(pos2, pos1), minimumDistance = 0; if (xVelocityDiff * xDist + yVelocityDiff * yDist < minimumDistance) { return; } const angle = -Math.atan2(yDist, xDist), m1 = p1.mass, m2 = p2.mass, u1 = p1.velocity.rotate(angle), u2 = p2.velocity.rotate(angle), v1 = collisionVelocity(u1, u2, m1, m2), v2 = collisionVelocity(u2, u1, m1, m2), vFinal1 = v1.rotate(-angle), vFinal2 = v2.rotate(-angle); p1.velocity.x = vFinal1.x * p1.factor.x; p1.velocity.y = vFinal1.y * p1.factor.y; p2.velocity.x = vFinal2.x * p2.factor.x; p2.velocity.y = vFinal2.y * p2.factor.y; } function executeOnSingleOrMultiple(obj, callback) { const defaultIndex = 0; return isArray(obj) ? obj.map((item, index) => callback(item, index)) : callback(obj, defaultIndex); } function itemFromSingleOrMultiple(obj, index, useIndex) { return isArray(obj) ? itemFromArray(obj, index, useIndex) : obj; } function getPositionOrSize(positionOrSize, canvasSize) { const isPercent = positionOrSize.mode === exports.PixelMode.percent; if (!isPercent) { const { mode: _, ...rest } = positionOrSize; return rest; } const isPosition = "x" in positionOrSize; if (isPosition) { return { x: (positionOrSize.x / percentDenominator) * canvasSize.width, y: (positionOrSize.y / percentDenominator) * canvasSize.height, }; } else { return { width: (positionOrSize.width / percentDenominator) * canvasSize.width, height: (positionOrSize.height / percentDenominator) * canvasSize.height, }; } } function getPosition(position, canvasSize) { return getPositionOrSize(position, canvasSize); } function cloneStyle(style) { const clonedStyle = safeDocument().createElement("div").style; for (const key in style) { const styleKey = style[key]; if (!(key in style) || isNull(styleKey)) { continue; } const styleValue = style.getPropertyValue?.(styleKey); if (!styleValue) { continue; } const stylePriority = style.getPropertyPriority?.(styleKey); if (stylePriority) { clonedStyle.setProperty(styleKey, styleValue, stylePriority); } else { clonedStyle.setProperty(styleKey, styleValue); } } return clonedStyle; } let _cachedZIndex, _cachedStyle; function getFullScreenStyle(zIndex) { if (_cachedZIndex !== zIndex || !_cachedStyle) { _cachedZIndex = zIndex; const fullScreenStyle = safeDocument().createElement("div").style, radix = 10, style = { width: "100%", height: "100%", margin: "0", padding: "0", borderWidth: "0", position: "fixed", zIndex: zIndex.toString(radix), "z-index": zIndex.toString(radix), top: "0", left: "0", "pointer-events": "none", }; for (const key in style) { const value = style[key]; if (value === undefined) { continue; } fullScreenStyle.setProperty(key, value); } _cachedStyle = fullScreenStyle; } return _cachedStyle; } function manageListener(element, event, handler, add, options) { if (add) { let addOptions = { passive: true }; if (isBoolean(options)) { addOptions.capture = options; } else if (options !== undefined) { addOptions = options; } element.addEventListener(event, handler, addOptions); } else { const removeOptions = options; element.removeEventListener(event, handler, removeOptions); } } async function getItemsFromInitializer(container, map, initializers, force = false) { let res = map.get(container); if (!res || force) { res = await Promise.all([...initializers.values()].map(t => t(container))); map.set(container, res); } return res; } async function getItemMapFromInitializer(container, map, initializers, force = false) { let res = map.get(container); if (!res || force) { const entries = await Promise.all([...initializers.entries()].map(([key, initializer]) => initializer(container).then(item => [key, item]))); res = new Map(entries); map.set(container, res); } return res; } class EventDispatcher { #listeners; constructor() { this.#listeners = new Map(); } addEventListener(type, listener) { this.removeEventListener(type, listener); let arr = this.#listeners.get(type); if (!arr) { arr = []; this.#listeners.set(type, arr); } arr.push(listener); } dispatchEvent(type, args) { const listeners = this.#listeners.get(type); listeners?.forEach(handler => { handler(args); }); } hasEventListener(type) { return !!this.#listeners.get(type); } removeAllEventListeners(type) { if (!type) { this.#listeners = new Map(); } else { this.#listeners.delete(type); } } removeEventListener(type, listener) { const arr = this.#listeners.get(type); if (!arr) { return; } const length = arr.length, idx = arr.indexOf(listener); if (idx < minIndex) { return; } if (length === deleteCount) { this.#listeners.delete(type); } else { arr.splice(idx, deleteCount); } } } exports.EventType = void 0; (function (EventType) { EventType["configAdded"] = "configAdded"; EventType["containerInit"] = "containerInit"; EventType["particlesSetup"] = "particlesSetup"; EventType["containerStarted"] = "containerStarted"; EventType["containerStopped"] = "containerStopped"; EventType["containerDestroyed"] = "containerDestroyed"; EventType["containerPaused"] = "containerPaused"; EventType["containerPlay"] = "containerPlay"; EventType["containerBuilt"] = "containerBuilt"; EventType["particleAdded"] = "particleAdded"; EventType["particleDestroyed"] = "particleDestroyed"; EventType["particleRemoved"] = "particleRemoved"; })(exports.EventType || (exports.EventType = {})); class PluginManager { colorManagers = new Map(); easingFunctions = new Map(); effectDrawers = new Map(); initializers = { effects: new Map(), shapes: new Map(), updaters: new Map(), }; palettes = new Map(); plugins = []; presets = new Map(); shapeDrawers = new Map(); updaters = new Map(); #allLoadersSet = new Set(); #configs = new Map(); #engine; #executedSet = new Set(); #initialized = false; #isRunningLoaders = false; #loadPromises = new Set(); constructor(engine) { this.#engine = engine; } get configs() { const res = {}; for (const [name, config] of this.#configs) { res[name] = config; } return res; } addColorManager(name, manager) { this.colorManagers.set(name, manager); } addConfig(config) { const key = config.key ?? config.name ?? "default"; this.#configs.set(key, config); this.#engine.dispatchEvent(exports.EventType.configAdded, { data: { name: key, config } }); } addEasing(name, easing) { if (this.easingFunctions.get(name)) { return; } this.easingFunctions.set(name, easing); } addEffect(effect, drawer) { this.initializers.effects.set(effect, drawer); } addPalette(name, palette) { this.palettes.set(name, palette); } addParticleUpdater(name, updaterInitializer) { this.initializers.updaters.set(name, updaterInitializer); } addPlugin(plugin) { if (this.getPlugin(plugin.id)) { return; } this.plugins.push(plugin); } addPreset(preset, options, override = false) { if (!(override || !this.getPreset(preset))) { return; } this.presets.set(preset, options); } addShape(shapes, drawer) { for (const shape of shapes) { this.initializers.shapes.set(shape, drawer); } } clearPlugins(container) { this.effectDrawers.delete(container); this.shapeDrawers.delete(container); this.updaters.delete(container); } getEasing(name) { return this.easingFunctions.get(name) ?? ((value) => value); } getEffectDrawers(container, force = false) { return getItemMapFromInitializer(container, this.effectDrawers, this.initializers.effects, force); } getPalette(name) { return this.palettes.get(name); } getPlugin(plugin) { return this.plugins.find(t => t.id === plugin); } getPreset(preset) { return this.presets.get(preset); } async getShapeDrawers(container, force = false) { return getItemMapFromInitializer(container, this.shapeDrawers, this.initializers.shapes, force); } async getUpdaters(container, force = false) { return getItemsFromInitializer(container, this.updaters, this.initializers.updaters, force); } async init() { if (this.#initialized || this.#isRunningLoaders) { return; } this.#isRunningLoaders = true; this.#executedSet = new Set(); this.#allLoadersSet = new Set(this.#loadPromises); try { for (const loader of this.#allLoadersSet) { await this.#runLoader(loader, this.#executedSet, this.#allLoadersSet); } } finally { this.#loadPromises.clear(); this.#isRunningLoaders = false; this.#initialized = true; } } loadParticlesOptions(container, options, ...sourceOptions) { const updaters = this.updaters.get(container); if (!updaters) { return; } updaters.forEach(updater => updater.loadOptions?.(options, ...sourceOptions)); } async register(...loaders) { if (this.#initialized) { throw new Error("Register plugins can only be done before calling tsParticles.load()"); } for (const loader of loaders) { if (this.#isRunningLoaders) { await this.#runLoader(loader, this.#executedSet, this.#allLoadersSet); } else { this.#loadPromises.add(loader); } } } async #runLoader(loader, executed, allLoaders) { if (executed.has(loader)) return; executed.add(loader); allLoaders.add(loader); await loader(this.#engine); } } const errorPrefix = "tsParticles - Error"; const wrap = (fn) => (...args) => { fn(...args); }, _logger = { debug: wrap(console.debug), error: (message, ...optionalParams) => { console.error(`${errorPrefix} - ${message}`, ...optionalParams); }, info: wrap(console.info), log: wrap(console.log), trace: wrap(console.trace), verbose: wrap(console.log), warning: wrap(console.warn), }; function setLogger(logger) { if (logger.debug) { _logger.debug = wrap(logger.debug); } if (logger.error) { _logger.error = wrap(logger.error); } if (logger.info) { _logger.info = wrap(logger.info); } if (logger.log) { _logger.log = wrap(logger.log); } if (logger.trace) { _logger.trace = wrap(logger.trace); } if (logger.verbose) { _logger.verbose = wrap(logger.verbose); } if (logger.warning) { _logger.warning = wrap(logger.warning); } } function getLogger() { return _logger; } 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; }; class Engine { pluginManager = new PluginManager(this); #domArray = []; #eventDispatcher = new EventDispatcher(); #initialized = false; get items() { return this.#domArray; } get version() { return "4.2.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 Promise.resolve().then(function () { return Container$1; }), 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); } } function initEngine() { return new Engine(); } var RangeType; (function (RangeType) { RangeType["circle"] = "circle"; RangeType["rectangle"] = "rectangle"; })(RangeType || (RangeType = {})); class BaseRange { position; type; constructor(x, y, type) { this.position = { x: x, y: y, }; this.type = type; } _resetPosition(x, y) { this.position.x = x; this.position.y = y; } } class Circle extends BaseRange { radius; constructor(x, y, radius) { super(x, y, RangeType.circle); this.radius = radius; } contains(point) { return checkDistance(point, this.position, this.radius); } intersects(range) { const pos1 = this.position, pos2 = range.position, r = this.radius, dx = Math.abs(pos2.x - pos1.x), dy = Math.abs(pos2.y - pos1.y); if (range instanceof Circle || range.type === RangeType.circle) { const circleRange = range, rSum = r + circleRange.radius, dist = Math.hypot(dx, dy); return rSum > dist; } else if (range instanceof Rectangle || range.type === RangeType.rectangle) { const rectRange = range, { width, height } = rectRange.size, edges = Math.pow(dx - width, squareExp) + Math.pow(dy - height, squareExp); return edges <= r ** squareExp || (dx <= r + width && dy <= r + height) || dx <= width || dy <= height; } return false; } reset(x, y, radius) { this._resetPosition(x, y); this.radius = radius; return this; } } class Rectangle extends BaseRange { size; constructor(x, y, width, height) { super(x, y, RangeType.rectangle); this.size = { height: height, width: width, }; } contains(point) { const w = this.size.width, h = this.size.height, pos = this.position; return point.x >= pos.x && point.x <= pos.x + w && point.y >= pos.y && point.y <= pos.y + h; } intersects(range) { if (range instanceof Circle) { return range.intersects(this); } if (!(range instanceof Rectangle)) { return false; } const w = this.size.width, h = this.size.height, pos1 = this.position, pos2 = range.position, size2 = range.size, w2 = size2.width, h2 = size2.height; return pos2.x < pos1.x + w && pos2.x + w2 > pos1.x && pos2.y < pos1.y + h && pos2.y + h2 > pos1.y; } reset(x, y, width, height) { this._resetPosition(x, y); this.size.width = width; this.size.height = height; return this; } } exports.RotateDirection = void 0; (function (RotateDirection) { RotateDirection["clockwise"] = "clockwise"; RotateDirection["counterClockwise"] = "counter-clockwise"; RotateDirection["random"] = "random"; })(exports.RotateDirection || (exports.RotateDirection = {})); exports.AnimationMode = void 0; (function (AnimationMode) { AnimationMode["auto"] = "auto"; AnimationMode["increase"] = "increase"; AnimationMode["decrease"] = "decrease"; AnimationMode["random"] = "random"; })(exports.AnimationMode || (exports.AnimationMode = {})); exports.LimitMode = void 0; (function (LimitMode) { LimitMode["delete"] = "delete"; LimitMode["wait"] = "wait"; })(exports.LimitMode || (exports.LimitMode = {})); exports.OutMode = void 0; (function (OutMode) { OutMode["bounce"] = "bounce"; OutMode["none"] = "none"; OutMode["out"] = "out"; OutMode["destroy"] = "destroy"; OutMode["split"] = "split"; })(exports.OutMode || (exports.OutMode = {})); exports.AlterType = void 0; (function (AlterType) { AlterType["darken"] = "darken"; AlterType["enlighten"] = "enlighten"; })(exports.AlterType || (exports.AlterType = {})); exports.DestroyType = void 0; (function (DestroyType) { DestroyType["none"] = "none"; DestroyType["max"] = "max"; DestroyType["min"] = "min"; })(exports.DestroyType || (exports.DestroyType = {})); exports.GradientType = void 0; (function (GradientType) { GradientType["linear"] = "linear"; GradientType["radial"] = "radial"; GradientType["random"] = "random"; })(exports.GradientType || (exports.GradientType = {})); exports.ParticleOutType = void 0; (function (ParticleOutType) { ParticleOutType["normal"] = "normal"; ParticleOutType["inside"] = "inside"; ParticleOutType["outside"] = "outside"; })(exports.ParticleOutType || (exports.ParticleOutType = {})); exports.StartValueType = void 0; (function (StartValueType) { StartValueType["max"] = "max"; StartValueType["min"] = "min"; StartValueType["random"] = "random"; })(exports.StartValueType || (exports.StartValueType = {})); exports.AnimationStatus = void 0; (function (AnimationStatus) { AnimationStatus["increasing"] = "increasing"; AnimationStatus["decreasing"] = "decreasing"; })(exports.AnimationStatus || (exports.AnimationStatus = {})); class OptionLoader { load(data) { if (isNull(data)) { return; } this.doLoad(data); } } function loadOptions(options, ...sourceOptionsArr) { for (const sourceOptions of sourceOptionsArr) { options.load(sourceOptions); } } function loadProperty(obj, key, value) { if (value !== undefined) { obj[key] = value; } } function loadRangeProperty(obj, key, value) { if (value !== undefined) { obj[key] = setRangeValue(value); } } function loadNestedProperty(obj, key, value) { if (value !== undefined) { obj[key].load(value); } } function loadLazyProperty(obj, key, value, factory) { if (value !== undefined) { const objRecord = obj; objRecord[key] ??= factory(); objRecord[key].load(value); } } function loadExtendProperty(obj, key, value) { if (value !== undefined) { obj[key] = deepExtend(obj[key] ?? {}, value); } } function loadOptionProperty(obj, key, optionClass, ...sources) { const objRecord = obj; objRecord[key] ??= new optionClass(); const target = objRecord[key]; for (const source of sources) { target.load(source?.[key]); } } class AnimationOptions extends OptionLoader { count = 0; decay = 0; delay = 0; enable = false; speed = 1; sync = false; doLoad(data) { loadRangeProperty(this, "count", data.count); loadProperty(this, "enable", data.enable); loadRangeProperty(this, "speed", data.speed); loadRangeProperty(this, "decay", data.decay); loadRangeProperty(this, "delay", data.delay); loadProperty(this, "sync", data.sync); } } class RangedAnimationOptions extends AnimationOptions { mode = exports.AnimationMode.auto; startValue = exports.StartValueType.random; doLoad(data) { super.doLoad(data); loadProperty(this, "mode", data.mode); loadProperty(this, "startValue", data.startValue); } } class ColorAnimation extends AnimationOptions { max; min; offset = 0; sync = true; constructor(min, max) { super(); this.min = min; this.max = max; } doLoad(data) { super.doLoad(data); loadProperty(this, "max", data.max); loadProperty(this, "min", data.min); loadRangeProperty(this, "offset", data.offset); } } class HslAnimation extends OptionLoader { h = new ColorAnimation(hMin, hMax); l = new ColorAnimation(lMin, lMax); s = new ColorAnimation(sMin, sMax); doLoad(data) { this.h.load(data.h); this.s.load(data.s); this.l.load(data.l); } } class OptionsColor extends OptionLoader { value = ""; static create(source, data) { const color = new OptionsColor(); color.load(source); if (data !== undefined) { if (isString(data) || isArray(data)) { color.load({ value: data }); } else { color.load(data); } } return color; } doLoad(data) { if (!isNull(data.value)) { this.value = data.value; } } } class AnimatableColor extends OptionsColor { animation = new HslAnimation(); static create(source, data) { const color = new AnimatableColor(); color.load(source); if (data !== undefined) { if (isString(data) || isArray(data)) { color.load({ value: data }); } else { color.load(data); } } return color; } doLoad(data) { super.doLoad(data); const colorAnimation = data.animation; if (colorAnimation !== undefined) { if (colorAnimation.enable === undefined) { this.animation.load(data.animation); } else { this.animation.h.load(colorAnimation); } } } } class Background extends OptionLoader { color; image = ""; opacity = 1; position = ""; repeat = ""; size