UNPKG

@tsparticles/particles

Version:

Easily create highly customizable particle 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.

1,227 lines (1,207 loc) 304 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.0.0-beta.12 */ (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$5 = 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 = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, 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); } divTo(n) { 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 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 calcExactPositionOrRandomFromSizeRanged(data) { const position = { x: data.position?.x !== undefined ? getRangeValue(data.position.x) : undefined, y: data.position?.y !== undefined ? getRangeValue(data.position.y) : undefined, }; return calcExactPositionOrRandomFromSize({ size: data.size, position }); } function parseAlpha(input) { const defaultAlpha = 1; if (!input) { return defaultAlpha; } return input.endsWith("%") ? parseFloat(input) / percentDenominator : parseFloat(input); } exports.AnimationMode = void 0; (function (AnimationMode) { AnimationMode["auto"] = "auto"; AnimationMode["increase"] = "increase"; AnimationMode["decrease"] = "decrease"; AnimationMode["random"] = "random"; })(exports.AnimationMode || (exports.AnimationMode = {})); exports.AnimationStatus = void 0; (function (AnimationStatus) { AnimationStatus["increasing"] = "increasing"; AnimationStatus["decreasing"] = "decreasing"; })(exports.AnimationStatus || (exports.AnimationStatus = {})); exports.DestroyType = void 0; (function (DestroyType) { DestroyType["none"] = "none"; DestroyType["max"] = "max"; DestroyType["min"] = "min"; })(exports.DestroyType || (exports.DestroyType = {})); 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 = {})); exports.StartValueType = void 0; (function (StartValueType) { StartValueType["max"] = "max"; StartValueType["min"] = "min"; StartValueType["random"] = "random"; })(exports.StartValueType || (exports.StartValueType = {})); const minRadius = 0, minMemoizeSize = 0; function memoize(fn, options) { const cache = new Map(), maxSize = options?.maxSize, ttlMs = options?.ttlMs, keyFn = options?.keyFn, stableStringify = (obj, seen = new WeakSet()) => { if (obj === null) { return "null"; } const t = typeof obj; if (t === "undefined") { return "undefined"; } if (t === "number" || t === "boolean" || t === "string") { return JSON.stringify(obj); } if (t === "function") { try { const fn = obj; return fn.toString(); } catch { return '"[Function]"'; } } if (t === "symbol") { try { return obj.toString(); } catch { return '"[Symbol]"'; } } if (Array.isArray(obj)) { return `[${obj.map(i => stableStringify(i, seen)).join(",")}]`; } if (seen.has(obj)) { return '"[Circular]"'; } seen.add(obj); const keys = Object.keys(obj).sort(); return `{${keys.map(k => `${JSON.stringify(k)}:${stableStringify(obj[k], seen)}`).join(",")}}`; }, defaultKeyer = (args) => stableStringify(args), makeKey = (args) => (keyFn ? keyFn(args) : defaultKeyer(args)), ensureBounds = () => { if (typeof maxSize === "number" && maxSize >= minMemoizeSize) { while (cache.size > maxSize) { const firstKey = cache.keys().next().value; if (firstKey === undefined) break; cache.delete(firstKey); } } }; return (...args) => { const key = makeKey(args), now = Date.now(), entry = cache.get(key); if (entry !== undefined) { if (ttlMs && now - entry.ts > ttlMs) { cache.delete(key); } else { cache.delete(key); cache.set(key, { value: entry.value, ts: entry.ts }); return entry.value; } } const result = fn(...args); cache.set(key, { value: result, ts: now }); ensureBounds(); return result; }; } function hasMatchMedia() { return typeof matchMedia !== "undefined"; } function safeDocument() { return globalThis.document; } function safeMatchMedia(query) { if (!hasMatchMedia()) { return; } return matchMedia(query); } function safeIntersectionObserver(callback) { if (typeof IntersectionObserver === "undefined") { return; } return new IntersectionObserver(callback); } 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 arrayRandomIndex(array) { return Math.floor(getRandom() * array.length); } function itemFromArray(array, index, useIndex = true) { return array[index !== undefined && useIndex ? index % array.length : arrayRandomIndex(array)]; } 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 findItemFromSingleOrMultiple(obj, callback) { if (isArray(obj)) { return obj.find((t, index) => callback(t, index)); } const defaultIndex = 0; return callback(obj, defaultIndex) ? obj : undefined; } function initParticleNumericAnimationValue(options, pxRatio) { const valueRange = options.value, animationOptions = options.animation, res = { delayTime: getRangeValue(animationOptions.delay) * millisecondsToSeconds, enable: animationOptions.enable, value: getRangeValue(options.value) * pxRatio, max: getRangeMax(valueRange) * pxRatio, min: getRangeMin(valueRange) * pxRatio, loops: 0, maxLoops: getRangeValue(animationOptions.count), time: 0, }, decayOffset = 1; if (animationOptions.enable) { res.decay = decayOffset - getRangeValue(animationOptions.decay); switch (animationOptions.mode) { case exports.AnimationMode.increase: res.status = exports.AnimationStatus.increasing; break; case exports.AnimationMode.decrease: res.status = exports.AnimationStatus.decreasing; break; case exports.AnimationMode.random: res.status = getRandom() >= half ? exports.AnimationStatus.increasing : exports.AnimationStatus.decreasing; break; } const autoStatus = animationOptions.mode === exports.AnimationMode.auto; switch (animationOptions.startValue) { case exports.StartValueType.min: res.value = res.min; if (autoStatus) { res.status = exports.AnimationStatus.increasing; } break; case exports.StartValueType.max: res.value = res.max; if (autoStatus) { res.status = exports.AnimationStatus.decreasing; } break; case exports.StartValueType.random: default: res.value = randomInRangeValue(res); if (autoStatus) { res.status = getRandom() >= half ? exports.AnimationStatus.increasing : exports.AnimationStatus.decreasing; } break; } } res.initialValue = res.value; return res; } 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 getSize(size, canvasSize) { return getPositionOrSize(size, canvasSize); } function checkDestroy(particle, destroyType, value, minValue, maxValue) { switch (destroyType) { case exports.DestroyType.max: if (value >= maxValue) { particle.destroy(); } break; case exports.DestroyType.min: if (value <= minValue) { particle.destroy(); } break; } } function updateAnimation(particle, data, changeDirection, destroyType, delta) { const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minDecay = 1; if (particle.destroyed || !data.enable || ((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) { return; } const velocity = (data.velocity ?? minVelocity) * delta.factor, minValue = data.min, maxValue = data.max, decay = data.decay ?? minDecay; data.time ??= 0; if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) { data.time += delta.value; } if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) { return; } switch (data.status) { case exports.AnimationStatus.increasing: data.value += velocity; break; case exports.AnimationStatus.decreasing: data.value -= velocity; break; } if (data.velocity && decay !== identity) { data.velocity *= decay; } switch (data.status) { case exports.AnimationStatus.increasing: if (data.value >= maxValue) { if (changeDirection) { data.status = exports.AnimationStatus.decreasing; } else { data.value -= maxValue; } data.loops ??= minLoops; data.loops++; } break; case exports.AnimationStatus.decreasing: if (data.value <= minValue) { if (changeDirection) { data.status = exports.AnimationStatus.increasing; } else { data.value += maxValue; } data.loops ??= minLoops; data.loops++; } break; } checkDestroy(particle, destroyType, data.value, minValue, maxValue); if (!particle.destroyed) { data.value = clamp(data.value, minValue, maxValue); } } 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; } function computeFullScreenStyle(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", }; for (const key in style) { const value = style[key]; if (value === undefined) { continue; } fullScreenStyle.setProperty(key, value); } return fullScreenStyle; } const getFullScreenStyle = memoize(computeFullScreenStyle); 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; if (domContainer instanceof HTMLCanvasElement || domContainer.tagName.toLowerCase() === canvasTag) { canvasEl = domContainer; canvasEl.dataset[generatedAttribute] ??= generatedFalse; } else { const existingCanvases = domContainer.getElementsByTagName(canvasTag), foundCanvas = existingCanvases[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; 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.0.0-beta.12"; } 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, item = items[index]; if (item?.destroyed) { items.splice(index, removeDeleteCount); return; } return item; } async load(params) { await this.init(); const { Container } = await Promise.resolve().then(function () { return Container$1; }), id = params.id ?? params.element?.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 domContainer = getDomContainer(id, params.element), canvasEl = getCanvasFromContainer(domContainer); newItem.canvas.loadCanvas(canvasEl); 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 <= h