@tsparticles/fireworks
Version:
tsParticles fireworks bundle — easily create spectacular fireworks and fountain particle effects with built-in presets. Ready to use components available for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.
8,807 lines • 356 kB
JavaScript
(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.4.0 */
(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.fireworks = global.__tsParticlesInternals.bundles.fireworks || {})));
})(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 = 0, minRadius$1 = 0, defaultTransformValue = 1, minimumSize = 0, zIndexFactorOffset = 1, defaultOpacity$1 = 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, 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 isForbiddenKey(key) {
return key === "__proto__" || key === "constructor" || key === "prototype";
}
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 = Object.create(null);
}
const sourceKeys = Object.keys(source), 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 (isForbiddenKey(key)) {
continue;
}
const v = sourceDict[key];
if (v !== undefined) {
destDict[key] = v;
}
}
continue;
}
for (const key of sourceKeys) {
if (isForbiddenKey(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.4.0";
}
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() {
const existing = globalThis.tsParticles;
if (existing?.pluginManager) {
return existing;
}
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.HdrMode = void 0;
(function (HdrMode) {
HdrMode["standard"] = "standard";
HdrMode["natural"] = "natural";
HdrMode["vivid"] = "vivid";
HdrMode["cinematic"] = "cinematic";
HdrMode["dynamic"] = "dynamic";
})(exports.HdrMode || (exports.HdrMode = {}));
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 = {}));
exports.DrawLayer = void 0;
(function (DrawLayer) {
DrawLayer[DrawLayer["BackgroundElement"] = 0] = "BackgroundElement";
DrawLayer[DrawLayer["BackgroundDraw"] = 1] = "BackgroundDraw";
DrawLayer[DrawLayer["BackgroundMask"] = 2] = "BackgroundMask";
DrawLayer[DrawLayer["CanvasSetup"] = 3] = "CanvasSetup";
DrawLayer[DrawLayer["PluginContent"] = 4] = "PluginContent";
DrawLayer[DrawLayer["Particles"] = 5] = "Particles";
DrawLayer[DrawLayer["CanvasCleanup"] = 6] = "CanvasCleanup";
DrawLayer[DrawLayer["Foreground"] = 7] = "Foreground";
})(exports.DrawLayer || (exports.DrawLayer = {}));
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) {
const keyStr = key;
if (keyStr === "__proto__" || keyStr === "constructor" || keyStr === "prototype") {
return;
}
const objRecord = obj, currentValue = Object.prototype.hasOwnProperty.call(objRecord, keyStr) ? objRecord[keyStr] : {};
objRecord[keyStr] = deepExtend(currentValue, 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;
draw;
element;
image = "";
opacity = 1;
position = "";
repeat = "";
size = "";
constructor() {
super();
this.color = new OptionsColor();
this.color.value = "";
}
doLoad(data) {
if (data.color !== undefined) {
this.color = OptionsColor.create(this.color, data.color);
}
loadProperty(this, "element", data.element);
loadProperty(this, "draw", data.draw);
loadProperty(this, "image", data.image);
loadProperty(this, "position", data.position);
loadProperty(this, "repeat", data.repeat);
loadProperty(this, "size", data.size);
loadProperty(this, "opacity", data.opacity);
}
}
class FullScreen extends OptionLoader {
enable = true;
zIndex = 0;
doLoad(data) {
loadProperty(this, "enable", data.enable);
loadProperty(this, "zIndex", data.zIndex);
}
}
const defaultPeakNits = 400, minPeakNits = 0, toStringValue = (value) => String(value);
class HDROptions extends OptionLoader {
enable = true;
mode = exports.HdrMode.standard;
peakNits = defaultPeakNits;
doLoad(data) {
if (data.enable !== undefined && !isBoolean(data.enable)) {
throw new Error(`Invalid HDR "enable" value: expected a boolean, got "${String(data.enable)}"`);
}
if (data.mode !== undefined &&
(typeof data.mode !== "string" || !Object.values(exports.HdrMode).includes(data.mode))) {
throw new Error(`Invalid HDR "mode" value: expected one of ${Object.values(exports.HdrMode).join(", ")}, got "${toStringValue(data.mode)}"`);
}
if (data.peakNits !== undefined &&
(!isNumber(data.peakNits) || !Number.isFinite(data.peakNits) || data.peakNits <= minPeakNits)) {
throw new Error(`Invalid HDR "peakNits" value: expected a positive number, got "${String(data.peakNits)}"`);
}
loadProperty(this, "enable", data.enable);
loadProperty(this, "mode", data.mode);
loadProperty(this, "peakNits", data.peakNits);
}
}
class ResizeEvent extends OptionLoader {
delay = 0.5;
enable = true;
doLoad(data) {
loadProperty(this, "delay", data.delay);
loadProperty(this, "enable", data.enable);
}
}
class Effect extends OptionLoader {
close = true;
options = {};
type = [];
doLoad(data) {
const options = data.options;
if (options !== undefined) {
for (const effect in options) {
const item = options[effect];
if (item) {
this.options[effect] = deepExtend(this.options[effect] ?? {}, item);
}
}
}
loadProperty(this, "close", data.close);
loadProperty(this, "type", data.type);
}
}
class Fill extends OptionLoader {
color;
enable = true;
opacity = 1;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class MoveAngle extends OptionLoader {
offset = 0;
value = 90;
doLoad(data) {
loadRangeProperty(this, "offset", data.offset);
loadRangeProperty(this, "value", data.value);
}
}
class MoveCenter extends OptionLoader {
mode = exports.PixelMode.percent;
radius = 0;
x = 50;
y = 50;
doLoad(data) {
loadProperty(this, "x", data.x);
loadProperty(this, "y", data.y);
loadProperty(this, "mode", data.mode);
loadProperty(this, "radius", data.radius);
}
}
class MoveGravity extends OptionLoader {
acceleration = 9.81;
enable = false;
inverse = false;
maxSpeed = 50;
doLoad(data) {
loadRangeProperty(this, "acceleration", data.acceleration);
loadProperty(this, "enable", data.enable);
loadProperty(this, "inverse", data.inverse);
loadRangeProperty(this, "maxSpeed", data.maxSpeed);
}
}
class ValueWithRandom extends OptionLoader {
value = 0;
doLoad(data) {
if (!isNull(data.value)) {
this.value = setRangeValue(data.value);
}
}
}
class AnimationValueWithRandom extends ValueWithRandom {
animation = new AnimationOptions();
doLoad(data) {
super.doLoad(data);
loadNestedProperty(this, "animation", data.animation);
}
}
class RangedAnimationValueWithRandom extends AnimationValueWithRandom {
animation = new RangedAnimationOptions();
}
class MovePath extends OptionLoader {
clamp = true;
delay = new ValueWithRandom();
enable = false;
generator;
options = {};
doLoad(data) {
loadProperty(this, "clamp", data.clamp);
this.delay.load(data.delay);
loadProperty(this, "enable", data.enable);
this.generator = data.generator;
if (data.options) {
this.options = deepExtend(this.options, data.options);
}
}
}
class OutModes extends OptionLoader {
bottom;
default = exports.OutMode.out;
left;
right;
top;
doLoad(data) {
if (data.default !== undefined) {
this.default = data.default;
}
this.bottom = data.bottom ?? data.default;
this.left = data.left ?? data.default;
this.right = data.right ?? data.default;
this.top = data.top ?? data.default;
}
}
class Spin extends OptionLoader {
acceleration = 0;
enable = false;
position;
doLoad(data) {
loadRangeProperty(this, "acceleration", data.acceleration);
loadProperty(this, "enable", data.enable);
if (data.position) {
this.position = deepExtend({}, data.position);
}
}
}
class Move extends OptionLoader {
angle = new MoveAngle();
center = new MoveCenter();
decay = 0;
direction = exports.MoveDirection.none;
distance = {};
drift = 0;
enable = false;
gravity = new MoveGravity();
outModes = new OutModes();
path = new MovePath();
random = false;
size = false;
speed = 2;
spin = new Spin();
straight = false;
vibrate = false;
warp = false;
doLoad(data) {
this.angle.load(isNumber(data.angle) ? { value: data.angle } : data.angle);
this.center.load(data.center);
loadRangeProperty(this, "decay", data.decay);
loadProperty(this, "direction", data.direction);
if (data.distance !== undefined) {
this.distance = isNumber(data.distance)
? {
horizontal: data.distance,
vertical: data.distance,
}
: { ...data.distance };
}
loadRangeProperty(this, "drift", data.drift);
loadProperty(this, "enable", data.enable);
this.gravity.load(data.gravity);
const outModes = data.outModes;
if (outModes !== undefined) {
if (isObject(outModes)) {
this.outModes.load(outModes);
}
else {
this.outModes.load({
default: outModes,
});
}
}
this.path.load(data.path);
loadProperty(this, "random", data.random);
loadProperty(this, "size", data.size);
loadRangeProperty(this, "speed", data.speed);
this.spin.load(data.spin);
loadProperty(this, "straight", data.straight);
loadProperty(this, "vibrate", data.vibrate);
loadProperty(this, "warp", data.warp);
}
}
class Stroke extends OptionLoader {
color;
opacity;
width = 0;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadRangeProperty(this, "width", data.width);
loadRangeProperty(this, "opacity", data.opacity);
}
}
class Paint extends OptionLoader {
color;
fill;
stroke;
doLoad(data) {
if (data.color !== undefined) {
this.color = AnimatableColor.create(this.color, data.color);
}
loadLazyProperty(this, "fill", data.fill, () => new Fill());
loadLazyProperty(this, "stroke", data.stroke, () => new Stroke());
}
}
class ParticlesBounceFactor extends ValueWithRandom {
value = 1;
}
class ParticlesBounce extends OptionLoader {
horizontal = new ParticlesBounceFactor();
vertical = new ParticlesBounceFactor();
doLoad(data) {
this.horizontal.load(data.horizontal);
this.vertical.load(data.vertical);
}
}
class ParticlesDensity extends OptionLoader {
enable = false;
height = 1080;
width = 1920;
doLoad(data) {
loadProperty(this, "enable", data.enable);
loadProperty(this, "width", data.width);
loadProperty(this, "height", data.height);
}
}
class ParticlesNumberLimit extends OptionLoader {
mode = exports.LimitMode.delete;
value = 0;
doLoad(data) {
loadProperty(this, "mode", data.mode);
loadProperty(this, "value", data.value);
}
}
class ParticlesNumber extends OptionLoader {
density = new ParticlesDensity();
limit = new ParticlesNumberLimit();
value = 0;
doLoad(data) {
this.density.load(data.density);
this.limit.load(data.limit);
loadProperty(this, "value", data.value);
}
}
class Shape extends OptionLoader {
close = true;
options = {};
type = "circle";
doLoad(data) {
const options = data.options;
if (options !== undefined) {
for (const shape in options) {
const item = options[shape];
if (item) {
this.options[shape] = deepExtend(this.options[shape] ?? {}, item);
}
}
}
loadProperty(this, "close", data.close);
loadProperty(this, "type", data.type);
}
}
class ZIndex extends ValueWithRandom {
opacityRate = 1;
sizeRate = 1;
velocityRate = 1;
doLoad(data) {
super.doLoad(data);
loadProperty(this, "opacityRate", data.opacityRate);
loadProperty(this, "sizeRate", data.sizeRate);
loadProperty(this, "velocityRate", data.velocityRate);
}
}
class ParticlesOptions extends OptionLoader {
bounce = new ParticlesBounce();
effect = new Effect();
groups = {};
move = new Move();
number = new ParticlesNumber();
paint;
palette;
reduceDuplicates = false;
shape = new Shape();
zIndex = new ZIndex();
#container;
#pluginManager;
constructor(pluginManager, container) {
super();
this.#pluginManager = pluginManager;
this.#container = container;
this.paint = new Paint();
this.paint.color = new AnimatableColor();
this.paint.color.value = "#fff";
this.paint.fill = new Fill();
this.paint.fill.enable = true;
}
doLoad(data) {
if (data.palette) {
this.palette = data.palette;
this.#importPalette(this.palette);
}
if (data.groups !== undefined) {
for (const group of Object.keys(data.groups)) {
if (!(group in data.groups)) {
continue;
}
const item = data.groups[group];
if (item !== undefined) {
this.groups[group] = deepExtend(this.groups[group] ?? {}, item);
}
}
}
if (data.reduceDuplicates !== undefined) {
this.reduceDuplicates = data.reduceDuplicates;
}
this.bounce.load(data.bounce);
this.effect.load(data.effect);
this.move.load(data.move);
this.number.load(data.number);
const paintToLoad = data.paint;
if (paintToLoad) {
if (isArray(paintToLoad)) {
this.paint = executeOnSingleOrMultiple(paintToLoad, t => {
const tmp = new Paint();
tmp.load(t);
return tmp;
});
}
else if (isArray(this.paint)) {
this.paint = new Paint();
this.paint.load(paintToLoad);
}
else {
this.paint.load(paintToLoad);
}
}
this.shape.load(data.shape);
this.zIndex.load(data.zIndex);
if (this.#container) {
for (const plugin of this.#pluginManager.plugins) {
if (plugin.loadParticlesOptions) {
plugin.loadParticlesOptions(this.#container, this, data);
}
}
const updaters = this.#pluginManager.updaters.get(this.#container);
if (updaters) {
for (const updater of updaters) {
if (updater.loadOptions) {
updater.loadOptions(this, data);
}
}
}
}
}
#importPalette(palette) {
const paletteData = this.#pluginManager.getPalette(palette);
if (!paletteData) {
return;
}
const paletteColors = paletteData.colors, defaultPaintStrokeWidth = 0, defaultPaintVariantsLength = 1, firstPaintVariantIndex = 0, defaultPalettePaintVariant = {}, colorVariants = isArray(paletteColors) ? paletteColors : [paletteColors], palettePaintVariants = colorVariants.flatMap(variant => {
const paletteFill = variant.fill, paletteStroke = variant.stroke, fillPart = paletteFill
? {
color: {
value: paletteFill.value,
},
enable: paletteFill.enable,
opacity: paletteFill.opacity,
}
: undefined;
if (!paletteStroke) {
return [
{
fill: fillPart,
},
];
}
return [
{
fill: fillPart,
stroke: {
color: {
value: paletteStroke.value,
},
opacity: paletteStroke.opacity,
width: paletteStroke.width || defaultPaintStrokeWidth,
},
},
];
}), palettePaint = palettePaintVariants.length > defaultPaintVariantsLength
? palettePaintVariants
: (palettePaintVariants[firstPaintVariantIndex] ?? defaultPalettePaintVariant);
this.load({
paint: palettePaint,
blend: {
enable: true,
mode: paletteData.blendMode,
},
});
}
}
function loadParticlesOptions(pluginManager, container, ...sourceOptionsArr) {
const options = new ParticlesOptions(pluginManager, container);
loadOptions(options, ...sourceOptionsArr);
return options;
}
class Options extends OptionLoader {
autoPlay = true;
background;
clear = true;
defaultThemes = {};
delay = 0;
detectRetina = true;
duration = 0;
fpsLimit = 120;
fullScreen;
hdr;
key;
name;
palette;
particles;
pauseOnBlur = true;
pauseOnOutsideViewport = true;
preset;
resize;
smooth = false;
style = {};
zLayers = 100;
#container;
#pluginManager;
constructor(pluginManager, container) {
super();
this.#pluginManager = pluginManager;
this.#container = container;
this.background = new Background();
this.fullScreen = new FullScreen();
this.hdr = new HDROptions();
this.particles = loadParticlesOptions(this.#pluginManager, this.#container);
this.resize = new ResizeEvent();
}
doLoad(data) {
if (data.preset !== undefined) {
this.preset = data.preset;
executeOnSingleOrMultiple(this.preset, preset => {
this.#importPreset(preset);
});
}
if (data.palette !== undefined) {
this.palette = data.palette;
this.#importPalette(this.palette);
}
loadProperty(this, "autoPlay", data.autoPlay);
loadProperty(this, "clear", data.clear);
loadProperty(this, "key", data.key);
loadProperty(this, "name", data.name);
loadRangeProperty(this, "delay", data.delay);
loadProperty(this, "detectRetina", data.detectRetina);
loadRangeProperty(this, "duration", data.duration);
loadProperty(this, "fpsLimit", data.fpsLimit);
const hdrData = data.hdr;
if (isBoolean(hdrData)) {
this.hdr.enable = hdrData;
this.hdr.mode = exports.HdrMode.standard;
this.hdr.peakNits = 400;
}
else {
this.hdr.load(hdrData);
}
loadProperty(this, "pauseOnBlur", data.pauseOnBlur);
loadProperty(this, "pauseOnOutsideViewport", data.pauseOnOutsideViewport);
loadProperty(this, "zLayers", data.zLayers);
this.background.load(data.background);
const fullScreen = data.fullScreen;
if (isBoolean(fullScreen)) {
this.fullScreen.enable = fullScreen;
}
else {
this.fullScreen.load(fullScreen);
}
this.particles.load(data.particles);
this.resize.load(data.resize);
this.style = deepExtend(this.style, data.style);
loadProperty(this, "smooth", data.smooth);
this.#pluginManager.plugins.forEach(plugin => {
plugin.loadOptions(this.#container, this, data);
});
}
#importPalette(palette) {
const paletteData = this.#pluginManager.getPalette(palette);
if (!paletteData) {
return;
}
this.load({
background: {
color: paletteData.background,
},
blend: {
enable: true,
mode: paletteData.blendMode,
},
particles: {
palette,
},
});
}
#importPreset(preset) {
this.load(this.#pluginManager.getPreset(preset));
}
}
const styleCache = new Map(), maxStyleCacheSize = 2000, rgbFixedPrecision = 2, hslFixedPrecision = 2, hdrRgbFixedPrecision = 4, hdrHslFixedPrecision = 4, sdrReferenceWhiteNits = 203, acesA = 2.51, acesB = 0.03, acesC = 2.43, acesD = 0.59, acesE = 0.14, saturationBoost = 1.15, lightnessBoost = 1.05, temperatureR = 1.05, temperatureB = 0.95, contrastFactor = 1.1, luminanceR = 0.2126, luminanceG = 0.7152, luminanceB = 0.0722, dynamicLuminanceFactor = 2, channelCount = 3, srgbThreshold = 0.04045, srgbDivisor = 12.92, srgbOffset = 0.055, srgbScale = 1.055, srgbExponent = 2.4, linearThreshold = 0.0031308, linearScale = 12.92;
function getCachedStyle(key, generator) {
let cached = styleCache.get(key);
if (!cached) {
cached = generator();
if (styleCache.size > maxStyleCacheSize) {
styleCache.clear();
}
styleCache.set(key, cached);
}
return cached;
}
function acesFilmic(x) {
return clamp((x * (acesA * x + acesB)) / (x * (acesC * x + acesD) + acesE), none, one);
}
function srgbToLinear(c) {
return c <= srgbThreshold ? c / srgbDivisor : Math.pow((c + srgbOffset) / srgbScale, srgbExponent);
}
function linearToSrgb(c) {
return c <= linearThreshold ? c * linearScale : srgbScale * Math.pow(c, one / srgbExponent) - srgbOffset;
}
function applyHdrModeAdjustments(r, g, b, mode, maxChannel) {
switch (mode) {
case exports.HdrMode.vivid: {
const avg = (r + g + b) / channelCount;
return {
b: clamp(clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
g: clamp(clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
r: clamp(clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, none, maxChannel),
};
}
case exports.HdrMode.cinematic: {
const tempR = r * temperatureR, tempB = b * temperatureB, avg = (tempR + g + tempB) / channelCount;
return {
b: clamp(avg + (tempB - avg) * contrastFactor, none, maxChannel),
g: clamp(avg + (g - avg) * contrastFactor, none, maxChannel),
r: clamp(avg + (tempR - avg) * contrastFactor, none, maxChannel),
};
}
case exports.HdrMode.dynamic: {
const luminance = luminanceR * r + luminanceG * g + luminanceB * b, vividWeight = Math.min(one, luminance * dynamicLuminanceFactor), naturalWeight = one - vividWeight, avg = (r + g + b) / channelCount, vividR = clamp(avg + (r - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividG = clamp(avg + (g - avg) * saturationBoost, none, maxChannel) * lightnessBoost, vividB = clamp(avg + (b - avg) * saturationBoost, none, maxChannel) * lightnessBoost;
return {
b: clamp(b * naturalWeight + vividB * vividWeight, none, maxChannel),
g: clamp(g * naturalWeight + vividG * vividWeight, none, maxChannel),
r: clamp(r * naturalWeight + vividR * vividWeight, none, maxChannel),
};
}
case exports.HdrMode.standard:
return { b, g, r };
case exports.HdrMode.natural:
default:
return { b, g, r };
}
}
function stringToRgba(pluginManager, input) {
if (!input) {
return;
}
for (const manager of pluginManager.colorManagers.values()) {
if (manager.accepts(input)) {
return manager.parseString(input);
}
}
return undefined;
}
function rangeColorToRgb(pluginManager, input, index, useIndex = true) {
if (!input) {
return;
}
const color = isString(input) ? { value: input } : input;
if (isString(color.value)) {
return colorToRgb(pluginManager, color.value, index, useIndex);
}
if (isArray(color.value)) {
const value = itemFromArray(color.value, index, useIndex);
if (!value) {
return;
}
return rangeColorToRgb(pluginManager, {
value,
});
}
for (const manager of pluginManager.colorManagers.values()) {
const res = manager.handleRangeColor(color);
if (res) {
return res;
}
}
return undefined;
}
function colorToRgb(pluginManager, input, index, useIndex = true) {
if (!input) {
return;
}
const color = isString(input) ? { value: input } : input;
if (isString(color.value)) {
return color.value === randomColorValue ? getRandomRgbColor() : stringToRgb(pluginManager, color.value);
}
if (isArray(color.value)) {
const value = itemFromArray(color.value, index, useIndex);
if (!value) {
return;
}
return colorToRgb(pluginManager, {
value,
});
}
for (const manager of pluginManager.colorManagers.values()) {
const res = manager.handleColor(color);
if (res) {
return res;
}
}
return undefined;
}
function rangeColorToHsl(pluginManager, color, index, useIndex = true) {
const rgb = rangeColorToRgb(pluginManager, color, index, useIndex);
return rgb ? rgbToHsl(rgb) : undefined;
}
function rgbToHsl(color) {
const r1 = color.r / rgbMax, g1 = color.g / rgbMax, b1 = color.b / rgbMax, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
h: hMin,
l: (max + min) * half,
s: sMin,
};
if (max !== min) {
res.s = res.l < half ? (max - min) / (max + min) : (max - min) / (double - max - min);
if (r1 === max) {
res.h = (g1 - b1) / (max - min);
}
else if (g1 === max) {
res.h = double + (b1 - r1) / (max - min);
}
else {
res.h = double * double + (r1 - g1) / (max - min);
}
}
res.l *= lMax;
res.s *= sMax;
res.h *= hPhase;
if (res.h < hMin) {
res.h += hMax;
}
if (res.h >= hMax) {
res.h -= hMax;
}
return res;
}
function stringToAlpha(pluginManager, input) {
return stringToRgba(pluginManager, input)?.a;
}
function stringToRgb(pluginManager, input) {
return stringToRgba(pluginManager, input);
}
function hslChannel(temp1, temp2, temp3) {
const temp3Min = 0, temp3Max = 1;
if (temp3 < temp3Min) {
temp3++;
}
if (temp3 > temp3Max) {
temp3--;
}
if (temp3 * sextuple < temp3Max) {
return temp1 + (temp2 - temp1) * sextuple * temp3;
}
if (temp3 * double < temp3Max) {
return temp2;
}
if (temp3 * triple < temp3Max * double) {
const temp3Offset = double / triple;
return temp1 + (temp2 - temp1) * (temp3Offset - temp3) * sextuple;
}
return temp1;
}
function hslToRgb(hsl) {
const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
if (s === sMin) {
const grayscaleValue = Math.round(lNormalized * rgbMax);
return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
}
const temp1 = lNormalized < half
? lNormalized * (sNormalizedOffset + sNormalized)
: lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
}
function hslToRgbFloat(hsl) {
const h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax;
if (s === sMin) {
const grayscaleValue = lNormalized * rgbMax;
return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
}
const temp1 = lNormalized < half
? lNormalized * (sNormalizedOffset + sNormalized)
: lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseThird = phaseNumerator / triple, red = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized)), blue = Math.min(rgbMax, rgbMax * hslChannel(temp2, temp1, hNormalized - phaseThird));
return { r: red, g: green, b: blue };
}
function hslaToRgba(hsla) {
const rgbResult = hslToRgb(hsla);
return {
a: hsla.a,
b: rgbResult.b,
g: rgbResult.g,
r: rgbResult.r,
};
}
function getRandomRgbColor(min, hdr) {
const fixedMin = min ?? defaultRgbMin;
if (hdr) {
return {
r: getRandomInRange(fixedMin, rgbMax),
g: getRandomInRange(fixedMin, rgbMax),
b: getRandomInRange(fixedMin, rgbMax),
};
}
const fixedMax = rgbMax + identity$1, getRgbInRangeValue = () => Math.floor(getRandomInRange(fixedMin, fixedMax));
return {
b: getRgbInRangeValue(),
g: getRgbInRangeValue(),
r: getRgbInRangeValue(),
};
}
function getStyleFromRgb(color, hdr, opacity, peakNits, mode) {
const rgbPrecision = hdr ? hdrRgbFixedPrecision : rgbFixedPrecision, op = opacity ?? defaultOpacity$1, key = hdr
? `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? exports.HdrMode.standard}`
: `rgb-${color.r.toFixed(rgbPrecision)}-${color.g.toFixed(rgbPrecision)}-${color.b.toFixed(rgbPrecision)}-sdr-${op.toString()}`;
return getCachedStyle(key, () => hdr ? getHdrStyleFromRgb(color, opacity, peakNits, mode) : getSdrStyleFromRgb(color, opacity));
}
function getHdrStyleFromRgb(color, opacity, peakNits = maxNits, mode = exports.HdrMode.standard) {
const headroom = peakNits / sdrReferenceWhiteNits, middleGray = 0.18, mapped = hdrToneMapColor(color, headroom, middleGray, mode);
return `color(display-p3 ${mapped.r.toString()} ${mapped.g.toString()} ${mapped.b.toString()} / ${(opacity ?? defaultOpacity$1).toString()})`;
}
function hdrToneMapColor(color, headroom, middleGray, mode) {
const rNorm = color.r / rgbMax, gNorm = color.g / rgbMax, bNorm = color.b / rgbMax;
if (mode !== exports.HdrMode.natural && mode !== exports.HdrMode.vivid && mode !== exports.HdrMode.cinematic && mode !== exports.HdrMode.dynamic) {
return { b: bNorm, g: gNorm, r: rNorm };
}
const maxChannel = Math.max(one, headroom), luminance = luminanceR * rNorm + luminanceG * gNorm + luminanceB * bNorm, mappedLuminance = hdrToneMap(luminance, headroom, middleGray), scale = luminance > none ? mappedLuminance / luminance : one;
return applyHdrModeAdjustments(clamp(rNorm * scale, none, maxChannel), clamp(gNorm * scale, none, maxChannel), clamp(bNorm * scale, none, maxChannel), mode, maxChannel);
}
function hdrToneMap(normalized, headroom, middleGray) {
if (normalized <= middleGray) {
return acesFilmic(normalized);
}
const expanded = normalized + (normalized - middleGray) * (headroom - one);
return acesFilmic(expanded);
}
function getSdrStyleFromRgb(color, opacity) {
return `rgba(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()}, ${(opacity ?? defaultOpacity$1).toString()})`;
}
function getStyleFromHsl(color, hdr, opacity, peakNits, mode) {
const hslPrecision = hdr ? hdrHslFixedPrecision : hslFixedPrecision, op = opacity ?? defaultOpacity$1, key = hdr
? `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-hdr-${op.toString()}-${(peakNits ?? maxNits).toString()}-${mode ?? exports.HdrMode.standard}`
: `hsl-${color.h.toFixed(hslPrecision)}-${color.s.toFixed(hslPrecision)}-${color.l.toFixed(hslPrecision)}-sdr-${op.toString()}`;
return getCachedStyle(key, () => hdr
? getStyleFromRgb(hslToRgbFloat(color), true, opacity, peakNits, mode)
: `hsla(${color.h.toString()}, ${color.s.toString()}%, ${color.l.toString()}%, ${op.toString()})`);
}
function colorMix(color1, color2, size1, size2, hdr) {
let rgb1 = color1, rgb2 = color2;
if (!("r" in rgb1)) {
rgb1 = hdr ? hslToRgbFloat(color1) : hslToRgb(color1);
}
if (!("r" in rgb2)) {
rgb2 = hdr ? hslToRgbFloat(color2) : hslToRgb(color2);
}
if (hdr) {
const lin1 = {
b: srgbToLinear(rgb1.b / rgbMax),
g: srgbToLinear(rgb1.g / rgbMax),
r: srgbToLinear(rgb1.r / rgbMax),
}, lin2 = {
b: srgbToLinear(rgb2.b / rgbMax),
g: srgbToLinear(rgb2.g / rgbMax),
r: srgbToLinear(rgb2.r / rgbMax),
}, totalWeight = size1 + size2;
return {
b: linearToSrgb((lin1.b * size1 + lin2.b * size2) / totalWeight) * rgbMax,
g: linearToSrgb((lin1.g * size1 + lin2.g * size2) / totalWeight) * rgbMax,
r: linearToSrgb((lin1.r * size1 + lin2.r * size2) / totalWeight) * rgbMax,
};
}
return {
b: mix(rgb1.b, rgb2.b, size1, size2),
g: mix(rgb1.g, rgb2.g, size1, size2),
r: mix(rgb1.r, rgb2.r, size1, size2),
};
}
function getLinkColor(p1, p2, linkColor, hdr) {
if (linkColor === randomColorValue) {
return getRandomRgbColor();
}
else if (linkColor === midColorValue) {
const sourceColor = p1.getFillColor() ?? p1.getStrokeColor(), destColor = p2?.getFillColor() ?? p2?.getStrokeColor();
if (sourceColor && destColor && p2) {
return colorMix(sourceColor, destColor, p1.getRadius(), p2.getRadius(), hdr);
}
else {
const hslColor = sourceColor ?? destColor;
if (hslColor) {
return hslToRgb(hslColor);
}
}
}
else {
return linkColor;
}
return undefined;
}
function getLinkRandomColor(pluginManager, optColor, blink, consent) {
const color = isString(optColor) ? optColor : optColor.value;
if (color === randomColorValue) {
if (consent) {
return rangeColorToRgb(pluginManager, {
value: color,
});
}
if (blink) {
return randomColorValue;
}
return midColorValue;
}
else if (color === midColorValue) {
return midColorValue;
}
else {
return rangeColorToRgb(pluginManager, {
value: color,
});
}
}
function getHslFromAnimation(animation) {
return animation === undefined
? undefined
: {
h: animation.h.value,
s: animation.s.value,
l: animation.l.value,
};
}
function getHslAnimationFromHsl(hsl, animationOptions, reduceFactor) {
const resColor = {
h: {
enable: false,
value: hsl.h,
min: hMin,
max: hMax,
},
s: {
enable: false,
value: hsl.s,
min: sMin,
max: sMax,
},
l: {
enable: false,
value: hsl.l,
min: lMin,
max: lMax,
},
};
if (animationOptions) {
setColorAnimation(resColor.h, animationOptions.h, reduceFactor);
setColorAnimation(resColor.s, animationOptions.s, reduceFactor);
setColorAnimation(resColor.l, animationOptions.l, reduceFactor);
}
return resColor;
}
function setColorAnimation(colorValue, colorAnimation, reduceFactor) {
colorValue.enable = colorAnimation.enable;
colorValue.min = colorAnimation.min;
colorValue.max = colorAnimation.max;
if (colorValue.enable) {
colorValue.velocity = (getRangeValue(colorAnimation.speed) / percentDenominator) * reduceFactor;
colorValue.decay = decayOffset - getRangeValue(colorAnimation.decay);
colorValue.status = exports.AnimationStatus.increasing;
colorValue.loops = defaultLoops;
colorValue.maxLoops = getRangeValue(colorAnimation.count);
colorValue.time = defaultTime;
colorValue.delayTime = getRangeValue(colorAnimation.delay) * millisecondsToSeconds;
if (!colorAnimation.sync) {
colorValue.velocity *= getRandom();
colorValue.value *= getRandom();
}
colorValue.initialValue = colorValue.value;
colorValue.offset = setRangeValue(colorAnimation.offset);
}
else {
colorValue.velocity = defaultVelocity;
}
}
function updateColorValue(data, decrease, delta, _hdr) {
const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
if (!data.enable ||
((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
return;
}
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;
}
const offset = data.offset ? randomInRangeValue(data.offset) : minOffset, velocity = (data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor, decay = data.decay ?? identity, max = data.max, min = data.min;
if (!decrease || data.status === exports.AnimationStatus.increasing) {
data.value += velocity;
if (data.value > max) {
data.loops ??= 0;
data.loops++;
if (decrease) {
data.status = exports.AnimationStatus.decreasing;
}
else {
data.value -= max;
}
}
}
else {
data.value -= velocity;
if (data.value < min) {
data.loops ??= 0;
data.loops++;
data.status = exports.AnimationStatus.increasing;
}
}
if (data.velocity && decay !== identity) {
data.velocity *= decay;
}
data.value = clamp(data.value, min, max);
}
function updateColor(color, delta, hdr) {
if (!color) {
return;
}
const { h, s, l } = color;
updateColorValue(h, false, delta);
updateColorValue(s, true, delta);
updateColorValue(l, true, delta);
}
function alterHsl(color, type, value) {
return {
h: color.h,
s: color.s,
l: color.l + (type === exports.AlterType.darken ? -lFactor : lFactor) * value,
};
}
const tsParticles = initEngine();
class FireworkOptions {
background;
brightness;
colors;
gravity;
minHeight;
rate;
saturation;
sounds;
speed;
splitCount;
constructor() {
this.background = "none";
this.brightness = {
min: -30,
max: 30,
};
this.colors = [
"#FF0000",
"#FF2A00",
"#FF5500",
"#FF8000",
"#FFAA00",
"#FFD400",
"#FFFF00",
"#D4FF00",
"#AAFF00",
"#80FF00",
"#55FF00",
"#2AFF00",
"#00FF00",
"#00FF2A",
"#00FF55",
"#00FF80",
"#00FFAA",
"#00FFD4",
"#00FFFF",
"#00D4FF",
"#00AAFF",
"#0080FF",
"#0055FF",
"#002AFF",
"#0000FF",
"#2A00FF",
"#5500FF",
"#8000FF",
"#AA00FF",
"#D400FF",
"#FF00FF",
"#FF00D4",
"#FF00AA",
"#FF0080",
"#FF0055",
"#FF002A",
];
this.gravity = 30;
this.minHeight = {
min: 10,
max: 30,
};
this.rate = 10;
this.saturation = {
min: -30,
max: 30,
};
this.sounds = true;
this.speed = { min: 20, max: 40 };
this.splitCount = 100;
}
load(data) {
if (isNull(data)) {
return;
}
if (data.colors !== undefined) {
if (isArray(data.colors)) {
this.colors = [...data.colors];
}
else {
this.colors = data.colors;
}
}
loadProperty(this, "background", data.background);
loadRangeProperty(this, "brightness", data.brightness);
loadRangeProperty(this, "gravity", data.gravity);
loadRangeProperty(this, "minHeight", data.minHeight);
loadRangeProperty(this, "rate", data.rate);
loadRangeProperty(this, "saturation", data.saturation);
loadProperty(this, "sounds", data.sounds);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "splitCount", data.splitCount);
}
}
const instances = new Map();
function deleteFireworksInstance(id) {
instances.delete(id);
}
const explodeSoundCheck = (args) => {
const data = args.data;
return data?.particle?.options.move.gravity.enable ?? false;
};
function getOptions(options, canvas) {
const identity = 1;
return {
detectRetina: true,
background: {
color: options.background,
},
blend: {
enable: true,
mode: "lighter",
},
fullScreen: {
enable: !canvas,
},
fpsLimit: 60,
emitters: {
direction: exports.MoveDirection.top,
life: {
count: 0,
duration: 0.1,
delay: 0.1,
},
rate: {
delay: isNumber(options.rate)
? identity / options.rate
: { min: identity / getRangeMin(options.rate), max: identity / getRangeMax(options.rate) },
quantity: 1,
},
size: {
width: 100,
height: 0,
},
position: {
y: 100,
x: 50,
},
},
particles: {
number: {
value: 0,
},
paint: {
fill: {
enable: false,
},
stroke: {
color: {
value: options.colors,
},
width: 2,
},
},
destroy: {
mode: "split",
bounds: {
top: setRangeValue(options.minHeight),
},
split: {
count: 1,
factor: {
value: 0.333333,
},
rate: {
value: options.splitCount,
},
strokeColorOffset: {
s: options.saturation,
l: options.brightness,
},
particles: {
group: "split",
number: {
value: 0,
},
opacity: {
value: {
min: 0.1,
max: 1,
},
animation: {
enable: true,
speed: { min: 2, max: 4 },
sync: true,
startValue: exports.StartValueType.max,
destroy: exports.DestroyType.min,
count: 1,
},
},
size: {
value: { min: 5, max: 10 },
},
life: {
count: 1,
duration: {
value: {
min: 0.5,
max: 1,
},
},
},
move: {
decay: 0.05,
enable: true,
gravity: {
enable: false,
},
speed: {
min: 10,
max: 25,
},
direction: "outside",
outModes: exports.OutMode.destroy,
},
},
},
},
life: {
count: 1,
},
shape: {
type: "line",
options: {
line: {
cap: "round",
},
},
},
size: {
value: { min: 10, max: 20 },
},
rotate: {
path: true,
},
move: {
enable: true,
gravity: {
acceleration: setRangeValue(options.gravity),
enable: true,
inverse: true,
maxSpeed: 150,
},
speed: setRangeValue(options.speed),
outModes: {
default: exports.OutMode.destroy,
top: exports.OutMode.none,
},
},
},
sounds: {
enable: options.sounds,
events: [
{
event: exports.EventType.particleRemoved,
filter: explodeSoundCheck,
audio: [
"https://particles.js.org/audio/explosion0.mp3",
"https://particles.js.org/audio/explosion1.mp3",
"https://particles.js.org/audio/explosion2.mp3",
],
},
],
volume: 50,
},
};
}
async function getFireworksInstance(engine, id, sourceOptions, canvas) {
const existing = instances.get(id);
if (existing instanceof Promise) {
return existing;
}
if (existing) {
if (!existing.destroyed) {
return existing;
}
instances.delete(id);
}
const create = async () => {
const options = new FireworkOptions();
options.load(sourceOptions);
const particlesOptions = getOptions(options, canvas), container = await engine.load({ id, element: canvas, options: particlesOptions });
if (!container) {
instances.delete(id);
return;
}
const { FireworksInstance } = await Promise.resolve().then(function () { return FireworksInstance$1; }), instance = new FireworksInstance(container, id);
instances.set(id, instance);
return instance;
}, createPromise = create();
instances.set(id, createPromise);
return createPromise;
}
class Blend {
enable = false;
mode = "destination-out";
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "mode", data.mode);
loadProperty(this, "enable", data.enable);
}
}
class BlendPlugin {
id = "blend";
async getPlugin(container) {
const { BlendPluginInstance } = await Promise.resolve().then(function () { return BlendPluginInstance$1; });
return new BlendPluginInstance(container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let blendOptions = options.blend;
if (!blendOptions?.load) {
options.blend = blendOptions = new Blend();
}
blendOptions.load(source?.blend);
}
loadParticlesOptions(_container, options, source) {
options.blend ??= new Blend();
options.blend.load(source?.blend);
}
needsPlugin(options) {
return !!options?.blend?.enable || !!options?.particles?.blend?.enable;
}
}
async function loadBlendPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new BlendPlugin());
});
}
const minAngle$1 = 0;
function drawCircle(data) {
const { context, particle, radius } = data;
particle.circleRange ??= { min: minAngle$1, max: doublePI };
const circleRange = particle.circleRange;
context.arc(originPoint.x, originPoint.y, radius, circleRange.min, circleRange.max, false);
}
const sides$2 = 12, maxAngle = 360, minAngle = 0;
class CircleDrawer {
draw(data) {
drawCircle(data);
}
getSidesCount() {
return sides$2;
}
particleInit(_container, particle) {
const shapeData = particle.shapeData, angle = shapeData?.angle ?? {
max: maxAngle,
min: minAngle,
};
particle.circleRange = isObject(angle)
? { min: degToRad(angle.min), max: degToRad(angle.max) }
: {
min: minAngle,
max: degToRad(angle),
};
}
}
async function loadCircleShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["circle"], () => {
return Promise.resolve(new CircleDrawer());
});
});
}
var RgbIndexes$1;
(function (RgbIndexes) {
RgbIndexes[RgbIndexes["r"] = 1] = "r";
RgbIndexes[RgbIndexes["g"] = 2] = "g";
RgbIndexes[RgbIndexes["b"] = 3] = "b";
RgbIndexes[RgbIndexes["a"] = 4] = "a";
})(RgbIndexes$1 || (RgbIndexes$1 = {}));
const shorthandHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, hexRadix = 16, defaultAlpha = 1, alphaFactor = 0xff;
class HexColorManager {
accepts(input) {
return input.startsWith("#");
}
handleColor(color) {
return this.#parseString(color.value);
}
handleRangeColor(color) {
return this.#parseString(color.value);
}
parseString(input) {
return this.#parseString(input);
}
#parseString(hexColor) {
if (typeof hexColor !== "string" || !this.accepts(hexColor)) {
return;
}
const hexFixed = hexColor.replace(shorthandHexRegex, (_, r, g, b, a) => {
return r + r + g + g + b + b + (a === undefined ? "" : a + a);
}), result = hexRegex.exec(hexFixed);
return result
? {
a: result[RgbIndexes$1.a] ? Number.parseInt(result[RgbIndexes$1.a], hexRadix) / alphaFactor : defaultAlpha,
b: Number.parseInt(result[RgbIndexes$1.b] ?? "0", hexRadix),
g: Number.parseInt(result[RgbIndexes$1.g] ?? "0", hexRadix),
r: Number.parseInt(result[RgbIndexes$1.r] ?? "0", hexRadix),
}
: undefined;
}
}
async function loadHexColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hex", new HexColorManager());
});
}
var HslIndexes;
(function (HslIndexes) {
HslIndexes[HslIndexes["h"] = 1] = "h";
HslIndexes[HslIndexes["s"] = 2] = "s";
HslIndexes[HslIndexes["l"] = 3] = "l";
HslIndexes[HslIndexes["a"] = 5] = "a";
})(HslIndexes || (HslIndexes = {}));
const hslRegex = /hsla?\(\s*(\d+)\s*[\s,]\s*(\d+)%\s*[\s,]\s*(\d+)%\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i;
class HslColorManager {
accepts(input) {
return input.startsWith("hsl");
}
handleColor(color) {
const colorValue = color.value, hslColor = colorValue.hsl ?? color.value;
if (!("h" in hslColor) || !("s" in hslColor) || !("l" in hslColor)) {
return;
}
return hslToRgb(hslColor);
}
handleRangeColor(color) {
const colorValue = color.value, hslColor = colorValue.hsl ?? color.value;
if (!("h" in hslColor) || !("s" in hslColor) || !("l" in hslColor)) {
return;
}
return hslToRgb({
h: getRangeValue(hslColor.h),
l: getRangeValue(hslColor.l),
s: getRangeValue(hslColor.s),
});
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = hslRegex.exec(input), minLength = 4, defaultAlpha = 1, radix = 10;
return result
? hslaToRgba({
a: result.length > minLength ? parseAlpha(result[HslIndexes.a]) : defaultAlpha,
h: Number.parseInt(result[HslIndexes.h] ?? "0", radix),
l: Number.parseInt(result[HslIndexes.l] ?? "0", radix),
s: Number.parseInt(result[HslIndexes.s] ?? "0", radix),
})
: undefined;
}
}
async function loadHslColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("hsl", new HslColorManager());
});
}
class MovePlugin {
id = "move";
#pluginManager;
constructor(pluginManager) {
this.#pluginManager = pluginManager;
}
async getPlugin(container) {
const { MovePluginInstance } = await Promise.resolve().then(function () { return MovePluginInstance$1; });
return new MovePluginInstance(this.#pluginManager, container);
}
loadOptions() {
}
needsPlugin() {
return true;
}
}
async function loadMovePlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
const moveEngine = e, movePluginManager = moveEngine.pluginManager;
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.pathGenerators ??= new Map();
movePluginManager.addPathGenerator = (name, generator) => {
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.initializers.pathGenerators.set(name, generator);
};
movePluginManager.getPathGenerators = async (container, force = false) => {
movePluginManager.initializers.pathGenerators ??= new Map();
movePluginManager.pathGenerators ??= new Map();
return getItemMapFromInitializer(container, movePluginManager.pathGenerators, movePluginManager.initializers.pathGenerators, force);
};
e.pluginManager.addPlugin(new MovePlugin(e.pluginManager));
});
}
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 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 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;
const delayTime = data.delayTime ?? minDelay;
if (delayTime > minDelay && data.time < delayTime) {
data.time += delta.value;
if (data.time < delayTime) {
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);
}
}
class OpacityAnimation extends RangedAnimationOptions {
destroy = exports.DestroyType.none;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "destroy", data.destroy);
}
}
class Opacity extends RangedAnimationValueWithRandom {
animation = new OpacityAnimation();
value = 1;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
const animation = data.animation;
if (animation !== undefined) {
this.animation.load(animation);
}
}
}
class OpacityUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const opacityOptions = particle.options.opacity, pxRatio = 1;
if (!opacityOptions) {
return;
}
particle.opacity = initParticleNumericAnimationValue(opacityOptions, pxRatio);
const opacityAnimation = opacityOptions.animation;
if (opacityAnimation.enable) {
particle.opacity.velocity =
(getRangeValue(opacityAnimation.speed) / percentDenominator) * this.#container.retina.reduceFactor;
if (!opacityAnimation.sync) {
particle.opacity.velocity *= getRandom();
}
}
}
isEnabled(particle) {
const none = 0;
return (!particle.destroyed &&
!particle.spawning &&
!!particle.opacity &&
particle.opacity.enable &&
((particle.opacity.maxLoops ?? none) <= none ||
((particle.opacity.maxLoops ?? none) > none &&
(particle.opacity.loops ?? none) < (particle.opacity.maxLoops ?? none))));
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "opacity", Opacity, ...sources);
}
reset(particle) {
if (!particle.opacity) {
return;
}
particle.opacity.time = 0;
particle.opacity.loops = 0;
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.opacity || !particle.options.opacity) {
return;
}
updateAnimation(particle, particle.opacity, true, particle.options.opacity.animation.destroy, delta);
}
}
async function loadOpacityUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("opacity", container => {
return Promise.resolve(new OpacityUpdater(container));
});
});
}
const boundsMin = 0;
function bounceHorizontal(data) {
if ((data.outMode !== exports.OutMode.bounce && data.outMode !== exports.OutMode.split) ||
(data.direction !== exports.OutModeDirection.left && data.direction !== exports.OutModeDirection.right)) {
return;
}
if (data.bounds.right < boundsMin && data.direction === exports.OutModeDirection.left) {
data.particle.position.x = data.size + data.offset.x;
}
else if (data.bounds.left > data.canvasSize.width && data.direction === exports.OutModeDirection.right) {
data.particle.position.x = data.canvasSize.width - data.size - data.offset.x;
}
const velocity = data.particle.velocity.x;
let bounced = false;
if (data.outOfCanvas &&
((data.direction === exports.OutModeDirection.right && velocity > minVelocity) ||
(data.direction === exports.OutModeDirection.left && velocity < minVelocity))) {
const newVelocity = getRangeValue(data.particle.options.bounce.horizontal.value);
data.particle.velocity.x *= -newVelocity;
bounced = true;
}
if (!bounced) {
return;
}
const minPos = data.offset.x + data.size;
if (data.outOfCanvas && data.direction === exports.OutModeDirection.right) {
data.particle.position.x = data.canvasSize.width - minPos;
}
else if (data.outOfCanvas && data.direction === exports.OutModeDirection.left) {
data.particle.position.x = minPos;
}
if (data.outMode === exports.OutMode.split) {
data.particle.destroy();
}
}
function bounceVertical(data) {
if ((data.outMode !== exports.OutMode.bounce && data.outMode !== exports.OutMode.split) ||
(data.direction !== exports.OutModeDirection.bottom && data.direction !== exports.OutModeDirection.top)) {
return;
}
if (data.bounds.bottom < boundsMin && data.direction === exports.OutModeDirection.top) {
data.particle.position.y = data.size + data.offset.y;
}
else if (data.bounds.top > data.canvasSize.height && data.direction === exports.OutModeDirection.bottom) {
data.particle.position.y = data.canvasSize.height - data.size - data.offset.y;
}
const velocity = data.particle.velocity.y;
let bounced = false;
if (data.outOfCanvas &&
((data.direction === exports.OutModeDirection.bottom && velocity > minVelocity) ||
(data.direction === exports.OutModeDirection.top && velocity < minVelocity))) {
const newVelocity = getRangeValue(data.particle.options.bounce.vertical.value);
data.particle.velocity.y *= -newVelocity;
bounced = true;
}
if (!bounced) {
return;
}
const minPos = data.offset.y + data.size;
if (data.outOfCanvas && data.direction === exports.OutModeDirection.bottom) {
data.particle.position.y = data.canvasSize.height - minPos;
}
else if (data.outOfCanvas && data.direction === exports.OutModeDirection.top) {
data.particle.position.y = minPos;
}
if (data.outMode === exports.OutMode.split) {
data.particle.destroy();
}
}
class BounceOutMode {
modes;
#container;
#particleBouncePlugins;
constructor(container) {
this.#container = container;
this.modes = [
exports.OutMode.bounce,
exports.OutMode.split,
];
this.#particleBouncePlugins = container.plugins.filter(p => p.particleBounce !== undefined);
}
update(particle, direction, delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
const container = this.#container;
let handled = false;
for (const plugin of this.#particleBouncePlugins) {
handled = plugin.particleBounce?.(particle, delta, direction) ?? false;
if (handled) {
break;
}
}
if (handled) {
return;
}
const pos = particle.getPosition(), offset = particle.offset, size = particle.getRadius(), bounds = calculateBounds(pos, size), canvasSize = container.canvas.size, outOfCanvas = !particle.isInsideCanvasForOutMode(outMode, direction);
bounceHorizontal({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
bounceVertical({ particle, outMode, direction, bounds, canvasSize, offset, outOfCanvas, size });
}
}
class DestroyOutMode {
modes;
constructor(_container) {
this.modes = [exports.OutMode.destroy];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
switch (particle.outType) {
case exports.ParticleOutType.normal:
case exports.ParticleOutType.outside:
if (particle.isInsideCanvasForOutMode(outMode, direction)) {
return;
}
break;
case exports.ParticleOutType.inside: {
const { dx, dy } = getDistances(particle.position, particle.moveCenter), { x: vx, y: vy } = particle.velocity;
if ((vx < minVelocity && dx > particle.moveCenter.radius) ||
(vy < minVelocity && dy > particle.moveCenter.radius) ||
(vx >= minVelocity && dx < -particle.moveCenter.radius) ||
(vy >= minVelocity && dy < -particle.moveCenter.radius)) {
return;
}
break;
}
}
particle.destroy(true);
}
}
class NoneOutMode {
modes;
#container;
constructor(container) {
this.#container = container;
this.modes = [exports.OutMode.none];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
if ((particle.options.move.distance.horizontal &&
(direction === exports.OutModeDirection.left || direction === exports.OutModeDirection.right)) ??
(particle.options.move.distance.vertical &&
(direction === exports.OutModeDirection.top || direction === exports.OutModeDirection.bottom))) {
return;
}
const gravityOptions = particle.options.move.gravity, container = this.#container, canvasSize = container.canvas.size, pRadius = particle.getRadius();
if (!gravityOptions.enable) {
if ((particle.velocity.y > minVelocity && particle.position.y <= canvasSize.height + pRadius) ||
(particle.velocity.y < minVelocity && particle.position.y >= -pRadius) ||
(particle.velocity.x > minVelocity && particle.position.x <= canvasSize.width + pRadius) ||
(particle.velocity.x < minVelocity && particle.position.x >= -pRadius)) {
return;
}
if (!isPointInside(particle.position, container.canvas.size, originPoint, pRadius, direction)) {
particle.destroy();
}
}
else {
const position = particle.position;
if ((!gravityOptions.inverse &&
position.y > canvasSize.height + pRadius &&
direction === exports.OutModeDirection.bottom) ||
(gravityOptions.inverse && position.y < -pRadius && direction === exports.OutModeDirection.top)) {
particle.destroy();
}
}
}
}
const updateVector = Vector.origin;
class OutOutMode {
modes;
#container;
constructor(container) {
this.#container = container;
this.modes = [exports.OutMode.out];
}
update(particle, direction, _delta, outMode) {
if (!this.modes.includes(outMode)) {
return;
}
const container = this.#container;
switch (particle.outType) {
case exports.ParticleOutType.inside: {
const { x: vx, y: vy } = particle.velocity;
updateVector.setTo(originPoint);
updateVector.length = particle.moveCenter.radius;
updateVector.angle = particle.velocity.angle + Math.PI;
updateVector.addTo(particle.moveCenter);
const { dx, dy } = getDistances(particle.position, updateVector);
if ((vx <= minVelocity && dx >= minDistance) ||
(vy <= minVelocity && dy >= minDistance) ||
(vx >= minVelocity && dx <= minDistance) ||
(vy >= minVelocity && dy <= minDistance)) {
return;
}
particle.position.x = Math.floor(randomInRangeValue({
min: 0,
max: container.canvas.size.width,
}));
particle.position.y = Math.floor(randomInRangeValue({
min: 0,
max: container.canvas.size.height,
}));
const { dx: newDx, dy: newDy } = getDistances(particle.position, particle.moveCenter);
particle.direction = Math.atan2(-newDy, -newDx);
particle.velocity.angle = particle.direction;
particle.justWarped = true;
break;
}
default: {
if (particle.isInsideCanvasForOutMode(outMode, direction)) {
return;
}
switch (particle.outType) {
case exports.ParticleOutType.outside: {
particle.position.x =
Math.floor(randomInRangeValue({
min: -particle.moveCenter.radius,
max: particle.moveCenter.radius,
})) + particle.moveCenter.x;
particle.position.y =
Math.floor(randomInRangeValue({
min: -particle.moveCenter.radius,
max: particle.moveCenter.radius,
})) + particle.moveCenter.y;
const { dx, dy } = getDistances(particle.position, particle.moveCenter);
if (particle.moveCenter.radius) {
particle.direction = Math.atan2(dy, dx);
particle.velocity.angle = particle.direction;
}
particle.justWarped = true;
break;
}
case exports.ParticleOutType.normal: {
const warp = particle.options.move.warp, canvasSize = container.canvas.size, newPos = {
bottom: canvasSize.height + particle.getRadius() + particle.offset.y,
left: -particle.getRadius() - particle.offset.x,
right: canvasSize.width + particle.getRadius() + particle.offset.x,
top: -particle.getRadius() - particle.offset.y,
}, sizeValue = particle.getRadius(), nextBounds = calculateBounds(particle.position, sizeValue);
if (direction === exports.OutModeDirection.right && nextBounds.left > canvasSize.width + particle.offset.x) {
particle.position.x = newPos.left;
particle.initialPosition.x = particle.position.x;
if (!warp) {
particle.position.y = getRandom() * canvasSize.height;
particle.initialPosition.y = particle.position.y;
}
particle.justWarped = true;
}
else if (direction === exports.OutModeDirection.left && nextBounds.right < -particle.offset.x) {
particle.position.x = newPos.right;
particle.initialPosition.x = particle.position.x;
if (!warp) {
particle.position.y = getRandom() * canvasSize.height;
particle.initialPosition.y = particle.position.y;
}
particle.justWarped = true;
}
if (direction === exports.OutModeDirection.bottom && nextBounds.top > canvasSize.height + particle.offset.y) {
if (!warp) {
particle.position.x = getRandom() * canvasSize.width;
particle.initialPosition.x = particle.position.x;
}
particle.position.y = newPos.top;
particle.initialPosition.y = particle.position.y;
particle.justWarped = true;
}
else if (direction === exports.OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
if (!warp) {
particle.position.x = getRandom() * canvasSize.width;
particle.initialPosition.x = particle.position.x;
}
particle.position.y = newPos.bottom;
particle.initialPosition.y = particle.position.y;
particle.justWarped = true;
}
break;
}
}
break;
}
}
}
}
const checkOutMode = (outModes, outMode) => {
return (outModes.default === outMode ||
outModes.bottom === outMode ||
outModes.left === outMode ||
outModes.right === outMode ||
outModes.top === outMode);
};
class OutOfCanvasUpdater {
updaters;
#container;
constructor(container) {
this.#container = container;
this.updaters = new Map();
}
init(particle) {
this.#addUpdaterIfMissing(particle, exports.OutMode.bounce, container => new BounceOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.out, container => new OutOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.destroy, container => new DestroyOutMode(container));
this.#addUpdaterIfMissing(particle, exports.OutMode.none, container => new NoneOutMode(container));
}
isEnabled(particle) {
return !particle.destroyed && !particle.spawning;
}
update(particle, delta) {
const outModes = particle.options.move.outModes;
particle.justWarped = false;
this.#updateOutMode(particle, delta, outModes.bottom ?? outModes.default, exports.OutModeDirection.bottom);
this.#updateOutMode(particle, delta, outModes.left ?? outModes.default, exports.OutModeDirection.left);
this.#updateOutMode(particle, delta, outModes.right ?? outModes.default, exports.OutModeDirection.right);
this.#updateOutMode(particle, delta, outModes.top ?? outModes.default, exports.OutModeDirection.top);
}
#addUpdaterIfMissing(particle, outMode, getUpdater) {
const outModes = particle.options.move.outModes;
if (!this.updaters.has(outMode) && checkOutMode(outModes, outMode)) {
this.updaters.set(outMode, getUpdater(this.#container));
}
}
#updateOutMode(particle, delta, outMode, direction) {
for (const updater of this.updaters.values()) {
updater.update(particle, direction, delta, outMode);
}
}
}
async function loadOutModesUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("outModes", container => {
return Promise.resolve(new OutOfCanvasUpdater(container));
});
});
}
const defaultOpacity = 1;
class PaintUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
}
init(particle) {
const container = this.#container, options = particle.options, paint = itemFromSingleOrMultiple(options.paint, particle.id, options.reduceDuplicates), color = paint?.color, paintColor = color ?? undefined, fill = paint?.fill, stroke = paint?.stroke;
if (fill) {
const fillColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), fill.color);
particle.fillEnabled = fill.enable;
particle.fillOpacity = getRangeValue(fill.opacity);
particle.fillAnimation = fillColor.animation;
const fillHslColor = rangeColorToHsl(this.#pluginManager, fillColor);
if (fillHslColor) {
particle.fillColor = getHslAnimationFromHsl(fillHslColor, particle.fillAnimation, container.retina.reduceFactor);
}
}
else {
particle.fillEnabled = false;
particle.fillAnimation = undefined;
particle.fillColor = undefined;
particle.fillOpacity = defaultOpacity;
}
if (stroke) {
const strokeColor = AnimatableColor.create(paintColor === undefined ? undefined : AnimatableColor.create(undefined, paintColor), stroke.color);
particle.strokeWidth = getRangeValue(stroke.width) * container.retina.pixelRatio;
particle.strokeOpacity = getRangeValue(stroke.opacity ?? defaultOpacity);
particle.strokeAnimation = strokeColor.animation;
const strokeHslColor = rangeColorToHsl(this.#pluginManager, strokeColor) ?? particle.getFillColor();
if (strokeHslColor) {
particle.strokeColor = getHslAnimationFromHsl(strokeHslColor, particle.strokeAnimation, container.retina.reduceFactor);
}
}
else {
particle.strokeAnimation = undefined;
particle.strokeColor = undefined;
particle.strokeOpacity = defaultOpacity;
particle.strokeWidth = 0;
}
}
isEnabled(particle) {
const { fillAnimation, fillColor, strokeAnimation, strokeColor } = particle, fillEnabled = !!fillAnimation &&
((fillColor?.h.value !== undefined && fillColor.h.enable) ||
(fillColor?.s.value !== undefined && fillColor.s.enable) ||
(fillColor?.l.value !== undefined && fillColor.l.enable)), strokeEnabled = !!strokeAnimation &&
((strokeColor?.h.value !== undefined && strokeColor.h.enable) ||
(strokeColor?.s.value !== undefined && strokeColor.s.enable) ||
(strokeColor?.l.value !== undefined && strokeColor.l.enable));
return !particle.destroyed && !particle.spawning && (fillEnabled || strokeEnabled);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
updateColor(particle.fillColor, delta, this.#container.hdr);
updateColor(particle.strokeColor, delta, this.#container.hdr);
}
}
async function loadPaintUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("paint", container => {
return Promise.resolve(new PaintUpdater(e.pluginManager, container));
});
});
}
var RgbIndexes;
(function (RgbIndexes) {
RgbIndexes[RgbIndexes["r"] = 1] = "r";
RgbIndexes[RgbIndexes["g"] = 2] = "g";
RgbIndexes[RgbIndexes["b"] = 3] = "b";
RgbIndexes[RgbIndexes["a"] = 5] = "a";
})(RgbIndexes || (RgbIndexes = {}));
const rgbRegex = /rgba?\(\s*(\d{1,3})\s*[\s,]\s*(\d{1,3})\s*[\s,]\s*(\d{1,3})\s*([\s,]\s*(0|1|0?\.\d+|(\d{1,3})%)\s*)?\)/i;
class RgbColorManager {
accepts(input) {
return input.startsWith("rgb");
}
handleColor(color) {
const colorValue = color.value, rgbColor = colorValue.rgb ?? color.value;
if (!("r" in rgbColor) || !("g" in rgbColor) || !("b" in rgbColor)) {
return;
}
return rgbColor;
}
handleRangeColor(color) {
const colorValue = color.value, rgbColor = colorValue.rgb ?? color.value;
if (!("r" in rgbColor) || !("g" in rgbColor) || !("b" in rgbColor)) {
return;
}
return {
r: getRangeValue(rgbColor.r),
g: getRangeValue(rgbColor.g),
b: getRangeValue(rgbColor.b),
};
}
parseString(input) {
if (!this.accepts(input)) {
return;
}
const result = rgbRegex.exec(input), radix = 10, minLength = 4, defaultAlpha = 1;
return result
? {
a: result.length > minLength ? parseAlpha(result[RgbIndexes.a]) : defaultAlpha,
b: parseInt(result[RgbIndexes.b] ?? "0", radix),
g: parseInt(result[RgbIndexes.g] ?? "0", radix),
r: parseInt(result[RgbIndexes.r] ?? "0", radix),
}
: undefined;
}
}
async function loadRgbColorPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addColorManager("rgb", new RgbColorManager());
});
}
class SizeAnimation extends RangedAnimationOptions {
destroy = exports.DestroyType.none;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
loadProperty(this, "destroy", data.destroy);
}
}
class Size extends RangedAnimationValueWithRandom {
animation = new SizeAnimation();
value = 3;
load(data) {
super.load(data);
if (isNull(data)) {
return;
}
const animation = data.animation;
if (animation !== undefined) {
this.animation.load(animation);
}
}
}
const minLoops = 0;
class SizeUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const container = this.#container, sizeOptions = particle.options.size;
if (!sizeOptions) {
return;
}
const sizeAnimation = sizeOptions.animation;
if (!sizeAnimation.enable) {
return;
}
particle.size.velocity = (particle.retina.sizeAnimationSpeed / percentDenominator) * container.retina.reduceFactor;
if (sizeAnimation.sync) {
return;
}
particle.size.velocity *= getRandom();
}
isEnabled(particle) {
return (!particle.destroyed &&
!particle.spawning &&
particle.size.enable &&
((particle.size.maxLoops ?? minLoops) <= minLoops ||
((particle.size.maxLoops ?? minLoops) > minLoops &&
(particle.size.loops ?? minLoops) < (particle.size.maxLoops ?? minLoops))));
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "size", Size, ...sources);
}
preInit(particle) {
const pxRatio = this.#container.retina.pixelRatio, options = particle.options, sizeOptions = options.size;
if (!sizeOptions) {
return;
}
particle.size = initParticleNumericAnimationValue(sizeOptions, pxRatio);
particle.retina.sizeAnimationSpeed = getRangeValue(sizeOptions.animation.speed) * pxRatio;
}
reset(particle) {
particle.size.time = 0;
particle.size.loops = 0;
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.options.size) {
return;
}
updateAnimation(particle, particle.size, true, particle.options.size.animation.destroy, delta);
}
}
async function loadSizeUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("size", container => {
return Promise.resolve(new SizeUpdater(container));
});
});
}
async function loadBasic(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
await Promise.all([
loadBlendPlugin(e),
loadHexColorPlugin(e),
loadHslColorPlugin(e),
loadRgbColorPlugin(e),
loadMovePlugin(e),
loadCircleShape(e),
loadPaintUpdater(e),
loadOpacityUpdater(e),
loadOutModesUpdater(e),
loadSizeUpdater(e),
]);
});
}
class DestroyBounds {
bottom;
left;
right;
top;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "bottom", data.bottom);
loadRangeProperty(this, "left", data.left);
loadRangeProperty(this, "right", data.right);
loadRangeProperty(this, "top", data.top);
}
}
var DestroyMode;
(function (DestroyMode) {
DestroyMode["explode"] = "explode";
DestroyMode["none"] = "none";
DestroyMode["split"] = "split";
})(DestroyMode || (DestroyMode = {}));
class Explode {
maxSizeFactor = 3;
speed = 2;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "maxSizeFactor", data.maxSizeFactor);
loadProperty(this, "speed", data.speed);
}
}
class SplitFactor extends ValueWithRandom {
value = 3;
}
class SplitRate extends ValueWithRandom {
value = { min: 4, max: 9 };
}
class Split {
count = 1;
factor = new SplitFactor();
fillColor;
fillColorOffset;
particles;
rate = new SplitRate();
sizeOffset = true;
strokeColor;
strokeColorOffset;
load(data) {
if (isNull(data)) {
return;
}
if (data.fillColor !== undefined) {
this.fillColor = OptionsColor.create(this.fillColor, data.fillColor);
}
if (data.strokeColor !== undefined) {
this.strokeColor = OptionsColor.create(this.strokeColor, data.strokeColor);
}
loadProperty(this, "count", data.count);
this.factor.load(data.factor);
this.rate.load(data.rate);
this.particles = executeOnSingleOrMultiple(data.particles, particles => {
return deepExtend({}, particles);
});
loadProperty(this, "sizeOffset", data.sizeOffset);
if (data.fillColorOffset) {
this.fillColorOffset = this.fillColorOffset ?? {};
if (data.fillColorOffset.h !== undefined) {
this.fillColorOffset.h = data.fillColorOffset.h;
}
if (data.fillColorOffset.s !== undefined) {
this.fillColorOffset.s = data.fillColorOffset.s;
}
if (data.fillColorOffset.l !== undefined) {
this.fillColorOffset.l = data.fillColorOffset.l;
}
}
if (data.strokeColorOffset) {
this.strokeColorOffset = this.strokeColorOffset ?? {};
if (data.strokeColorOffset.h !== undefined) {
this.strokeColorOffset.h = data.strokeColorOffset.h;
}
if (data.strokeColorOffset.s !== undefined) {
this.strokeColorOffset.s = data.strokeColorOffset.s;
}
if (data.strokeColorOffset.l !== undefined) {
this.strokeColorOffset.l = data.strokeColorOffset.l;
}
}
}
}
class Destroy {
bounds = new DestroyBounds();
explode = new Explode();
mode = DestroyMode.none;
split = new Split();
load(data) {
if (isNull(data)) {
return;
}
if (data.mode) {
this.mode = data.mode;
}
if (data.bounds) {
this.bounds.load(data.bounds);
}
this.explode.load(data.explode);
this.split.load(data.split);
}
}
const defaultOffset = 0, minDestroySize = 0.5, defaultSplitCount = 0, increment = 1, unbreakableTime = 500, minSplitCount = 0, minHue = 0, hueRange = 360, minSaturation = 0, maxSaturation = 100, minLightness = 0, maxLightness = 100;
function createParentColor(parentColor) {
return AnimatableColor.create(undefined, {
value: {
hsl: parentColor,
},
});
}
function createOffsetColor(parentColor, offset) {
const offsetH = getRangeValue(offset.h ?? defaultOffset), offsetS = getRangeValue(offset.s ?? defaultOffset), offsetL = getRangeValue(offset.l ?? defaultOffset), h = (parentColor.h + offsetH) % hueRange, s = Math.max(minSaturation, Math.min(maxSaturation, parentColor.s + offsetS)), l = Math.max(minLightness, Math.min(maxLightness, parentColor.l + offsetL));
return AnimatableColor.create(undefined, {
value: {
hsl: {
h: h < minHue ? h + hueRange : h,
s,
l,
},
},
});
}
function resolveSplitColor(offset, splitColor, splitParticlesColor, parentColor) {
if (offset && parentColor) {
return createOffsetColor(parentColor, offset);
}
if (splitColor) {
return AnimatableColor.create(undefined, splitColor);
}
if (splitParticlesColor) {
return AnimatableColor.create(undefined, splitParticlesColor);
}
return parentColor ? createParentColor(parentColor) : undefined;
}
function addSplitParticle(pluginManager, container, parent, splitParticlesOptions) {
const destroyOptions = parent.options.destroy;
if (!destroyOptions) {
return;
}
const splitOptions = destroyOptions.split, splitParticleOptions = loadParticlesOptions(pluginManager, container, parent.options), splitFillColor = itemFromSingleOrMultiple(splitOptions.fillColor), splitStrokeColor = itemFromSingleOrMultiple(splitOptions.strokeColor), parentFillColor = parent.getFillColor(), parentStrokeColor = parent.getStrokeColor();
splitParticleOptions.move.load({
center: {
x: parent.position.x,
y: parent.position.y,
mode: exports.PixelMode.precise,
},
});
const factor = identity$1 / getRangeValue(splitOptions.factor.value), sizeOptions = splitParticleOptions["size"];
if (sizeOptions) {
if (isNumber(sizeOptions.value)) {
sizeOptions.value *= factor;
}
else {
sizeOptions.value.min *= factor;
sizeOptions.value.max *= factor;
}
}
splitParticleOptions.load(splitParticlesOptions);
const splitParticlePaintOptions = itemFromSingleOrMultiple(splitParticleOptions.paint), splitParticleFillOptions = splitParticlePaintOptions?.fill, splitParticleStrokeOptions = splitParticlePaintOptions?.stroke, fillColor = resolveSplitColor(splitOptions.fillColorOffset, splitFillColor, splitParticleFillOptions?.color, parentFillColor), strokeColor = resolveSplitColor(splitOptions.strokeColorOffset, splitStrokeColor, splitParticleStrokeOptions?.color, parentStrokeColor);
if (fillColor && splitParticleFillOptions) {
splitParticleFillOptions.color = fillColor;
}
if (strokeColor && splitParticleStrokeOptions) {
splitParticleStrokeOptions.color = strokeColor;
}
const offset = splitOptions.sizeOffset ? setRangeValue(-parent.size.value, parent.size.value) : defaultOffset, position = {
x: parent.position.x + randomInRangeValue(offset),
y: parent.position.y + randomInRangeValue(offset),
};
return container.particles.addParticle(position, splitParticleOptions, parent.group, (particle) => {
if (particle.size.value < minDestroySize) {
return false;
}
particle.velocity.length = randomInRangeValue(setRangeValue(parent.velocity.length, particle.velocity.length));
particle.splitCount = (parent.splitCount ?? defaultSplitCount) + increment;
particle.unbreakable = true;
particle.unbreakableUntil = performance.now() + unbreakableTime;
return true;
});
}
function split(pluginManager, container, particle) {
const destroyOptions = particle.options.destroy;
if (!destroyOptions) {
return;
}
const splitOptions = destroyOptions.split;
if (splitOptions.count >= minSplitCount) {
if (particle.splitCount === undefined || particle.splitCount > splitOptions.count) {
return;
}
particle.splitCount++;
}
const rate = getRangeValue(splitOptions.rate.value), particlesSplitOptions = itemFromSingleOrMultiple(splitOptions.particles);
for (let i = 0; i < rate; i++) {
addSplitParticle(pluginManager, container, particle, particlesSplitOptions);
}
}
const defaultDeltaFactor$1 = 1, fpsFactor = 60, minExplodeSpeed = 0.01, maxExplodeProgress = 1;
class DestroyUpdater {
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#container = container;
this.#pluginManager = pluginManager;
}
init(particle) {
const container = this.#container, particlesOptions = particle.options, destroyOptions = particlesOptions.destroy;
if (!destroyOptions) {
return;
}
particle.exploding = undefined;
particle.splitCount = 0;
const destroyBoundsOptions = destroyOptions.bounds;
particle.destroyBounds ??= {};
const { bottom, left, right, top } = destroyBoundsOptions, { destroyBounds } = particle, canvasSize = container.canvas.size;
if (bottom) {
destroyBounds.bottom = (getRangeValue(bottom) * canvasSize.height) / percentDenominator;
}
if (left) {
destroyBounds.left = (getRangeValue(left) * canvasSize.width) / percentDenominator;
}
if (right) {
destroyBounds.right = (getRangeValue(right) * canvasSize.width) / percentDenominator;
}
if (top) {
destroyBounds.top = (getRangeValue(top) * canvasSize.height) / percentDenominator;
}
}
isEnabled(particle) {
const destroyParticle = particle;
return !destroyParticle.destroyed || !!destroyParticle.exploding;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "destroy", Destroy, ...sources);
}
particleDestroyed(particle, override) {
if (override) {
return;
}
const destroyOptions = particle.options.destroy;
switch (destroyOptions?.mode) {
case DestroyMode.split:
split(this.#pluginManager, this.#container, particle);
break;
case DestroyMode.explode: {
if (particle.exploding) {
particle.destroyed = false;
break;
}
const { explode } = destroyOptions, initialSize = particle.size.value, maxSize = initialSize * explode.maxSizeFactor, opacity = particle.getOpacity();
particle.exploding = {
initialFillOpacity: opacity.fillOpacity,
initialSize,
initialStrokeOpacity: opacity.strokeOpacity,
maxSize,
progress: 0,
speed: Math.max(explode.speed, minExplodeSpeed),
};
particle.fillOpacity = opacity.fillOpacity;
particle.strokeOpacity = opacity.strokeOpacity;
particle.destroyed = false;
break;
}
}
}
update(particle, delta) {
if (particle.exploding) {
const explosionState = particle.exploding, deltaFactor = delta.factor || defaultDeltaFactor$1;
explosionState.progress = Math.min(maxExplodeProgress, explosionState.progress + (explosionState.speed * deltaFactor) / fpsFactor);
const progress = explosionState.progress;
particle.size.value =
explosionState.initialSize + (explosionState.maxSize - explosionState.initialSize) * progress;
particle.fillOpacity = explosionState.initialFillOpacity * (maxExplodeProgress - progress);
particle.strokeOpacity = explosionState.initialStrokeOpacity * (maxExplodeProgress - progress);
if (progress >= maxExplodeProgress) {
particle.exploding = undefined;
particle.destroy(true);
}
return;
}
if (particle.unbreakableUntil !== undefined && performance.now() >= particle.unbreakableUntil) {
particle.unbreakable = false;
particle.unbreakableUntil = undefined;
}
const position = particle.getPosition(), bounds = particle.destroyBounds;
if (!bounds) {
return;
}
if ((bounds.bottom !== undefined && position.y >= bounds.bottom) ||
(bounds.left !== undefined && position.x <= bounds.left) ||
(bounds.right !== undefined && position.x >= bounds.right) ||
(bounds.top !== undefined && position.y <= bounds.top)) {
particle.destroy();
}
}
}
async function loadDestroyUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("destroy", container => {
return Promise.resolve(new DestroyUpdater(e.pluginManager, container));
});
});
}
class EmitterLife {
count;
delay;
duration;
wait = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "count", data.count);
loadRangeProperty(this, "delay", data.delay);
loadRangeProperty(this, "duration", data.duration);
loadProperty(this, "wait", data.wait);
}
}
class EmitterRate {
delay = 0.1;
quantity = 1;
load(data) {
if (isNull(data)) {
return;
}
loadRangeProperty(this, "quantity", data.quantity);
loadRangeProperty(this, "delay", data.delay);
}
}
class EmitterShapeReplace {
color = false;
opacity = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "color", data.color);
loadProperty(this, "opacity", data.opacity);
}
}
class EmitterShape {
options = {};
replace = new EmitterShapeReplace();
type = "square";
load(data) {
if (isNull(data)) {
return;
}
if (data.options !== undefined) {
this.options = deepExtend({}, data.options ?? {});
}
this.replace.load(data.replace);
loadProperty(this, "type", data.type);
}
}
class EmitterSize {
height = 0;
mode = exports.PixelMode.percent;
width = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "mode", data.mode);
loadProperty(this, "height", data.height);
loadProperty(this, "width", data.width);
}
}
class EmitterSpawn {
fill;
stroke;
load(data) {
if (isNull(data)) {
return;
}
if (data.fill) {
this.fill ??= new Fill();
this.fill.load(data.fill);
}
if (data.stroke) {
this.stroke ??= new Stroke();
this.stroke.load(data.stroke);
}
}
}
class Emitter {
autoPlay = true;
direction;
domId;
fill = true;
life = new EmitterLife();
name;
particles;
position;
rate = new EmitterRate();
shape = new EmitterShape();
size;
spawn = new EmitterSpawn();
spawnFillColor;
spawnStrokeColor;
startCount = 0;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "autoPlay", data.autoPlay);
if (data.size !== undefined) {
this.size ??= new EmitterSize();
this.size.load(data.size);
}
loadProperty(this, "direction", data.direction);
loadProperty(this, "domId", data.domId);
loadProperty(this, "fill", data.fill);
this.life.load(data.life);
loadProperty(this, "name", data.name);
this.particles = executeOnSingleOrMultiple(data.particles, particles => {
return deepExtend({}, particles);
});
this.rate.load(data.rate);
this.shape.load(data.shape);
this.spawn.load(data.spawn);
if (data.position !== undefined) {
this.position = {};
if (data.position.x !== undefined) {
this.position.x = setRangeValue(data.position.x);
}
if (data.position.y !== undefined) {
this.position.y = setRangeValue(data.position.y);
}
}
if (data.spawnFillColor !== undefined) {
this.spawnFillColor ??= new AnimatableColor();
this.spawnFillColor.load(data.spawnFillColor);
}
if (data.spawnStrokeColor !== undefined) {
this.spawnStrokeColor ??= new AnimatableColor();
this.spawnStrokeColor.load(data.spawnStrokeColor);
}
loadProperty(this, "startCount", data.startCount);
}
}
class EmittersPlugin {
id = "emitters";
#instancesManager;
constructor(instancesManager) {
this.#instancesManager = instancesManager;
}
async getPlugin(container) {
const { EmittersPluginInstance } = await Promise.resolve().then(function () { return EmittersPluginInstance$1; });
return new EmittersPluginInstance(this.#instancesManager, container);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
if (source?.emitters) {
options.emitters = executeOnSingleOrMultiple(source.emitters, emitter => {
const tmp = new Emitter();
tmp.load(emitter);
return tmp;
});
}
}
needsPlugin(options) {
if (!options) {
return false;
}
const emitters = options.emitters;
return (isArray(emitters) && !!emitters.length) || emitters !== undefined;
}
}
async function addEmittersShapesManager(e) {
const { ShapeManager } = await Promise.resolve().then(function () { return ShapeManager$1; }), pluginManager = e.pluginManager;
pluginManager.emitterShapeManager ??= new ShapeManager();
pluginManager.addEmitterShapeGenerator ??= (name, generator) => {
pluginManager.emitterShapeManager?.addShapeGenerator(name, generator);
};
}
const instancesManagers = new WeakMap();
function getEmittersInstancesManager(e) {
const pluginManager = e.pluginManager;
let manager = instancesManagers.get(pluginManager);
if (!manager) {
manager = Promise.resolve().then(function () { return EmittersInstancesManager$1; })
.then(({ EmittersInstancesManager }) => new EmittersInstancesManager(pluginManager))
.catch((error) => {
instancesManagers.delete(pluginManager);
throw error;
});
instancesManagers.set(pluginManager, manager);
}
return manager;
}
class EmitterShapeBase {
fill;
options;
position;
size;
constructor(position, size, fill, options) {
this.position = position;
this.size = size;
this.fill = fill;
this.options = options;
}
resize(position, size) {
this.position = position;
this.size = size;
}
}
var EmitterClickMode;
(function (EmitterClickMode) {
EmitterClickMode["emitter"] = "emitter";
})(EmitterClickMode || (EmitterClickMode = {}));
async function loadEmittersPluginSimple(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const instancesManager = await getEmittersInstancesManager(e);
await addEmittersShapesManager(e);
e.pluginManager.addPlugin(new EmittersPlugin(instancesManager));
});
}
function ensureEmittersPluginLoaded(e) {
if (!e.pluginManager.addEmitterShapeGenerator) {
throw new Error("tsParticles Emitters Plugin is not loaded");
}
}
const sides$1 = 4;
var Sides;
(function (Sides) {
Sides[Sides["TopLeft"] = 0] = "TopLeft";
Sides[Sides["TopRight"] = 1] = "TopRight";
Sides[Sides["BottomRight"] = 2] = "BottomRight";
Sides[Sides["BottomLeft"] = 3] = "BottomLeft";
})(Sides || (Sides = {}));
function randomSquareCoordinate(position, offset) {
return position + offset * (getRandom() - half);
}
function getRandomSize(sides) {
return Math.floor(getRandom() * sides);
}
class EmittersSquareShape extends EmitterShapeBase {
constructor(position, size, fill, options) {
super(position, size, fill, options);
}
async init() {
}
randomPosition() {
const fill = this.fill, position = this.position, size = this.size;
if (fill) {
return {
position: {
x: randomSquareCoordinate(position.x, size.width),
y: randomSquareCoordinate(position.y, size.height),
},
};
}
else {
const halfW = size.width * half, halfH = size.height * half, side = getRandomSize(sides$1), v = (getRandom() - half) * double;
switch (side) {
case Sides.TopLeft:
return {
position: {
x: position.x + v * halfW,
y: position.y - halfH,
},
};
case Sides.TopRight:
return {
position: {
x: position.x - halfW,
y: position.y + v * halfH,
},
};
case Sides.BottomRight:
return {
position: {
x: position.x + v * halfW,
y: position.y + halfH,
},
};
case Sides.BottomLeft:
default:
return {
position: {
x: position.x + halfW,
y: position.y + v * halfH,
},
};
}
}
}
}
class EmittersSquareShapeGenerator {
generate(_container, position, size, fill, options) {
return new EmittersSquareShape(position, size, fill, options);
}
}
async function loadEmittersShapeSquare(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register((e) => {
ensureEmittersPluginLoaded(e);
e.pluginManager.addEmitterShapeGenerator?.("square", new EmittersSquareShapeGenerator());
});
}
class LifeDelay extends ValueWithRandom {
sync = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "sync", data.sync);
}
}
class LifeDuration extends ValueWithRandom {
sync = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "sync", data.sync);
}
}
class Life {
count = 0;
delay = new LifeDelay();
duration = new LifeDuration();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "count", data.count);
this.delay.load(data.delay);
this.duration.load(data.duration);
}
}
const noTime$1 = 0, infiniteValue$1 = -1, noLife = 0, minCanvasSize = 0;
function updateLife(particle, delta, canvasSize) {
if (!particle.life) {
return;
}
const life = particle.life;
let justSpawned = false;
if (particle.spawning) {
life.delayTime += delta.value;
if (life.delayTime >= particle.life.delay) {
justSpawned = true;
particle.spawning = false;
life.delayTime = noTime$1;
life.time = noTime$1;
}
else {
return;
}
}
if (life.duration === infiniteValue$1) {
return;
}
if (justSpawned) {
life.time = noTime$1;
}
else {
life.time += delta.value;
}
if (life.time < life.duration) {
return;
}
life.time = noTime$1;
if (particle.life.count > noLife) {
particle.life.count--;
}
if (particle.life.count === noLife) {
particle.destroy();
return;
}
const widthRange = setRangeValue(minCanvasSize, canvasSize.width), heightRange = setRangeValue(minCanvasSize, canvasSize.width);
particle.position.x = randomInRangeValue(widthRange);
particle.position.y = randomInRangeValue(heightRange);
particle.spawning = true;
life.delayTime = noTime$1;
life.time = noTime$1;
particle.reset();
const lifeOptions = particle.options.life;
if (lifeOptions) {
life.delay = getRangeValue(lifeOptions.delay.value) * millisecondsToSeconds;
life.duration = getRangeValue(lifeOptions.duration.value) * millisecondsToSeconds;
}
}
const noTime = 0, identity = 1, infiniteValue = -1;
class LifeUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const container = this.#container, particlesOptions = particle.options, lifeOptions = particlesOptions.life;
if (!lifeOptions) {
return;
}
const delayFactor = lifeOptions.delay.sync ? identity : getRandom(), durationFactor = lifeOptions.duration.sync ? identity : getRandom();
particle.life = {
delay: container.retina.reduceFactor
? ((getRangeValue(lifeOptions.delay.value) * delayFactor) / container.retina.reduceFactor) *
millisecondsToSeconds
: noTime,
delayTime: noTime,
duration: container.retina.reduceFactor
? ((getRangeValue(lifeOptions.duration.value) * durationFactor) / container.retina.reduceFactor) *
millisecondsToSeconds
: noTime,
time: noTime,
count: lifeOptions.count,
};
if (particle.life.duration <= noTime) {
particle.life.duration = infiniteValue;
}
if (particle.life.count <= noTime) {
particle.life.count = infiniteValue;
}
particle.spawning = particle.life.delay > noTime;
}
isEnabled(particle) {
return !particle.destroyed;
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "life", Life, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle) || !particle.life) {
return;
}
updateLife(particle, delta, this.#container.canvas.size);
}
}
async function loadLifeUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("life", container => {
return Promise.resolve(new LifeUpdater(container));
});
});
}
function drawLine(data) {
const { context, particle, radius } = data, shapeData = particle.shapeData, centerY = 0;
context.moveTo(-radius, centerY);
context.lineTo(radius, centerY);
context.lineCap = shapeData?.cap ?? "butt";
}
const sides = 1;
class LineDrawer {
draw(data) {
drawLine(data);
}
getSidesCount() {
return sides;
}
}
async function loadLineShape(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addShape(["line"], () => Promise.resolve(new LineDrawer()));
});
}
class RotateAnimation {
decay = 0;
enable = false;
speed = 0;
sync = false;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
loadRangeProperty(this, "speed", data.speed);
loadRangeProperty(this, "decay", data.decay);
loadProperty(this, "sync", data.sync);
}
}
class Rotate extends ValueWithRandom {
animation = new RotateAnimation();
direction = exports.RotateDirection.clockwise;
path = false;
load(data) {
if (isNull(data)) {
return;
}
super.load(data);
loadProperty(this, "direction", data.direction);
this.animation.load(data.animation);
loadProperty(this, "path", data.path);
}
}
const doublePIDeg = 360;
class RotateUpdater {
#container;
constructor(container) {
this.#container = container;
}
init(particle) {
const rotateOptions = particle.options.rotate;
if (!rotateOptions) {
return;
}
particle.rotate = {
enable: rotateOptions.animation.enable,
value: degToRad(getRangeValue(rotateOptions.value)),
min: 0,
max: doublePI,
};
particle.pathRotation = rotateOptions.path;
let rotateDirection = rotateOptions.direction;
if (rotateDirection === exports.RotateDirection.random) {
const index = Math.floor(getRandom() * double), minIndex = 0;
rotateDirection = index > minIndex ? exports.RotateDirection.counterClockwise : exports.RotateDirection.clockwise;
}
switch (rotateDirection) {
case exports.RotateDirection.counterClockwise:
case "counterClockwise":
particle.rotate.status = exports.AnimationStatus.decreasing;
break;
case exports.RotateDirection.clockwise:
particle.rotate.status = exports.AnimationStatus.increasing;
break;
}
const rotateAnimation = rotateOptions.animation;
if (rotateAnimation.enable) {
particle.rotate.decay = identity$1 - getRangeValue(rotateAnimation.decay);
particle.rotate.velocity =
(getRangeValue(rotateAnimation.speed) / doublePIDeg) * this.#container.retina.reduceFactor;
if (!rotateAnimation.sync) {
particle.rotate.velocity *= getRandom();
}
}
particle.rotation = particle.rotate.value;
}
isEnabled(particle) {
const rotate = particle.options.rotate;
if (!rotate) {
return false;
}
return !particle.destroyed && !particle.spawning && (!!rotate.value || rotate.animation.enable || rotate.path);
}
loadOptions(options, ...sources) {
loadOptionProperty(options, "rotate", Rotate, ...sources);
}
update(particle, delta) {
if (!this.isEnabled(particle)) {
return;
}
particle.isRotating = !!particle.rotate;
if (!particle.rotate) {
return;
}
updateAnimation(particle, particle.rotate, false, exports.DestroyType.none, delta);
particle.rotation = particle.rotate.value;
}
}
async function loadRotateUpdater(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addParticleUpdater("rotate", container => {
return Promise.resolve(new RotateUpdater(container));
});
});
}
const mouseDownEvent = "pointerdown", touchStartEvent = "touchstart";
class SoundsAudio {
loop = false;
source = "";
load(data) {
if (isNull(data)) {
return;
}
if (isObject(data)) {
loadProperty(this, "loop", data.loop);
if (data.source !== undefined) {
this.source = data.source;
}
}
else {
this.source = data;
}
}
}
class SoundsNote {
duration = 500;
value = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "duration", data.duration);
loadProperty(this, "value", data.value);
}
}
class SoundsMelody {
loop = false;
melodies = [];
notes = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "loop", data.loop);
if (data.melodies !== undefined) {
this.melodies = data.melodies.map(s => {
const tmp = new SoundsMelody();
tmp.load(s);
return tmp;
});
}
if (data.notes !== undefined) {
this.notes = data.notes.map(s => {
const tmp = new SoundsNote();
tmp.load(s);
return tmp;
});
}
}
}
class SoundsEvent {
audio;
event = [];
filter;
melodies;
notes = [];
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "event", data.event);
if (data.audio !== undefined) {
if (isArray(data.audio)) {
this.audio = data.audio.map(s => {
const tmp = new SoundsAudio();
tmp.load(s);
return tmp;
});
}
else {
this.audio = new SoundsAudio();
this.audio.load(data.audio);
}
}
if (data.notes !== undefined) {
this.notes = data.notes.map(t => {
const tmp = new SoundsNote();
tmp.load(t);
return tmp;
});
}
if (data.melodies !== undefined) {
this.melodies = data.melodies.map(t => {
const tmp = new SoundsMelody();
tmp.load(t);
return tmp;
});
}
if (data.filter) {
if (isString(data.filter)) {
const filterFunc = globalThis[data.filter];
if (isFunction(filterFunc)) {
this.filter = filterFunc;
}
}
else {
this.filter = data.filter;
}
}
}
}
class SoundsIcon {
height = 24;
path;
style = "";
svg;
width = 24;
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "path", data.path);
loadProperty(this, "svg", data.svg);
loadProperty(this, "width", data.width);
loadProperty(this, "height", data.height);
}
}
class SoundsIcons {
enable;
mute;
unmute;
volumeDown;
volumeUp;
constructor() {
this.mute = new SoundsIcon();
this.unmute = new SoundsIcon();
this.volumeDown = new SoundsIcon();
this.volumeUp = new SoundsIcon();
this.enable = false;
this.mute.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M19.707,5.293c-0.391-0.391-1.023-0.391-1.414,0l-1.551,1.551c-0.345-0.688-0.987-1.02-1.604-1.02c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C10.357,8.561,8.904,9,8,9c-1.654,0-3,1.346-3,3v2c0,1.237,0.754,2.302,1.826,2.76l-1.533,1.533c-0.391,0.391-0.391,1.023,0,1.414C5.488,19.902,5.744,20,6,20s0.512-0.098,0.707-0.293l2.527-2.527c0.697,0.174,1.416,0.455,1.875,0.762l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C16.035,20.176,17,19.495,17,18V9.414l2.707-2.707C20.098,6.316,20.098,5.684,19.707,5.293z M14.891,7.941c0.038-0.025,0.073-0.046,0.104-0.062C14.998,7.914,15,7.954,15,8v1.293l-2,2V9.202L14.891,7.941z M7,12c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v2.439l-2.83,2.83C8.757,15.046,8.356,15,8,15c-0.552,0-1-0.448-1-1V12z M10.301,15.406L12,13.707v2.439C11.519,15.859,10.925,15.604,10.301,15.406z M14.994,18.12c-0.03-0.016-0.065-0.036-0.104-0.062L13,16.798v-4.091l2-2V18C15,18.046,14.998,18.086,14.994,18.12z"/>
</g>
</svg>`;
this.unmute.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M17.138,5.824c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C12.357,8.561,10.904,9,10,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C18.035,20.176,19,19.495,19,18V8C19,6.505,18.035,5.824,17.138,5.824z M14,16.146C12.907,15.495,11.211,15,10,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146V16.146z M17,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L15,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C16.998,7.914,17,7.954,17,8V18z"/>
</g>
</svg>`;
this.volumeDown.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M15.138,5.824c-0.449,0-0.905,0.152-1.356,0.453l-2.672,1.781C10.357,8.561,8.904,9,8,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C16.035,20.176,17,19.495,17,18V8C17,6.505,16.035,5.824,15.138,5.824z M8,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v6.293C10.907,15.495,9.211,15,8,15z M15,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L13,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C14.998,7.914,15,7.954,15,8V18z"/>
<path fill="#fff" d="M18.292,10.294c-0.39,0.391-0.39,1.023,0.002,1.414c0.345,0.345,0.535,0.803,0.535,1.291c0,0.489-0.19,0.948-0.536,1.294c-0.391,0.39-0.391,1.023,0,1.414C18.488,15.902,18.744,16,19,16s0.512-0.098,0.707-0.293c0.724-0.723,1.122-1.685,1.122-2.708s-0.398-1.984-1.123-2.707C19.317,9.903,18.683,9.901,18.292,10.294z"/>
</g>
</svg>`;
this.volumeUp.svg = `<?xml version="1.0"?>
<svg baseProfile="tiny" height="24px" version="1.2" viewBox="0 0 24 24" width="24px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1">
<path fill="#fff" d="M16.706,10.292c-0.389-0.389-1.023-0.391-1.414,0.002c-0.39,0.391-0.39,1.023,0.002,1.414c0.345,0.345,0.535,0.803,0.535,1.291c0,0.489-0.19,0.948-0.536,1.294c-0.391,0.39-0.391,1.023,0,1.414C15.488,15.902,15.744,16,16,16s0.512-0.098,0.707-0.293c0.724-0.723,1.122-1.685,1.122-2.708S17.431,11.015,16.706,10.292z"/>
<path fill="#fff" d="M18.706,8.292c-0.391-0.389-1.023-0.39-1.414,0.002c-0.39,0.391-0.39,1.024,0.002,1.414c0.879,0.877,1.363,2.044,1.364,3.287c0.001,1.246-0.484,2.417-1.365,3.298c-0.391,0.391-0.391,1.023,0,1.414C17.488,17.902,17.744,18,18,18s0.512-0.098,0.707-0.293c1.259-1.259,1.952-2.933,1.951-4.713C20.657,11.217,19.964,9.547,18.706,8.292z"/>
<path fill="#fff" d="M20.706,6.292c-0.391-0.389-1.023-0.39-1.414,0.002c-0.39,0.391-0.39,1.024,0.002,1.414c1.412,1.409,2.191,3.285,2.192,5.284c0.002,2.002-0.777,3.885-2.193,5.301c-0.391,0.391-0.391,1.023,0,1.414C19.488,19.902,19.744,20,20,20s0.512-0.098,0.707-0.293c1.794-1.794,2.781-4.18,2.779-6.717C23.485,10.457,22.497,8.078,20.706,6.292z"/>
<path fill="#fff" d="M12.138,5.824c-0.449,0-0.905,0.152-1.356,0.453L8.109,8.059C7.357,8.561,5.904,9,5,9c-1.654,0-3,1.346-3,3v2c0,1.654,1.346,3,3,3c0.904,0,2.357,0.439,3.109,0.941l2.672,1.781c0.451,0.301,0.907,0.453,1.356,0.453C13.035,20.176,14,19.495,14,18V8C14,6.505,13.035,5.824,12.138,5.824z M5,15c-0.552,0-1-0.448-1-1v-2c0-0.552,0.448-1,1-1c1.211,0,2.907-0.495,4-1.146v6.293C7.907,15.495,6.211,15,5,15z M12,18c0,0.046-0.002,0.086-0.006,0.12c-0.03-0.016-0.065-0.036-0.104-0.062L10,16.798V9.202l1.891-1.261c0.038-0.025,0.073-0.046,0.104-0.062C11.998,7.914,12,7.954,12,8V18z"/>
</g>
</svg>`;
}
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "enable", data.enable);
this.mute.load(data.mute);
this.unmute.load(data.unmute);
this.volumeDown.load(data.volumeDown);
this.volumeUp.load(data.volumeUp);
}
}
class SoundsVolume {
max = 100;
min = 0;
step = 10;
value = 100;
load(data) {
if (isNull(data)) {
return;
}
if (isObject(data)) {
loadProperty(this, "max", data.max);
loadProperty(this, "min", data.min);
loadProperty(this, "step", data.step);
if (data.value !== undefined) {
this.value = data.value;
}
}
else {
this.value = data;
}
}
}
class Sounds {
autoPlay = true;
enable = false;
events = [];
icons = new SoundsIcons();
volume = new SoundsVolume();
load(data) {
if (isNull(data)) {
return;
}
loadProperty(this, "autoPlay", data.autoPlay);
loadProperty(this, "enable", data.enable);
if (data.events !== undefined) {
this.events = data.events.map(t => {
const event = new SoundsEvent();
event.load(t);
return event;
});
}
this.icons.load(data.icons);
if (data.volume !== undefined) {
this.volume.load(data.volume);
}
}
}
const notes = new Map();
notes.set("C", [16.35, 32.7, 65.41, 130.81, 261.63, 523.25, 1046.5, 2093.0, 4186.01]);
notes.set("Db", [17.32, 34.65, 69.3, 138.59, 277.18, 554.37, 1108.73, 2217.46, 4434.92]);
notes.set("D", [18.35, 36.71, 73.42, 146.83, 293.66, 587.33, 1174.66, 2349.32, 4698.63]);
notes.set("Eb", [19.45, 38.89, 77.78, 155.56, 311.13, 622.25, 1244.51, 2489.02, 4978.03]);
notes.set("E", [20.6, 41.2, 82.41, 164.81, 329.63, 659.25, 1318.51, 2637.02, 5274.04]);
notes.set("F", [21.83, 43.65, 87.31, 174.61, 349.23, 698.46, 1396.91, 2793.83, 5587.65]);
notes.set("Gb", [23.12, 46.25, 92.5, 185.0, 369.99, 739.99, 1479.98, 2959.96, 5919.91]);
notes.set("G", [24.5, 49.0, 98.0, 196.0, 392.0, 783.99, 1567.98, 3135.96, 6271.93]);
notes.set("Ab", [25.96, 51.91, 103.83, 207.65, 415.3, 830.61, 1661.22, 3322.44, 6644.88]);
notes.set("A", [27.5, 55.0, 110.0, 220.0, 440.0, 880.0, 1760.0, 3520.0, 7040.0]);
notes.set("Bb", [29.14, 58.27, 116.54, 233.08, 466.16, 932.33, 1864.66, 3729.31, 7458.62]);
notes.set("B", [30.87, 61.74, 123.47, 246.94, 493.88, 987.77, 1975.53, 3951.07, 7902.13]);
notes.set("pause", [0]);
function getNoteFrequency(note) {
const regex = /(([A-G]b?)(\d))|pause/i, result = regex.exec(note), groupKey = 2, defaultMatchKey = 0, innerGroupKey = 3;
if (!result?.length) {
return;
}
const noteKey = result[groupKey] ?? result[defaultMatchKey], noteItem = notes.get(noteKey);
if (!noteItem) {
return;
}
return noteItem[parseInt(result[innerGroupKey] ?? "0")];
}
let muted = true;
const isWindowMuted = () => {
return muted;
};
const unmuteWindow = () => {
muted = false;
};
const generalFirstClickHandler = () => {
removeEventListener(mouseDownEvent, generalFirstClickHandler);
removeEventListener(touchStartEvent, generalFirstClickHandler);
unmuteWindow();
};
class SoundsPlugin {
id = "sounds";
#engine;
constructor(engine) {
this.#engine = engine;
const listenerOptions = {
capture: true,
once: true,
};
addEventListener(mouseDownEvent, generalFirstClickHandler, listenerOptions);
addEventListener(touchStartEvent, generalFirstClickHandler, listenerOptions);
}
async getPlugin(container) {
const { SoundsPluginInstance } = await Promise.resolve().then(function () { return SoundsPluginInstance$1; });
return new SoundsPluginInstance(container, this.#engine);
}
loadOptions(_container, options, source) {
if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
return;
}
let soundsOptions = options.sounds;
if (soundsOptions?.load === undefined) {
options.sounds = soundsOptions = new Sounds();
}
soundsOptions.load(source?.sounds);
}
needsPlugin(options) {
return options?.sounds?.enable ?? false;
}
}
async function loadSoundsPlugin(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(e => {
e.pluginManager.addPlugin(new SoundsPlugin(e));
});
}
let initPromise = null;
async function doInitPlugins(engine) {
engine.checkVersion("4.4.0");
await engine.pluginManager.register(async (e) => {
const loadEmittersForFireworks = async (e) => {
await loadEmittersPluginSimple(e);
await loadEmittersShapeSquare(e);
};
await Promise.all([
loadBasic(e),
loadLineShape(e),
loadBlendPlugin(e),
loadEmittersForFireworks(e),
loadSoundsPlugin(e),
loadRotateUpdater(e),
loadDestroyUpdater(e),
loadLifeUpdater(e),
loadPaintUpdater(e),
]);
});
}
async function initPlugins(engine) {
if (initPromise) {
return initPromise;
}
initPromise = doInitPlugins(engine);
return initPromise;
}
async function fireworks(idOrOptions, sourceOptions) {
await initPlugins(tsParticles);
let id, options;
if (isString(idOrOptions)) {
id = idOrOptions;
options = sourceOptions ?? {};
}
else {
id = "fireworks";
options = idOrOptions ?? {};
}
return getFireworksInstance(tsParticles, id, options);
}
fireworks.create = async (canvas, options) => {
await initPlugins(tsParticles);
const id = canvas?.id ?? "fireworks";
return getFireworksInstance(tsParticles, id, options ?? {}, canvas ?? undefined);
};
fireworks.init = async () => {
await initPlugins(tsParticles);
};
fireworks.version = "4.4.0";
globalThis.fireworks = fireworks;
const globalObject = globalThis;
globalObject.fireworks = fireworks;
globalObject.tsParticles = tsParticles;
const fColorIndex = 0, sColorIndex = 1;
function setTransformValue(factor, newFactor, key) {
const newValue = newFactor[key];
if (newValue !== undefined) {
factor[key] = (factor[key] ?? defaultTransformValue) * newValue;
}
}
class RenderManager {
#backgroundElement;
#backgroundWarnings;
#canvasClearPlugins;
#canvasManager;
#colorPlugins;
#container;
#context;
#contextSettings;
#drawParticlePlugins;
#drawParticlesCleanupPlugins;
#drawParticlesSetupPlugins;
#layers;
#pluginManager;
#postDrawUpdaters;
#preDrawUpdaters;
#reusableColorStyles = {};
#reusablePluginColors = [undefined, undefined];
#reusableTransform = {};
constructor(pluginManager, container, canvasManager) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#canvasManager = canvasManager;
this.#context = null;
this.#backgroundElement = null;
this.#backgroundWarnings = new Set();
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesCleanupPlugins = [];
this.#drawParticlesSetupPlugins = [];
this.#layers = {
0: [],
1: [],
2: [],
3: [],
4: [],
5: [],
6: [],
7: [],
};
}
get settings() {
return this.#contextSettings;
}
canvasClear() {
if (!this.#container.actualOptions.clear) {
return;
}
this.draw(ctx => {
ctx.clearRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
clear() {
for (const plugin of this.#canvasClearPlugins) {
if (plugin.canvasClear?.() ?? false) {
return;
}
}
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
for (const plugin of this.#getLayerPlugins(layer)) {
if (plugin.canvasClear?.() ?? false) {
return;
}
}
}
}
this.canvasClear();
}
destroy() {
this.stop();
this.#backgroundElement = null;
this.#backgroundWarnings.clear();
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesCleanupPlugins = [];
this.#drawParticlesSetupPlugins = [];
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
this.#layers[layer] = [];
}
}
}
draw(cb) {
const ctx = this.#context;
if (!ctx) {
return;
}
return cb(ctx);
}
drawParticle(particle, delta) {
if (particle.spawning || particle.destroyed) {
return;
}
const radius = particle.getRadius();
if (radius <= minimumSize) {
return;
}
const pfColor = particle.getFillColor(), psColor = particle.getStrokeColor();
let [fColor, sColor] = this.#getPluginParticleColors(particle);
fColor ??= pfColor;
sColor ??= psColor;
if (!fColor && !sColor) {
return;
}
const container = this.#container, zIndexOptions = particle.options.zIndex, zIndexFactor = zIndexFactorOffset - particle.zIndexFactor, { fillOpacity, opacity, strokeOpacity } = particle.getOpacity(), transform = this.#reusableTransform, colorStyles = this.#reusableColorStyles, fill = fColor
? getStyleFromHsl(fColor, container.hdr, fillOpacity * opacity, container.peakNits, container.hdrMode)
: undefined, stroke = sColor
? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity, container.peakNits, container.hdrMode)
: fill;
transform.a = transform.b = transform.c = transform.d = undefined;
colorStyles.fill = fill;
colorStyles.stroke = stroke;
this.draw((context) => {
for (const plugin of this.#drawParticlesSetupPlugins) {
plugin.drawParticleSetup?.(context, particle, delta);
}
this.#applyPreDrawUpdaters(context, particle, radius, opacity, colorStyles, transform);
this.#drawParticle({
container,
context,
particle,
delta,
colorStyles,
radius: radius * zIndexFactor ** zIndexOptions.sizeRate,
opacity: opacity,
transform,
});
this.#applyPostDrawUpdaters(particle);
for (const plugin of this.#drawParticlesCleanupPlugins) {
plugin.drawParticleCleanup?.(context, particle, delta);
}
});
}
drawParticlePlugins(particle, delta) {
this.draw(ctx => {
for (const plugin of this.#drawParticlePlugins) {
this.#drawParticlePlugin(ctx, plugin, particle, delta);
}
});
}
drawParticles(delta) {
const { particles, actualOptions } = this.#container;
this.clear();
particles.update(delta);
this.draw(ctx => {
const width = this.#canvasManager.size.width, height = this.#canvasManager.size.height;
if (this.#backgroundElement) {
try {
ctx.drawImage(this.#backgroundElement, originPoint.x, originPoint.y, width, height);
}
catch {
this.#warnOnce("background-element-draw-error", "Error drawing background element onto canvas");
}
}
const background = actualOptions.background;
if (background.draw) {
try {
background.draw(ctx, delta);
}
catch {
this.#warnOnce("background-draw-error", "Error in background.draw callback");
}
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.BackgroundMask)) {
plugin.canvasPaint?.();
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.CanvasSetup)) {
plugin.drawSettingsSetup?.(ctx, delta);
}
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.PluginContent)) {
plugin.draw?.(ctx, delta);
}
particles.drawParticles(delta);
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.CanvasCleanup)) {
plugin.clearDraw?.(ctx, delta);
plugin.drawSettingsCleanup?.(ctx, delta);
}
});
}
init() {
this.initUpdaters();
this.initPlugins();
this.#resolveBackgroundElement();
this.paint();
}
initPlugins() {
this.#canvasClearPlugins = [];
this.#colorPlugins = [];
this.#drawParticlePlugins = [];
this.#drawParticlesSetupPlugins = [];
this.#drawParticlesCleanupPlugins = [];
for (const layer of Object.values(exports.DrawLayer)) {
if (typeof layer === "number") {
this.#layers[layer] = [];
}
}
for (const plugin of this.#container.plugins) {
if (plugin.particleFillColor ?? plugin.particleStrokeColor) {
this.#colorPlugins.push(plugin);
}
if (plugin.drawParticle) {
this.#drawParticlePlugins.push(plugin);
}
if (plugin.drawParticleSetup) {
this.#drawParticlesSetupPlugins.push(plugin);
}
if (plugin.drawParticleCleanup) {
this.#drawParticlesCleanupPlugins.push(plugin);
}
if (plugin.canvasClear) {
this.#canvasClearPlugins.push(plugin);
}
if (plugin.canvasPaint) {
this.#getLayerPlugins(exports.DrawLayer.BackgroundMask).push(plugin);
}
if (plugin.drawSettingsSetup) {
this.#getLayerPlugins(exports.DrawLayer.CanvasSetup).push(plugin);
}
if (plugin.draw) {
this.#getLayerPlugins(exports.DrawLayer.PluginContent).push(plugin);
}
if (plugin.clearDraw ?? plugin.drawSettingsCleanup) {
this.#getLayerPlugins(exports.DrawLayer.CanvasCleanup).push(plugin);
}
}
}
initUpdaters() {
this.#preDrawUpdaters = [];
this.#postDrawUpdaters = [];
for (const updater of this.#container.particleUpdaters) {
if (updater.afterDraw) {
this.#postDrawUpdaters.push(updater);
}
if (updater.getColorStyles ?? updater.getTransformValues ?? updater.beforeDraw) {
this.#preDrawUpdaters.push(updater);
}
}
}
paint() {
let handled = false;
for (const plugin of this.#getLayerPlugins(exports.DrawLayer.BackgroundMask)) {
handled = plugin.canvasPaint?.() ?? false;
if (handled) {
break;
}
}
if (handled) {
return;
}
this.paintBase();
}
paintBase(baseColor) {
this.draw(ctx => {
ctx.fillStyle = baseColor ?? "rgba(0,0,0,0)";
ctx.fillRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
paintImage(image, opacity) {
this.draw(ctx => {
if (!image) {
return;
}
const prevAlpha = ctx.globalAlpha;
ctx.globalAlpha = opacity;
ctx.drawImage(image, originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
ctx.globalAlpha = prevAlpha;
});
}
setContext(context) {
this.#context = context;
if (this.#context) {
this.#context.globalCompositeOperation = defaultCompositeValue;
}
}
setContextSettings(settings) {
this.#contextSettings = settings;
}
stop() {
this.draw(ctx => {
ctx.clearRect(originPoint.x, originPoint.y, this.#canvasManager.size.width, this.#canvasManager.size.height);
});
}
#applyPostDrawUpdaters(particle) {
for (const updater of this.#postDrawUpdaters) {
updater.afterDraw?.(particle);
}
}
#applyPreDrawUpdaters(ctx, particle, radius, zOpacity, colorStyles, transform) {
for (const updater of this.#preDrawUpdaters) {
if (updater.getColorStyles) {
const { fill, stroke } = updater.getColorStyles(particle, ctx, radius, zOpacity);
if (fill) {
colorStyles.fill = fill;
}
if (stroke) {
colorStyles.stroke = stroke;
}
}
if (updater.getTransformValues) {
const updaterTransform = updater.getTransformValues(particle);
for (const key in updaterTransform) {
setTransformValue(transform, updaterTransform, key);
}
}
updater.beforeDraw?.(particle);
}
}
#drawAfterEffect(drawer, data) {
if (!drawer?.drawAfter) {
return;
}
const { particle } = data;
if (!particle.effect) {
return;
}
drawer.drawAfter(data);
}
#drawBeforeEffect(drawer, data) {
if (!drawer?.drawBefore) {
return;
}
const { particle } = data;
if (!particle.effect) {
return;
}
drawer.drawBefore(data);
}
#drawParticle(data) {
const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, { effectDrawers, shapeDrawers } = container, pos = particle.getPosition(), transformData = particle.getTransformData(transform), drawScale = defaultZoom, drawPosition = {
x: pos.x,
y: pos.y,
};
context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
if (colorStyles.fill) {
context.fillStyle = colorStyles.fill;
}
const fillEnabled = !!particle.fillEnabled, strokeWidth = particle.strokeWidth ?? minStrokeWidth;
context.lineWidth = strokeWidth;
if (colorStyles.stroke) {
context.strokeStyle = colorStyles.stroke;
}
const drawData = {
context,
particle,
radius,
drawRadius: radius * drawScale,
opacity,
delta,
pixelRatio: container.retina.pixelRatio,
fill: fillEnabled,
stroke: strokeWidth > minStrokeWidth,
transformData,
position: { ...pos },
drawPosition,
drawScale,
};
for (const plugin of container.plugins) {
plugin.drawParticleTransform?.(drawData);
}
const effect = particle.effect ? effectDrawers.get(particle.effect) : undefined, shape = particle.shape ? shapeDrawers.get(particle.shape) : undefined;
this.#drawBeforeEffect(effect, drawData);
this.#drawShapeBeforeDraw(shape, drawData);
this.#drawShape(shape, drawData);
this.#drawShapeAfterDraw(shape, drawData);
this.#drawAfterEffect(effect, drawData);
context.resetTransform();
}
#drawParticlePlugin(context, plugin, particle, delta) {
if (!plugin.drawParticle) {
return;
}
plugin.drawParticle(context, particle, delta);
}
#drawShape(drawer, data) {
if (!drawer) {
return;
}
const { context, fill, particle, stroke } = data;
if (!particle.shape) {
return;
}
context.beginPath();
drawer.draw(data);
if (particle.shapeClose) {
context.closePath();
}
if (fill) {
context.fill();
}
if (stroke) {
context.stroke();
}
}
#drawShapeAfterDraw(drawer, data) {
if (!drawer?.afterDraw) {
return;
}
const { particle } = data;
if (!particle.shape) {
return;
}
drawer.afterDraw(data);
}
#drawShapeBeforeDraw(drawer, data) {
if (!drawer?.beforeDraw) {
return;
}
const { particle } = data;
if (!particle.shape) {
return;
}
drawer.beforeDraw(data);
}
#getLayerPlugins(layer) {
return this.#layers[layer];
}
#getPluginParticleColors(particle) {
let fColor, sColor;
for (const plugin of this.#colorPlugins) {
if (!fColor && plugin.particleFillColor) {
fColor = rangeColorToHsl(this.#pluginManager, plugin.particleFillColor(particle));
}
if (!sColor && plugin.particleStrokeColor) {
sColor = rangeColorToHsl(this.#pluginManager, plugin.particleStrokeColor(particle));
}
if (fColor && sColor) {
break;
}
}
this.#reusablePluginColors[fColorIndex] = fColor;
this.#reusablePluginColors[sColorIndex] = sColor;
return this.#reusablePluginColors;
}
#resolveBackgroundElement() {
const background = this.#container.actualOptions.background;
this.#backgroundElement = null;
if (!background.element) {
return;
}
if (typeof background.element === "string") {
if (typeof document !== "undefined") {
const node = document.querySelector(background.element);
if ((typeof HTMLCanvasElement !== "undefined" && node instanceof HTMLCanvasElement) ||
(typeof HTMLVideoElement !== "undefined" && node instanceof HTMLVideoElement) ||
(typeof HTMLImageElement !== "undefined" && node instanceof HTMLImageElement)) {
this.#backgroundElement = node;
}
else if (node) {
this.#warnOnce("background-element-not-supported", `Background element "${background.element}" is not a supported drawable element (canvas, video, or img)`);
}
else {
this.#warnOnce("background-element-not-found", `Background element selector "${background.element}" not found`);
}
}
}
else if ((typeof HTMLCanvasElement !== "undefined" && background.element instanceof HTMLCanvasElement) ||
(typeof OffscreenCanvas !== "undefined" && background.element instanceof OffscreenCanvas) ||
(typeof HTMLVideoElement !== "undefined" && background.element instanceof HTMLVideoElement) ||
(typeof HTMLImageElement !== "undefined" && background.element instanceof HTMLImageElement)) {
this.#backgroundElement = background.element;
}
}
#warnOnce(key, message) {
if (!this.#backgroundWarnings.has(key)) {
this.#backgroundWarnings.add(key);
getLogger().warning(message);
}
}
}
const transferredCanvases = new WeakMap(), getTransferredCanvas = (canvas) => {
const transferredCanvas = transferredCanvases.get(canvas);
if (transferredCanvas) {
return transferredCanvas;
}
if (typeof canvas.transferControlToOffscreen !== "function") {
throw new TypeError("OffscreenCanvas is required but not supported by this browser");
}
try {
const offscreenCanvas = canvas.transferControlToOffscreen();
transferredCanvases.set(canvas, offscreenCanvas);
return offscreenCanvas;
}
catch {
throw new TypeError("OffscreenCanvas transfer failed");
}
}, isHtmlCanvasElement = (canvas) => {
return typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement;
};
function setStyle(canvas, style, important = false) {
if (!style) {
return;
}
const element = canvas, elementStyle = element.style, keys = new Set();
for (let i = 0; i < elementStyle.length; i++) {
const key = elementStyle.item(i);
if (!key) {
continue;
}
keys.add(key);
}
for (let i = 0; i < style.length; i++) {
const key = style.item(i);
if (!key) {
continue;
}
keys.add(key);
}
for (const key of keys) {
const value = style.getPropertyValue(key);
if (value) {
elementStyle.setProperty(key, value, important ? "important" : "");
}
else {
elementStyle.removeProperty(key);
}
}
}
class CanvasManager {
domElement;
render;
renderCanvas;
size;
zoom = defaultZoom;
#container;
#generated;
#hdrMediaListeners;
#mutationObserver;
#originalStyle;
#pluginManager;
#pointerEvents;
#resizePlugins;
#standardSize;
#zoomCenter;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.render = new RenderManager(pluginManager, container, this);
this.#standardSize = {
height: 0,
width: 0,
};
const pxRatio = container.retina.pixelRatio, stdSize = this.#standardSize;
this.size = {
height: stdSize.height * pxRatio,
width: stdSize.width * pxRatio,
};
this.#generated = false;
this.#resizePlugins = [];
this.#pointerEvents = "none";
}
get #fullScreen() {
return this.#container.actualOptions.fullScreen.enable;
}
destroy() {
this.stop();
if (this.#generated) {
const element = this.domElement;
element?.remove();
this.domElement = undefined;
this.renderCanvas = undefined;
}
else {
this.#resetOriginalStyle();
}
this.render.destroy();
this.#resizePlugins = [];
}
getZoomCenter() {
const pxRatio = this.#container.retina.pixelRatio, { width, height } = this.size;
if (this.#zoomCenter) {
return this.#zoomCenter;
}
return {
x: (width * half) / pxRatio,
y: (height * half) / pxRatio,
};
}
init() {
this.#safeMutationObserver(obs => {
obs.disconnect();
});
this.#mutationObserver = safeMutationObserver(records => {
for (const record of records) {
if (record.type === "attributes" && record.attributeName === "style") {
this.#repairStyle();
}
}
});
this.resize();
this.#initStyle();
this.#safeMutationObserver(obs => {
const element = this.domElement;
if (!element || !(element instanceof Node)) {
return;
}
obs.observe(element, { attributes: true });
});
this.initPlugins();
this.#initContext();
this.initBackground();
this.#initHdrListeners();
this.render.init();
}
initBackground() {
const container = this.#container, options = container.actualOptions, background = options.background, element = this.domElement;
if (!element) {
return;
}
const elementStyle = element.style, color = rangeColorToRgb(this.#pluginManager, background.color);
if (color) {
const hdrOptions = container.actualOptions.hdr;
elementStyle.backgroundColor = getStyleFromRgb(color, container.hdr, background.opacity, hdrOptions.peakNits, hdrOptions.mode);
}
else {
elementStyle.backgroundColor = "";
}
elementStyle.backgroundImage = background.image || "";
elementStyle.backgroundPosition = background.position || "";
elementStyle.backgroundRepeat = background.repeat || "";
elementStyle.backgroundSize = background.size || "";
}
initPlugins() {
this.#resizePlugins = [];
for (const plugin of this.#container.plugins) {
if (plugin.resize) {
this.#resizePlugins.push(plugin);
}
}
}
loadCanvas(canvas) {
if (this.#generated && this.domElement) {
this.domElement.remove();
}
const domCanvas = isHtmlCanvasElement(canvas) ? canvas : undefined;
this.domElement = domCanvas;
this.#generated = domCanvas ? domCanvas.dataset[generatedAttribute] === "true" : false;
this.renderCanvas = domCanvas ? getTransferredCanvas(domCanvas) : canvas;
const domElement = this.domElement;
if (domElement) {
domElement.ariaHidden = "true";
this.#originalStyle = cloneStyle(domElement.style);
}
const standardSize = this.#standardSize, renderCanvas = this.renderCanvas;
if (domElement) {
standardSize.height = domElement.offsetHeight;
standardSize.width = domElement.offsetWidth;
}
else {
standardSize.height = renderCanvas.height;
standardSize.width = renderCanvas.width;
}
const pxRatio = this.#container.retina.pixelRatio, retinaSize = this.size;
renderCanvas.height = retinaSize.height = standardSize.height * pxRatio;
renderCanvas.width = retinaSize.width = standardSize.width * pxRatio;
}
resize() {
const element = this.domElement;
if (!element) {
return false;
}
const container = this.#container, renderCanvas = this.renderCanvas;
if (renderCanvas === undefined) {
return false;
}
const currentSize = container.canvas.#standardSize, newSize = {
width: element.offsetWidth,
height: element.offsetHeight,
}, pxRatio = container.retina.pixelRatio, retinaSize = {
width: newSize.width * pxRatio,
height: newSize.height * pxRatio,
};
if (newSize.height === currentSize.height &&
newSize.width === currentSize.width &&
retinaSize.height === renderCanvas.height &&
retinaSize.width === renderCanvas.width) {
return false;
}
const oldSize = { ...currentSize };
currentSize.height = newSize.height;
currentSize.width = newSize.width;
const canvasSize = this.size;
renderCanvas.width = canvasSize.width = retinaSize.width;
renderCanvas.height = canvasSize.height = retinaSize.height;
if (this.#container.started) {
container.particles.setResizeFactor({
width: currentSize.width / oldSize.width,
height: currentSize.height / oldSize.height,
});
}
return true;
}
setPointerEvents(type) {
const element = this.domElement;
if (!element) {
return;
}
this.#pointerEvents = type;
this.#repairStyle();
}
setZoom(zoomLevel, center) {
this.zoom = zoomLevel;
this.#zoomCenter = center;
}
stop() {
this.#safeMutationObserver(obs => {
obs.disconnect();
});
this.#mutationObserver = undefined;
this.#removeHdrListeners();
this.render.stop();
}
async windowResize() {
if (!this.domElement || !this.resize()) {
return;
}
const container = this.#container, needsRefresh = container.updateActualOptions();
container.particles.setDensity();
this.#applyResizePlugins();
if (needsRefresh) {
await container.refresh();
}
}
#applyResizePlugins() {
for (const plugin of this.#resizePlugins) {
plugin.resize?.();
}
}
#initContext() {
const container = this.#container, canSupportHdr = container.actualOptions.hdr.enable &&
safeMatchMedia("(color-gamut: p3)")?.matches &&
safeMatchMedia("(dynamic-range: high)")?.matches;
container.hdr = canSupportHdr ?? false;
container.hdrMode = container.actualOptions.hdr.mode;
container.peakNits = container.actualOptions.hdr.peakNits;
const renderCanvas = this.renderCanvas;
if (!renderCanvas) {
return;
}
this.render.setContextSettings({
alpha: true,
desynchronized: true,
willReadFrequently: false,
...(canSupportHdr
? { colorSpace: "display-p3", colorType: "float16" }
: { colorSpace: "srgb" }),
});
let context;
try {
context = renderCanvas.getContext("2d", this.render.settings);
}
catch {
context = null;
}
if (canSupportHdr && !context) {
container.hdr = false;
const sdrSettings = {
alpha: true,
desynchronized: true,
willReadFrequently: false,
colorSpace: "srgb",
};
this.render.setContextSettings(sdrSettings);
try {
context = renderCanvas.getContext("2d", sdrSettings);
}
catch {
context = null;
}
}
this.render.setContext(context);
}
#initHdrListeners() {
this.#removeHdrListeners();
const p3Query = safeMatchMedia("(color-gamut: p3)"), hdrQuery = safeMatchMedia("(dynamic-range: high)"), handleChange = () => {
this.#recreateRenderCanvas();
this.#initContext();
this.initBackground();
}, listeners = [];
if (p3Query) {
p3Query.addEventListener("change", handleChange);
listeners.push({ handler: handleChange, mql: p3Query });
}
if (hdrQuery) {
hdrQuery.addEventListener("change", handleChange);
listeners.push({ handler: handleChange, mql: hdrQuery });
}
this.#hdrMediaListeners = listeners;
}
#initStyle() {
const element = this.domElement, options = this.#container.actualOptions;
if (!element) {
return;
}
if (this.#fullScreen) {
this.#setFullScreenStyle();
}
else {
this.#resetOriginalStyle();
}
for (const key in options.style) {
if (!key || !(key in options.style)) {
continue;
}
const value = options.style[key];
if (!value) {
continue;
}
element.style.setProperty(key, value, "important");
}
}
#recreateRenderCanvas() {
const renderCanvas = this.renderCanvas;
if (!renderCanvas) {
return;
}
if (this.domElement) {
return;
}
this.renderCanvas = new OffscreenCanvas(renderCanvas.width, renderCanvas.height);
}
#removeHdrListeners() {
if (!this.#hdrMediaListeners) {
return;
}
for (const { handler, mql } of this.#hdrMediaListeners) {
mql.removeEventListener("change", handler);
}
this.#hdrMediaListeners = undefined;
}
#repairStyle() {
const element = this.domElement;
if (!element) {
return;
}
this.#safeMutationObserver(observer => {
observer.disconnect();
});
this.#initStyle();
this.initBackground();
const pointerEvents = this.#pointerEvents;
element.style.pointerEvents = pointerEvents;
element.style.setProperty("pointer-events", pointerEvents);
this.#safeMutationObserver(observer => {
if (!(element instanceof Node)) {
return;
}
observer.observe(element, { attributes: true });
});
}
#resetOriginalStyle() {
const element = this.domElement, originalStyle = this.#originalStyle;
if (!element || !originalStyle) {
return;
}
setStyle(element, originalStyle, true);
}
#safeMutationObserver(callback) {
if (!this.#mutationObserver) {
return;
}
callback(this.#mutationObserver);
}
#setFullScreenStyle() {
const element = this.domElement;
if (!element) {
return;
}
setStyle(element, getFullScreenStyle(this.#container.actualOptions.fullScreen.zIndex), true);
}
}
class EventListeners {
#container;
#handlers;
#resizeObserver;
#resizeTimeout;
constructor(container) {
this.#container = container;
this.#handlers = {
visibilityChange: () => {
this.#handleVisibilityChange();
},
resize: () => {
this.#handleWindowResize();
},
};
}
addListeners() {
this.#manageListeners(true);
}
removeListeners() {
this.#manageListeners(false);
}
#handleVisibilityChange() {
const container = this.#container, options = container.actualOptions;
if (!options.pauseOnBlur) {
return;
}
if (safeDocument().hidden) {
container.pageHidden = true;
container.pause();
}
else {
container.pageHidden = false;
if (container.animationStatus) {
container.play(true);
}
else {
container.draw(true);
}
}
}
#handleWindowResize() {
if (this.#resizeTimeout) {
clearTimeout(this.#resizeTimeout);
this.#resizeTimeout = undefined;
}
const handleResize = async () => {
const canvas = this.#container.canvas;
await canvas.windowResize();
};
this.#resizeTimeout = setTimeout(() => void handleResize(), this.#container.actualOptions.resize.delay * millisecondsToSeconds);
}
#manageListeners(add) {
const handlers = this.#handlers;
this.#manageResize(add);
manageListener(document, visibilityChangeEvent, handlers.visibilityChange, add, false);
}
#manageResize(add) {
const handlers = this.#handlers, container = this.#container, options = container.actualOptions;
if (!options.resize.enable) {
return;
}
if (typeof ResizeObserver === "undefined") {
manageListener(globalThis, resizeEvent, handlers.resize, add);
return;
}
const canvasEl = container.canvas.domElement;
if (this.#resizeObserver && !add) {
if (canvasEl) {
this.#resizeObserver.unobserve(canvasEl);
}
this.#resizeObserver.disconnect();
this.#resizeObserver = undefined;
}
else if (!this.#resizeObserver && add && canvasEl) {
this.#resizeObserver = new ResizeObserver((entries) => {
const entry = entries.find(e => e.target === canvasEl);
if (!entry) {
return;
}
this.#handleWindowResize();
});
this.#resizeObserver.observe(canvasEl);
}
}
}
function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
const effectData = effectOptions.options[effect];
return deepExtend({
close: effectOptions.close,
}, itemFromSingleOrMultiple(effectData, id, reduceDuplicates));
}
function loadShapeData(shape, shapeOptions, id, reduceDuplicates) {
const shapeData = shapeOptions.options[shape];
return deepExtend({
close: shapeOptions.close,
}, itemFromSingleOrMultiple(shapeData, id, reduceDuplicates));
}
function fixOutMode(data) {
if (!isInArray(data.outMode, data.checkModes)) {
return;
}
const diameter = data.radius * double;
if (data.coord > data.maxCoord - diameter) {
data.setCb(-data.radius);
}
else if (data.coord < diameter) {
data.setCb(data.radius);
}
}
function normalizeAngle(angle, modulus) {
const normalized = angle % modulus;
return normalized < defaultAngle ? normalized + modulus : normalized;
}
function initParticleState(particle, id, group) {
particle.id = id;
particle.group = group;
particle.justWarped = false;
particle.effectClose = true;
particle.shapeClose = true;
particle.pathRotation = false;
particle.lastPathTime = 0;
particle.destroyed = false;
particle.unbreakable = false;
particle.isRotating = false;
particle.rotation = 0;
particle.misplaced = false;
particle.retina = {
maxDistance: {},
maxSpeed: 0,
moveDrift: 0,
moveSpeed: 0,
sizeAnimationSpeed: 0,
};
particle.size = {
value: 1,
max: 1,
min: 1,
enable: false,
};
particle.outType = exports.ParticleOutType.normal;
particle.ignoresResizeRatio = true;
}
function resolveParticleOptions(particle, container, pluginManager, overrideOptions) {
const mainOptions = container.actualOptions, particlesOptions = loadParticlesOptions(pluginManager, container, mainOptions.particles), reduceDuplicates = particlesOptions.reduceDuplicates;
particle.effect = itemFromSingleOrMultiple(particlesOptions.effect.type, particle.id, reduceDuplicates);
particle.shape = itemFromSingleOrMultiple(particlesOptions.shape.type, particle.id, reduceDuplicates);
const effectOptions = particlesOptions.effect, shapeOptions = particlesOptions.shape;
if (overrideOptions) {
if (overrideOptions.effect) {
const overrideEffectType = overrideOptions.effect.type;
if (overrideEffectType && overrideEffectType !== particle.effect) {
const effect = itemFromSingleOrMultiple(overrideEffectType, particle.id, reduceDuplicates);
if (effect) {
particle.effect = effect;
}
}
effectOptions.load(overrideOptions.effect);
}
if (overrideOptions.shape) {
const overrideShapeType = overrideOptions.shape.type;
if (overrideShapeType && overrideShapeType !== particle.shape) {
const shape = itemFromSingleOrMultiple(overrideShapeType, particle.id, reduceDuplicates);
if (shape) {
particle.shape = shape;
}
}
shapeOptions.load(overrideOptions.shape);
}
}
if (particle.effect === randomColorValue) {
const availableEffects = [...container.effectDrawers.keys()];
particle.effect = availableEffects[Math.floor(getRandom() * availableEffects.length)];
}
if (particle.shape === randomColorValue) {
const availableShapes = [...container.shapeDrawers.keys()];
particle.shape = availableShapes[Math.floor(getRandom() * availableShapes.length)];
}
particle.effectData = particle.effect
? loadEffectData(particle.effect, effectOptions, particle.id, reduceDuplicates)
: undefined;
particle.shapeData = particle.shape
? loadShapeData(particle.shape, shapeOptions, particle.id, reduceDuplicates)
: undefined;
particlesOptions.load(overrideOptions);
const effectData = particle.effectData, shapeData = particle.shapeData;
if (effectData) {
particlesOptions.load(effectData.particles);
}
if (shapeData) {
particlesOptions.load(shapeData.particles);
}
particle.effectClose = effectData?.close ?? particlesOptions.effect.close;
particle.shapeClose = shapeData?.close ?? particlesOptions.shape.close;
return particlesOptions;
}
function initParticleDrawers(particle, container) {
let effectDrawer, shapeDrawer;
if (particle.effect) {
effectDrawer = container.effectDrawers.get(particle.effect);
}
if (effectDrawer?.loadEffect) {
effectDrawer.loadEffect(particle);
}
if (particle.shape) {
shapeDrawer = container.shapeDrawers.get(particle.shape);
}
if (shapeDrawer?.loadShape) {
shapeDrawer.loadShape(particle);
}
const sideCountFunc = shapeDrawer?.getSidesCount;
if (sideCountFunc) {
particle.sides = sideCountFunc(particle);
}
}
function runUpdaterPreInit(updaters, particle) {
for (const updater of updaters) {
updater.preInit?.(particle);
}
}
function runUpdaterInit(updaters, particle) {
for (const updater of updaters) {
updater.init(particle);
}
}
function runDrawerInit(container, particle) {
const shapeDrawer = particle.shape ? container.shapeDrawers.get(particle.shape) : undefined, effectDrawer = particle.effect ? container.effectDrawers.get(particle.effect) : undefined;
effectDrawer?.particleInit?.(container, particle);
shapeDrawer?.particleInit?.(container, particle);
}
function runParticleCreatedPlugins(container, particle) {
for (const plugin of container.particleCreatedPlugins) {
plugin.particleCreated?.(particle);
}
}
class Particle {
backColor;
destroyed;
direction;
effect;
effectClose;
effectData;
fillColor;
fillEnabled;
fillOpacity;
group;
id;
ignoresResizeRatio;
initialPosition;
initialVelocity;
isRotating;
justWarped;
lastPathTime;
misplaced;
moveCenter;
offset;
opacity;
options;
outType;
pathRotation;
position;
randomIndexData;
retina;
roll;
rotation;
shape;
shapeClose;
shapeData;
sides;
size;
spawning;
strokeColor;
strokeOpacity;
strokeWidth;
unbreakable;
velocity;
zIndexFactor;
#cachedOpacityData = {
fillOpacity: defaultOpacity$1,
opacity: defaultOpacity$1,
strokeOpacity: defaultOpacity$1,
};
#cachedPosition = Vector3d.origin;
#cachedRotateData = { sin: 0, cos: 0 };
#cachedTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};
#container;
#modifiers = [];
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
}
addModifier(modifier) {
this.#modifiers.push(modifier);
this.#modifiers.sort((a, b) => a.priority - b.priority);
}
clearModifiers() {
this.#modifiers.length = 0;
}
destroy(override) {
if (this.unbreakable || this.destroyed) {
return;
}
this.destroyed = true;
this.clearModifiers();
const container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined;
shapeDrawer?.particleDestroy?.(this);
for (const plugin of container.particleDestroyedPlugins) {
plugin.particleDestroyed?.(this, override);
}
for (const updater of container.particleUpdaters) {
updater.particleDestroyed?.(this, override);
}
this.#container.dispatchEvent(exports.EventType.particleDestroyed, {
particle: this,
});
}
draw(delta) {
const container = this.#container, render = container.canvas.render;
render.drawParticlePlugins(this, delta);
render.drawParticle(this, delta);
}
getAngle() {
return this.rotation + (this.pathRotation ? this.velocity.angle : defaultAngle);
}
getFillColor() {
return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.fillColor), m => m.fillColor));
}
getMass() {
return this.getRadius() ** squareExp * Math.PI * half;
}
getModifier(id) {
return this.#modifiers.find(m => m.id === id);
}
getOpacity() {
const zIndexOptions = this.options.zIndex, zIndexFactor = zIndexFactorOffset - this.zIndexFactor, zOpacityFactor = zIndexFactor ** zIndexOptions.opacityRate, baseOpacity = getRangeValue(this.opacity?.value ?? defaultOpacity$1), modifierOpacity = this.#applyModifiers(undefined, m => m.opacity), opacity = modifierOpacity ?? baseOpacity, fillOpacity = this.fillOpacity ?? defaultOpacity$1, strokeOpacity = this.strokeOpacity ?? defaultOpacity$1;
this.#cachedOpacityData.fillOpacity = fillOpacity;
this.#cachedOpacityData.opacity = opacity * zOpacityFactor;
this.#cachedOpacityData.strokeOpacity = strokeOpacity;
return this.#cachedOpacityData;
}
getPosition() {
this.#cachedPosition.x = this.position.x + this.offset.x;
this.#cachedPosition.y = this.position.y + this.offset.y;
this.#cachedPosition.z = this.position.z;
return this.#cachedPosition;
}
getRadius() {
return this.#applyModifiers(this.size.value, m => m.radius);
}
getRotateData() {
const angle = this.getAngle();
this.#cachedRotateData.sin = Math.sin(angle);
this.#cachedRotateData.cos = Math.cos(angle);
return this.#cachedRotateData;
}
getStrokeColor() {
return this.#getRollColor(this.#applyModifiers(getHslFromAnimation(this.strokeColor), m => m.strokeColor));
}
getTransformData(externalTransform) {
const rotateData = this.getRotateData(), rotating = this.isRotating;
this.#cachedTransform.a = rotateData.cos * (externalTransform.a ?? defaultTransform.a);
this.#cachedTransform.b = rotating
? rotateData.sin * (externalTransform.b ?? identity$1)
: (externalTransform.b ?? defaultTransform.b);
this.#cachedTransform.c = rotating
? -rotateData.sin * (externalTransform.c ?? identity$1)
: (externalTransform.c ?? defaultTransform.c);
this.#cachedTransform.d = rotateData.cos * (externalTransform.d ?? defaultTransform.d);
return this.#cachedTransform;
}
init(id, position, overrideOptions, group) {
const container = this.#container;
initParticleState(this, id, group);
this.options = resolveParticleOptions(this, container, this.#pluginManager, overrideOptions);
container.retina.initParticle(this);
runUpdaterPreInit(container.particleUpdaters, this);
this.#initPosition(position);
this.initialVelocity = this.#calculateVelocity();
this.velocity = this.initialVelocity.copy();
this.zIndexFactor = this.position.z / container.zLayers;
this.sides = 24;
initParticleDrawers(this, container);
this.spawning = false;
runUpdaterInit(container.particleUpdaters, this);
runDrawerInit(container, this);
runParticleCreatedPlugins(container, this);
}
isInsideCanvas(direction) {
return this.#getInsideCanvasResult({ direction }).inside;
}
isInsideCanvasForOutMode(outMode, direction) {
return this.#getInsideCanvasResult({ direction, outMode }).inside;
}
isShowingBack() {
if (!this.roll) {
return false;
}
const angle = this.roll.angle;
if (this.roll.horizontal && this.roll.vertical) {
const adjustedAngle = normalizeAngle(angle, doublePI);
return adjustedAngle >= Math.PI * half && adjustedAngle < Math.PI * triple * half;
}
if (this.roll.horizontal) {
const adjustedAngle = normalizeAngle(angle + Math.PI * half, doublePI);
return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
}
if (this.roll.vertical) {
const adjustedAngle = normalizeAngle(angle, doublePI);
return adjustedAngle >= Math.PI && adjustedAngle < Math.PI * double;
}
return false;
}
isVisible() {
return !this.destroyed && !this.spawning && this.isInsideCanvas();
}
removeModifier(id) {
const idx = this.#modifiers.findIndex(m => m.id === id);
if (idx >= defaultAngle) {
this.#modifiers.splice(idx, identity$1);
}
}
reset() {
for (const updater of this.#container.particleUpdaters) {
updater.reset?.(this);
}
}
#applyModifiers(base, getter) {
let value = base;
for (const mod of this.#modifiers) {
if (mod.enabled) {
const override = getter(mod);
if (override !== undefined) {
value = override;
}
}
}
return value;
}
#calcPosition(position, zIndex) {
let tryCount = defaultRetryCount, posVec = position ? Vector3d.create(position.x, position.y, zIndex) : undefined;
const container = this.#container, plugins = container.particlePositionPlugins, outModes = this.options.move.outModes, radius = this.getRadius(), canvasSize = container.canvas.size;
for (;;) {
for (const plugin of plugins) {
const pluginPos = plugin.particlePosition?.(posVec, this);
if (pluginPos) {
return Vector3d.create(pluginPos.x, pluginPos.y, zIndex);
}
}
const exactPosition = calcExactPositionOrRandomFromSize({
size: canvasSize,
position: posVec,
}), pos = Vector3d.create(exactPosition.x, exactPosition.y, zIndex);
this.#fixHorizontal(pos, radius, outModes.left ?? outModes.default);
this.#fixHorizontal(pos, radius, outModes.right ?? outModes.default);
this.#fixVertical(pos, radius, outModes.top ?? outModes.default);
this.#fixVertical(pos, radius, outModes.bottom ?? outModes.default);
let isValidPosition = true;
for (const plugin of container.particles.checkParticlePositionPlugins) {
isValidPosition = plugin.checkParticlePosition?.(this, pos, tryCount) ?? true;
if (!isValidPosition) {
break;
}
}
if (isValidPosition) {
return pos;
}
tryCount += tryCountIncrement;
posVec = undefined;
}
}
#calculateVelocity() {
const moveOptions = this.options.move, baseVelocity = getParticleBaseVelocity(this.direction), res = baseVelocity.copy();
if (moveOptions.direction === exports.MoveDirection.inside || moveOptions.direction === exports.MoveDirection.outside) {
return res;
}
const rad = degToRad(getRangeValue(moveOptions.angle.value)), radOffset = degToRad(getRangeValue(moveOptions.angle.offset)), range = {
left: radOffset - rad * half,
right: radOffset + rad * half,
};
if (!moveOptions.straight) {
res.angle += randomInRangeValue(setRangeValue(range.left, range.right));
}
if (moveOptions.random && typeof moveOptions.speed === "number") {
res.length *= getRandom();
}
return res;
}
#fixHorizontal(pos, radius, outMode) {
fixOutMode({
outMode,
checkModes: [exports.OutMode.bounce],
coord: pos.x,
maxCoord: this.#container.canvas.size.width,
setCb: (value) => (pos.x += value),
radius,
});
}
#fixVertical(pos, radius, outMode) {
fixOutMode({
outMode,
checkModes: [exports.OutMode.bounce],
coord: pos.y,
maxCoord: this.#container.canvas.size.height,
setCb: (value) => (pos.y += value),
radius,
});
}
#getDefaultInsideCanvasResult(direction, outMode) {
const radius = this.getRadius(), canvasSize = this.#container.canvas.size, position = this.position, isBounce = outMode === exports.OutMode.bounce;
if (direction === exports.OutModeDirection.bottom) {
return {
inside: isBounce ? position.y + radius < canvasSize.height : position.y - radius < canvasSize.height,
reason: "default",
};
}
if (direction === exports.OutModeDirection.left) {
return {
inside: isBounce ? position.x - radius > defaultAngle : position.x + radius > defaultAngle,
reason: "default",
};
}
if (direction === exports.OutModeDirection.right) {
return {
inside: isBounce ? position.x + radius < canvasSize.width : position.x - radius < canvasSize.width,
reason: "default",
};
}
if (direction === exports.OutModeDirection.top) {
return {
inside: isBounce ? position.y - radius > defaultAngle : position.y + radius > defaultAngle,
reason: "default",
};
}
return {
inside: position.x >= -radius &&
position.y >= -radius &&
position.y <= canvasSize.height + radius &&
position.x <= canvasSize.width + radius,
reason: "default",
};
}
#getInsideCanvasCallbackData(direction, outMode) {
return {
canvasSize: this.#container.canvas.size,
direction,
outMode,
particle: this,
radius: this.getRadius(),
};
}
#getInsideCanvasResult(data) {
const defaultResult = this.#getDefaultInsideCanvasResult(data.direction, data.outMode), container = this.#container, shapeDrawer = this.shape ? container.shapeDrawers.get(this.shape) : undefined, effectDrawer = this.effect ? container.effectDrawers.get(this.effect) : undefined, shapeCheck = shapeDrawer?.isInsideCanvas, effectCheck = effectDrawer?.isInsideCanvas;
if (!shapeCheck && !effectCheck) {
return defaultResult;
}
const callbackData = this.#getInsideCanvasCallbackData(data.direction, data.outMode), shapeResult = shapeCheck ? this.#normalizeInsideCanvasResult(shapeCheck(callbackData), "shape") : undefined, effectResult = effectCheck ? this.#normalizeInsideCanvasResult(effectCheck(callbackData), "effect") : undefined;
if (shapeResult && effectResult) {
const margin = Math.max(shapeResult.margin ?? defaultAngle, effectResult.margin ?? defaultAngle);
return {
inside: shapeResult.inside && effectResult.inside,
margin: margin > defaultAngle ? margin : undefined,
reason: "combined",
};
}
return shapeResult ?? effectResult ?? defaultResult;
}
#getRollColor(color) {
if (!color || !this.roll || (!this.backColor && !this.roll.alter)) {
return color;
}
if (!this.isShowingBack()) {
return color;
}
if (this.backColor) {
return this.backColor;
}
if (this.roll.alter) {
return alterHsl(color, this.roll.alter.type, this.roll.alter.value);
}
return color;
}
#initPosition(position) {
const container = this.#container, zIndexValue = Math.floor(getRangeValue(this.options.zIndex.value)), initialPosition = this.#calcPosition(position, clamp(zIndexValue, minZ, container.zLayers));
if (!initialPosition) {
throw new Error("a valid position cannot be found for particle");
}
this.position = initialPosition;
this.initialPosition = this.position.copy();
const canvasSize = container.canvas.size;
this.moveCenter = {
...getPosition(this.options.move.center, canvasSize),
radius: this.options.move.center.radius,
mode: this.options.move.center.mode,
};
this.direction = getParticleDirectionAngle(this.options.move.direction, this.position, this.moveCenter);
switch (this.options.move.direction) {
case exports.MoveDirection.inside:
this.outType = exports.ParticleOutType.inside;
break;
case exports.MoveDirection.outside:
this.outType = exports.ParticleOutType.outside;
break;
}
this.offset = Vector.origin;
}
#normalizeInsideCanvasResult(result, reason) {
if (typeof result === "boolean") {
return {
inside: result,
reason,
};
}
return {
inside: result.inside,
margin: result.margin,
reason: result.reason ?? reason,
};
}
}
class SpatialHashGrid {
#cellSize;
#cells = new Map();
#circlePool = [];
#circlePoolIdx;
#pendingCellSize;
#rectanglePool = [];
#rectanglePoolIdx;
constructor(cellSize) {
this.#cellSize = cellSize;
this.#circlePoolIdx = 0;
this.#rectanglePoolIdx = 0;
}
clear() {
this.#cells.clear();
const pendingCellSize = this.#pendingCellSize;
if (pendingCellSize) {
this.#cellSize = pendingCellSize;
}
this.#pendingCellSize = undefined;
}
insert(particle) {
const { x, y } = particle.getPosition(), key = this.#cellKeyFromCoords(x, y);
if (!this.#cells.has(key)) {
this.#cells.set(key, []);
}
this.#cells.get(key)?.push(particle);
}
query(range, check, out = []) {
const bounds = this.#getRangeBounds(range);
if (!bounds) {
return out;
}
const minCellX = Math.floor(bounds.minX / this.#cellSize), maxCellX = Math.floor(bounds.maxX / this.#cellSize), minCellY = Math.floor(bounds.minY / this.#cellSize), maxCellY = Math.floor(bounds.maxY / this.#cellSize);
for (let cx = minCellX; cx <= maxCellX; cx++) {
for (let cy = minCellY; cy <= maxCellY; cy++) {
const key = `${cx}_${cy}`, cellParticles = this.#cells.get(key);
if (!cellParticles) {
continue;
}
for (const p of cellParticles) {
if (check && !check(p)) {
continue;
}
if (range.contains(p.getPosition())) {
out.push(p);
}
}
}
}
return out;
}
queryCircle(position, radius, check, out = []) {
const circle = this.#acquireCircle(position.x, position.y, radius), result = this.query(circle, check, out);
this.#releaseShapes();
return result;
}
queryRectangle(position, size, check, out = []) {
const rect = this.#acquireRectangle(position.x, position.y, size.width, size.height), result = this.query(rect, check, out);
this.#releaseShapes();
return result;
}
setCellSize(cellSize) {
this.#pendingCellSize = cellSize;
}
#acquireCircle(x, y, r) {
return (this.#circlePool[this.#circlePoolIdx++] ??= new Circle(x, y, r)).reset(x, y, r);
}
#acquireRectangle(x, y, w, h) {
return (this.#rectanglePool[this.#rectanglePoolIdx++] ??= new Rectangle(x, y, w, h)).reset(x, y, w, h);
}
#cellKeyFromCoords(x, y) {
const cellX = Math.floor(x / this.#cellSize), cellY = Math.floor(y / this.#cellSize);
return `${cellX}_${cellY}`;
}
#getRangeBounds(range) {
if (range instanceof Circle) {
const r = range.radius, { x, y } = range.position;
return {
minX: x - r,
maxX: x + r,
minY: y - r,
maxY: y + r,
};
}
if (range instanceof Rectangle) {
const { x, y } = range.position, { width, height } = range.size;
return {
minX: x,
maxX: x + width,
minY: y,
maxY: y + height,
};
}
return null;
}
#releaseShapes() {
this.#circlePoolIdx = 0;
this.#rectanglePoolIdx = 0;
}
}
class ParticlesManager {
checkParticlePositionPlugins;
grid;
#array;
#container;
#groupLimits;
#limit;
#nextId;
#particleBuckets;
#particleResetPlugins;
#particleUpdatePlugins;
#pluginManager;
#pool;
#postParticleUpdatePlugins;
#postUpdatePlugins;
#resizeFactor;
#updatePlugins;
#zBuckets;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#nextId = 0;
this.#array = [];
this.#pool = [];
this.#limit = 0;
this.#groupLimits = new Map();
this.#particleBuckets = new Map();
this.#zBuckets = this.#createBuckets(this.#container.zLayers);
this.grid = new SpatialHashGrid(spatialHashGridCellSize);
this.checkParticlePositionPlugins = [];
this.#particleResetPlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#postParticleUpdatePlugins = [];
this.#updatePlugins = [];
}
get count() {
return this.#array.length;
}
addParticle(position, overrideOptions, group, initializer) {
const limitMode = this.#container.actualOptions.particles.number.limit.mode, limit = group === undefined ? this.#limit : (this.#groupLimits.get(group) ?? this.#limit), currentCount = this.count;
if (limit > minLimit) {
switch (limitMode) {
case exports.LimitMode.delete: {
const countToRemove = currentCount + countOffset - limit;
if (countToRemove > minCount) {
this.removeQuantity(countToRemove);
}
break;
}
case exports.LimitMode.wait:
if (currentCount >= limit) {
return;
}
break;
}
}
try {
const particle = this.#pool.pop() ?? new Particle(this.#pluginManager, this.#container);
particle.init(this.#nextId, position, overrideOptions, group);
let canAdd = true;
if (initializer) {
canAdd = initializer(particle);
}
if (!canAdd) {
this.#pool.push(particle);
return;
}
this.#array.push(particle);
this.#insertParticleIntoBucket(particle);
this.#nextId++;
this.#container.dispatchEvent(exports.EventType.particleAdded, {
particle,
});
return particle;
}
catch (e) {
getLogger().warning(`error adding particle: ${e}`);
}
return undefined;
}
clear() {
this.#array = [];
this.#particleBuckets.clear();
this.#resetBuckets(this.#container.zLayers);
}
destroy() {
this.#array = [];
this.#pool.length = 0;
this.#particleBuckets.clear();
this.#zBuckets = [];
this.checkParticlePositionPlugins = [];
this.#particleResetPlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#postParticleUpdatePlugins = [];
this.#updatePlugins = [];
}
drawParticles(delta) {
for (let i = this.#zBuckets.length - one; i >= minIndex; i--) {
const bucket = this.#zBuckets[i];
if (!bucket) {
continue;
}
for (const particle of bucket) {
particle.draw(delta);
}
}
}
filter(condition) {
return this.#array.filter(condition);
}
find(condition) {
return this.#array.find(condition);
}
get(index) {
return this.#array[index];
}
async init() {
const container = this.#container, options = container.actualOptions;
this.checkParticlePositionPlugins = [];
this.#updatePlugins = [];
this.#particleUpdatePlugins = [];
this.#postUpdatePlugins = [];
this.#particleResetPlugins = [];
this.#postParticleUpdatePlugins = [];
this.#particleBuckets.clear();
this.#resetBuckets(container.zLayers);
this.grid = new SpatialHashGrid(spatialHashGridCellSize * container.retina.pixelRatio);
for (const plugin of container.plugins) {
if (plugin.redrawInit) {
await plugin.redrawInit();
}
if (plugin.checkParticlePosition) {
this.checkParticlePositionPlugins.push(plugin);
}
if (plugin.update) {
this.#updatePlugins.push(plugin);
}
if (plugin.particleUpdate) {
this.#particleUpdatePlugins.push(plugin);
}
if (plugin.postUpdate) {
this.#postUpdatePlugins.push(plugin);
}
if (plugin.particleReset) {
this.#particleResetPlugins.push(plugin);
}
if (plugin.postParticleUpdate) {
this.#postParticleUpdatePlugins.push(plugin);
}
}
await this.#container.initDrawersAndUpdaters();
for (const drawer of this.#container.effectDrawers.values()) {
await drawer.init?.(container);
}
for (const drawer of this.#container.shapeDrawers.values()) {
await drawer.init?.(container);
}
let handled = false;
for (const plugin of container.plugins) {
handled = plugin.particlesInitialization?.() ?? handled;
if (handled) {
break;
}
}
if (!handled) {
const particlesOptions = options.particles, groups = particlesOptions.groups;
for (const group in groups) {
const groupOptions = groups[group];
if (!groupOptions) {
continue;
}
for (let i = this.count, j = 0; j < groupOptions.number.value && i < particlesOptions.number.value; i++, j++) {
this.addParticle(undefined, groupOptions, group);
}
}
for (let i = this.count; i < particlesOptions.number.value; i++) {
this.addParticle();
}
}
}
push(nb, position, overrideOptions, group) {
for (let i = 0; i < nb; i++) {
this.addParticle(position, overrideOptions, group);
}
}
async redraw() {
this.clear();
await this.init();
this.#container.canvas.render.drawParticles({ value: 0, factor: 0 });
}
remove(particle, group, override) {
this.removeAt(this.#array.indexOf(particle), undefined, group, override);
}
removeAt(index, quantity = defaultRemoveQuantity, group, override) {
if (index < minIndex || index > this.count) {
return;
}
let deleted = 0;
for (let i = index; deleted < quantity && i < this.count; i++) {
if (this.#removeParticle(i, group, override)) {
i--;
deleted++;
}
}
}
removeQuantity(quantity, group) {
this.removeAt(minIndex, quantity, group);
}
setDensity() {
const options = this.#container.actualOptions, groups = options.particles.groups;
let pluginsCount = 0;
for (const plugin of this.#container.plugins) {
if (plugin.particlesDensityCount) {
pluginsCount += plugin.particlesDensityCount();
}
}
for (const group in groups) {
const groupData = groups[group];
if (!groupData) {
continue;
}
const groupDataOptions = loadParticlesOptions(this.#pluginManager, this.#container, groupData);
this.#applyDensity(groupDataOptions, pluginsCount, group);
}
this.#applyDensity(options.particles, pluginsCount);
}
setResizeFactor(factor) {
this.#resizeFactor = factor;
}
update(delta) {
this.grid.clear();
for (const plugin of this.#updatePlugins) {
plugin.update?.(delta);
}
const particlesToDelete = this.#updateParticlesPhase1(delta);
for (const plugin of this.#postUpdatePlugins) {
plugin.postUpdate?.(delta);
}
this.#updateParticlesPhase2(delta, particlesToDelete);
if (particlesToDelete.size) {
for (const particle of particlesToDelete) {
this.remove(particle);
}
}
this.#resizeFactor = undefined;
}
#addToPool(...particles) {
this.#pool.push(...particles);
}
#applyDensity(options, pluginsCount, group, groupOptions) {
const numberOptions = options.number;
if (!numberOptions.density.enable) {
if (group === undefined) {
this.#limit = numberOptions.limit.value;
}
else if (groupOptions?.number.limit.value ?? numberOptions.limit.value) {
this.#groupLimits.set(group, groupOptions?.number.limit.value ?? numberOptions.limit.value);
}
return;
}
const densityFactor = this.#initDensityFactor(numberOptions.density), optParticlesNumber = numberOptions.value, optParticlesLimit = numberOptions.limit.value > minLimit ? numberOptions.limit.value : optParticlesNumber, particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + pluginsCount, particlesCount = Math.min(this.count, this.filter(t => t.group === group).length);
if (group === undefined) {
this.#limit = numberOptions.limit.value * densityFactor;
}
else {
this.#groupLimits.set(group, numberOptions.limit.value * densityFactor);
}
if (particlesCount < particlesNumber) {
this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
}
else if (particlesCount > particlesNumber) {
this.removeQuantity(particlesCount - particlesNumber, group);
}
}
#createBuckets(zLayers) {
const bucketCount = Math.max(Math.floor(zLayers), one);
return Array.from({ length: bucketCount }, () => []);
}
#getBucketIndex(zIndex) {
const maxBucketIndex = this.#zBuckets.length - one;
if (maxBucketIndex <= minIndex) {
return minIndex;
}
return Math.min(Math.max(Math.floor(zIndex), minIndex), maxBucketIndex);
}
#initDensityFactor(densityOptions) {
const container = this.#container;
if (!densityOptions.enable) {
return defaultDensityFactor;
}
const canvasSize = container.canvas.size, pxRatio = container.retina.pixelRatio;
if (!canvasSize.width || !canvasSize.height) {
return defaultDensityFactor;
}
return ((canvasSize.width * canvasSize.height) / (densityOptions.height * densityOptions.width * pxRatio ** squareExp));
}
#insertParticleIntoBucket(particle) {
const bucketIndex = this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
if (!bucket) {
return;
}
bucket.push(particle);
this.#particleBuckets.set(particle.id, bucketIndex);
}
#removeParticle(index, group, override) {
const particle = this.#array[index];
if (!particle) {
return false;
}
if (particle.group !== group) {
return false;
}
this.#array.splice(index, deleteCount);
this.#removeParticleFromBucket(particle);
particle.destroy(override);
this.#container.dispatchEvent(exports.EventType.particleRemoved, {
particle,
});
this.#addToPool(particle);
return true;
}
#removeParticleFromBucket(particle) {
const bucketIndex = this.#particleBuckets.get(particle.id) ?? this.#getBucketIndex(particle.position.z), bucket = this.#zBuckets[bucketIndex];
if (!bucket) {
this.#particleBuckets.delete(particle.id);
return;
}
const idx = bucket.findIndex(p => p.id === particle.id);
if (idx >= minIndex) {
bucket.splice(idx, deleteCount);
}
this.#particleBuckets.delete(particle.id);
}
#resetBuckets(zLayers) {
const bucketCount = Math.max(Math.floor(zLayers), one);
if (this.#zBuckets.length !== bucketCount) {
this.#zBuckets = this.#createBuckets(bucketCount);
return;
}
for (const bucket of this.#zBuckets) {
bucket.length = minIndex;
}
}
#updateParticleBucket(particle) {
const newBucketIndex = this.#getBucketIndex(particle.position.z), currentBucketIndex = this.#particleBuckets.get(particle.id);
if (currentBucketIndex === undefined) {
this.#insertParticleIntoBucket(particle);
return;
}
if (currentBucketIndex === newBucketIndex) {
return;
}
const currentBucket = this.#zBuckets[currentBucketIndex];
if (currentBucket) {
const idx = currentBucket.findIndex(p => p.id === particle.id);
if (idx >= minIndex) {
currentBucket.splice(idx, deleteCount);
}
}
const newBucket = this.#zBuckets[newBucketIndex];
if (!newBucket) {
this.#particleBuckets.set(particle.id, newBucketIndex);
return;
}
newBucket.push(particle);
if (newBucket.length >= double) {
const prev = newBucket[newBucket.length - double];
if (prev && particle.id < prev.id) {
newBucket.sort((a, b) => a.id - b.id);
}
}
this.#particleBuckets.set(particle.id, newBucketIndex);
}
#updateParticlesPhase1(delta) {
const particlesToDelete = new Set(), resizeFactor = this.#resizeFactor;
for (const particle of this.#array) {
if (resizeFactor && !particle.ignoresResizeRatio) {
particle.position.x *= resizeFactor.width;
particle.position.y *= resizeFactor.height;
particle.initialPosition.x *= resizeFactor.width;
particle.initialPosition.y *= resizeFactor.height;
}
particle.ignoresResizeRatio = false;
for (const plugin of this.#particleResetPlugins) {
plugin.particleReset?.(particle);
}
for (const plugin of this.#particleUpdatePlugins) {
if (particle.destroyed) {
break;
}
plugin.particleUpdate?.(particle, delta);
}
if (particle.destroyed) {
particlesToDelete.add(particle);
continue;
}
this.grid.insert(particle);
}
return particlesToDelete;
}
#updateParticlesPhase2(delta, particlesToDelete) {
for (const particle of this.#array) {
if (particle.destroyed) {
particlesToDelete.add(particle);
continue;
}
for (const updater of this.#container.particleUpdaters) {
updater.update(particle, delta);
}
if (!particle.spawning) {
for (const plugin of this.#postParticleUpdatePlugins) {
plugin.postParticleUpdate?.(particle, delta);
}
}
this.#updateParticleBucket(particle);
}
}
}
class Retina {
pixelRatio;
reduceFactor;
#container;
constructor(container) {
this.#container = container;
this.pixelRatio = defaultRatio;
this.reduceFactor = defaultReduceFactor;
}
init() {
const container = this.#container, options = container.actualOptions;
this.pixelRatio = options.detectRetina ? devicePixelRatio : defaultRatio;
this.reduceFactor = defaultReduceFactor;
const ratio = this.pixelRatio, canvas = container.canvas, element = canvas.domElement;
if (element) {
canvas.size.width = element.offsetWidth * ratio;
canvas.size.height = element.offsetHeight * ratio;
}
}
initParticle(particle) {
const options = particle.options, ratio = this.pixelRatio, moveOptions = options.move, moveDistance = moveOptions.distance, props = particle.retina;
props.maxSpeed = getRangeValue(moveOptions.gravity.maxSpeed) * ratio;
props.moveDrift = getRangeValue(moveOptions.drift) * ratio;
props.moveSpeed = getRangeValue(moveOptions.speed) * ratio;
const maxDistance = props.maxDistance;
maxDistance.horizontal = moveDistance.horizontal === undefined ? undefined : moveDistance.horizontal * ratio;
maxDistance.vertical = moveDistance.vertical === undefined ? undefined : moveDistance.vertical * ratio;
}
}
function guardCheck(container) {
return !container.destroyed;
}
function updateDelta(delta, value, fpsLimit = defaultFps, smooth = false) {
delta.value = value;
delta.factor = smooth ? defaultFps / fpsLimit : (defaultFps * value) / millisecondsToSeconds;
}
function loadContainerOptions(pluginManager, container, ...sourceOptionsArr) {
const options = new Options(pluginManager, container);
loadOptions(options, ...sourceOptionsArr);
return options;
}
class Container {
actualOptions;
canvas;
destroyed;
effectDrawers;
fpsLimit;
hdr;
hdrMode;
id;
pageHidden;
particleCreatedPlugins;
particleDestroyedPlugins;
particlePositionPlugins;
particleUpdaters;
particles;
peakNits;
plugins;
retina;
shapeDrawers;
started;
zLayers;
#delay;
#delayTimeout;
#delta = { value: 0, factor: 0 };
#dispatchCallback;
#drawAnimationFrame;
#duration;
#eventListeners;
#firstStart;
#initialSourceOptions;
#lastFrameTime;
#lifeTime;
#onDestroy;
#options;
#paused;
#pluginManager;
#smooth;
#sourceOptions;
constructor(params) {
const { dispatchCallback, pluginManager, id, onDestroy, sourceOptions } = params;
this.#pluginManager = pluginManager;
this.#dispatchCallback = dispatchCallback;
this.#onDestroy = onDestroy;
this.id = Symbol(id);
this.fpsLimit = 120;
this.hdr = false;
this.hdrMode = exports.HdrMode.standard;
this.peakNits = 400;
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.effectDrawers = new Map();
this.shapeDrawers = new Map();
this.particleUpdaters = [];
this.retina = new Retina(this);
this.canvas = new CanvasManager(this.#pluginManager, this);
this.particles = new ParticlesManager(this.#pluginManager, this);
this.plugins = [];
this.particleDestroyedPlugins = [];
this.particleCreatedPlugins = [];
this.particlePositionPlugins = [];
this.#options = loadContainerOptions(this.#pluginManager, this);
this.actualOptions = loadContainerOptions(this.#pluginManager, this);
this.#eventListeners = new EventListeners(this);
this.dispatchEvent(exports.EventType.containerBuilt);
}
get animationStatus() {
return !this.#paused && !this.pageHidden && guardCheck(this);
}
get options() {
return this.#options;
}
get sourceOptions() {
return this.#sourceOptions;
}
addLifeTime(value) {
this.#lifeTime += value;
}
alive() {
return !this.#duration || this.#lifeTime <= this.#duration;
}
destroy(remove = true) {
if (!guardCheck(this)) {
return;
}
this.stop();
this.particles.destroy();
this.canvas.destroy();
for (const [, effectDrawer] of this.effectDrawers) {
effectDrawer.destroy?.(this);
}
for (const [, shapeDrawer] of this.shapeDrawers) {
shapeDrawer.destroy?.(this);
}
for (const plugin of this.plugins) {
plugin.destroy?.();
}
this.effectDrawers = new Map();
this.shapeDrawers = new Map();
this.particleUpdaters = [];
this.plugins.length = 0;
this.#pluginManager.clearPlugins(this);
this.destroyed = true;
this.#onDestroy(remove);
this.dispatchEvent(exports.EventType.containerDestroyed);
}
dispatchEvent(type, data) {
this.#dispatchCallback(type, {
container: this,
data,
});
}
draw(force) {
if (!guardCheck(this)) {
return;
}
let refreshTime = force;
this.#drawAnimationFrame = animate((timestamp) => {
if (refreshTime) {
this.#lastFrameTime = undefined;
refreshTime = false;
}
this.#nextFrame(timestamp);
});
}
async export(type, options = {}) {
for (const plugin of this.plugins) {
if (!plugin.export) {
continue;
}
const res = await plugin.export(type, options);
if (!res.supported) {
continue;
}
return res.blob;
}
getLogger().error(`Export plugin with type ${type} not found`);
return undefined;
}
async init() {
if (!guardCheck(this)) {
return;
}
const allContainerPlugins = new Map();
for (const plugin of this.#pluginManager.plugins) {
const containerPlugin = await plugin.getPlugin(this);
if (containerPlugin.preInit) {
await containerPlugin.preInit();
}
allContainerPlugins.set(plugin, containerPlugin);
}
await this.initDrawersAndUpdaters();
this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
this.plugins.length = 0;
this.particleDestroyedPlugins.length = 0;
this.particleCreatedPlugins.length = 0;
this.particlePositionPlugins.length = 0;
for (const [plugin, containerPlugin] of allContainerPlugins) {
if (plugin.needsPlugin(this.actualOptions)) {
this.plugins.push(containerPlugin);
if (containerPlugin.particleCreated) {
this.particleCreatedPlugins.push(containerPlugin);
}
if (containerPlugin.particleDestroyed) {
this.particleDestroyedPlugins.push(containerPlugin);
}
if (containerPlugin.particlePosition) {
this.particlePositionPlugins.push(containerPlugin);
}
}
}
this.retina.init();
this.canvas.init();
this.updateActualOptions();
this.canvas.initBackground();
this.canvas.resize();
const { delay, duration, fpsLimit, smooth, zLayers } = this.actualOptions, hdrOptions = this.actualOptions.hdr;
this.hdrMode = hdrOptions.mode;
this.peakNits = hdrOptions.peakNits;
this.zLayers = zLayers;
this.#duration = getRangeValue(duration) * millisecondsToSeconds;
this.#delay = getRangeValue(delay) * millisecondsToSeconds;
this.#lifeTime = 0;
this.fpsLimit = fpsLimit > minFpsLimit ? fpsLimit : defaultFpsLimit;
this.#smooth = smooth;
for (const plugin of this.plugins) {
await plugin.init?.();
}
await this.particles.init();
this.dispatchEvent(exports.EventType.containerInit);
this.particles.setDensity();
for (const plugin of this.plugins) {
plugin.particlesSetup?.();
}
this.dispatchEvent(exports.EventType.particlesSetup);
}
async initDrawersAndUpdaters() {
const pluginManager = this.#pluginManager;
this.effectDrawers = await pluginManager.getEffectDrawers(this, true);
this.shapeDrawers = await pluginManager.getShapeDrawers(this, true);
this.particleUpdaters = await pluginManager.getUpdaters(this, true);
}
pause() {
if (!guardCheck(this)) {
return;
}
if (this.#drawAnimationFrame !== undefined) {
cancelAnimation(this.#drawAnimationFrame);
this.#drawAnimationFrame = undefined;
}
if (this.#paused) {
return;
}
for (const plugin of this.plugins) {
plugin.pause?.();
}
if (!this.pageHidden) {
this.#paused = true;
}
this.dispatchEvent(exports.EventType.containerPaused);
}
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.dispatchEvent(exports.EventType.containerPlay);
this.draw(needsUpdate ?? false);
}
async refresh() {
if (!guardCheck(this)) {
return;
}
this.stop();
return this.start();
}
async reset(sourceOptions) {
if (!guardCheck(this)) {
return;
}
this.#initialSourceOptions = sourceOptions;
this.#sourceOptions = sourceOptions;
this.#options = loadContainerOptions(this.#pluginManager, this, this.#initialSourceOptions, this.sourceOptions);
this.actualOptions = loadContainerOptions(this.#pluginManager, this, this.#options);
return this.refresh();
}
async start() {
if (!guardCheck(this) || this.started) {
return;
}
await this.init();
this.started = true;
await new Promise(resolve => {
const start = async () => {
this.#eventListeners.addListeners();
for (const plugin of this.plugins) {
await plugin.start?.();
}
this.dispatchEvent(exports.EventType.containerStarted);
this.play();
resolve();
};
this.#delayTimeout = setTimeout(() => void start(), this.#delay);
});
}
stop() {
if (!guardCheck(this) || !this.started) {
return;
}
if (this.#delayTimeout) {
clearTimeout(this.#delayTimeout);
this.#delayTimeout = undefined;
}
this.#firstStart = true;
this.started = false;
this.#eventListeners.removeListeners();
this.pause();
this.particles.clear();
this.canvas.stop();
for (const plugin of this.plugins) {
plugin.stop?.();
}
this.particleCreatedPlugins.length = 0;
this.particleDestroyedPlugins.length = 0;
this.particlePositionPlugins.length = 0;
this.#sourceOptions = this.#options;
this.dispatchEvent(exports.EventType.containerStopped);
}
updateActualOptions() {
let refresh = false;
for (const plugin of this.plugins) {
if (plugin.updateActualOptions) {
refresh = plugin.updateActualOptions() || refresh;
}
}
return refresh;
}
#nextFrame(timestamp) {
try {
if (!this.#smooth &&
this.#lastFrameTime !== undefined &&
timestamp < this.#lastFrameTime + millisecondsToSeconds / this.fpsLimit) {
this.draw(false);
return;
}
this.#lastFrameTime ??= timestamp;
updateDelta(this.#delta, timestamp - this.#lastFrameTime, this.fpsLimit, this.#smooth);
this.addLifeTime(this.#delta.value);
this.#lastFrameTime = timestamp;
if (this.#delta.value > millisecondsToSeconds) {
this.draw(false);
return;
}
this.canvas.render.drawParticles(this.#delta);
if (!this.alive()) {
this.destroy();
return;
}
if (this.animationStatus) {
this.draw(false);
}
}
catch (e) {
getLogger().error("error in animation loop", e);
}
}
}
var Container$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
Container: Container
});
class FireworksInstance {
#container;
#id;
constructor(container, id) {
this.#container = container;
this.#id = id;
}
get destroyed() {
return this.#container.destroyed;
}
destroy() {
this.#container.destroy();
deleteFireworksInstance(this.#id);
}
pause() {
this.#container.pause();
}
play() {
this.#container.play();
}
stop() {
this.#container.stop();
}
}
var FireworksInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
FireworksInstance: FireworksInstance
});
class BlendPluginInstance {
layer = exports.DrawLayer.CanvasSetup;
#container;
#defaultCompositeValue;
constructor(container) {
this.#container = container;
}
drawParticleCleanup(context, particle) {
if (!particle.options.blend?.enable) {
return;
}
context.globalCompositeOperation = particle.originalBlendMode ?? defaultCompositeValue;
particle.originalBlendMode = undefined;
}
drawParticleSetup(context, particle) {
if (!particle.options.blend?.enable) {
return;
}
particle.originalBlendMode = context.globalCompositeOperation;
context.globalCompositeOperation = particle.options.blend.mode;
}
drawSettingsCleanup(context) {
if (!this.#defaultCompositeValue) {
return;
}
context.globalCompositeOperation = this.#defaultCompositeValue;
}
drawSettingsSetup(context) {
const previousComposite = context.globalCompositeOperation, blend = this.#container.actualOptions.blend;
this.#defaultCompositeValue = previousComposite;
context.globalCompositeOperation = blend?.enable ? blend.mode : previousComposite;
}
}
var BlendPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
BlendPluginInstance: BlendPluginInstance
});
const moveSpeedFactor = 60, minSpinRadius = 0, spinFactor = 0.01, defaultPathDelay = 0, noDecay = 1;
function applyDistance(particle) {
const initialPosition = particle.initialPosition, { dx, dy } = getDistances(initialPosition, particle.position), dxFixed = Math.abs(dx), dyFixed = Math.abs(dy), { maxDistance } = particle.retina, hDistance = maxDistance.horizontal, vDistance = maxDistance.vertical;
if (!hDistance && !vDistance) {
return;
}
const hasHDistance = (hDistance && dxFixed >= hDistance) ?? false, hasVDistance = (vDistance && dyFixed >= vDistance) ?? false;
if ((hasHDistance || hasVDistance) && !particle.misplaced) {
particle.misplaced = (!!hDistance && dxFixed > hDistance) || (!!vDistance && dyFixed > vDistance);
if (hDistance) {
particle.velocity.x = particle.velocity.y * half - particle.velocity.x;
}
if (vDistance) {
particle.velocity.y = particle.velocity.x * half - particle.velocity.y;
}
}
else if ((!hDistance || dxFixed < hDistance) && (!vDistance || dyFixed < vDistance) && particle.misplaced) {
particle.misplaced = false;
}
else if (particle.misplaced) {
const pos = particle.position, vel = particle.velocity;
if (hDistance &&
((pos.x < initialPosition.x && vel.x < minVelocity) || (pos.x > initialPosition.x && vel.x > minVelocity))) {
vel.x *= -getRandom();
}
if (vDistance &&
((pos.y < initialPosition.y && vel.y < minVelocity) || (pos.y > initialPosition.y && vel.y > minVelocity))) {
vel.y *= -getRandom();
}
}
}
function move(particle, moveOptions, moveSpeed, maxSpeed, moveDrift, reduceFactor, delta) {
applyPath(particle, delta);
const gravityOptions = particle.gravity, gravityFactor = gravityOptions?.enable && gravityOptions.inverse ? -identity$1 : identity$1;
if (moveDrift && moveSpeed) {
particle.velocity.x += (moveDrift * delta.factor) / (moveSpeedFactor * moveSpeed);
}
if (gravityOptions?.enable && moveSpeed) {
particle.velocity.y +=
(gravityFactor * (gravityOptions.acceleration * delta.factor)) / (moveSpeedFactor * moveSpeed);
}
const decay = particle.moveDecay;
particle.velocity.multTo(decay ?? noDecay);
const velocity = particle.velocity.mult(moveSpeed);
if (gravityOptions?.enable &&
maxSpeed > minVelocity &&
((!gravityOptions.inverse && velocity.y >= minVelocity && velocity.y >= maxSpeed) ||
(gravityOptions.inverse && velocity.y <= minVelocity && velocity.y <= -maxSpeed))) {
velocity.y = gravityFactor * maxSpeed;
if (moveSpeed) {
particle.velocity.y = velocity.y / moveSpeed;
}
}
const zIndexOptions = particle.options.zIndex, zVelocityFactor = (identity$1 - particle.zIndexFactor) ** zIndexOptions.velocityRate;
velocity.multTo(zVelocityFactor);
velocity.multTo(reduceFactor);
const { position } = particle;
position.addTo(velocity);
if (moveOptions.vibrate) {
position.x += Math.sin(position.x * Math.cos(position.y)) * reduceFactor;
position.y += Math.cos(position.y * Math.sin(position.x)) * reduceFactor;
}
}
function spin(container, particle, moveSpeed, reduceFactor) {
if (!particle.spin) {
return;
}
const spinClockwise = particle.spin.direction === exports.RotateDirection.clockwise, updateFunc = {
x: spinClockwise ? Math.cos : Math.sin,
y: spinClockwise ? Math.sin : Math.cos,
};
particle.position.x =
particle.spin.center.x + particle.spin.radius * updateFunc.x(particle.spin.angle) * reduceFactor;
particle.position.y =
particle.spin.center.y + particle.spin.radius * updateFunc.y(particle.spin.angle) * reduceFactor;
particle.spin.radius += particle.spin.acceleration * reduceFactor;
const maxCanvasSize = Math.max(container.canvas.size.width, container.canvas.size.height), halfMaxSize = maxCanvasSize * half;
if (particle.spin.radius > halfMaxSize) {
particle.spin.radius = halfMaxSize;
particle.spin.acceleration *= -identity$1;
}
else if (particle.spin.radius < minSpinRadius) {
particle.spin.radius = minSpinRadius;
particle.spin.acceleration *= -identity$1;
}
particle.spin.angle += moveSpeed * spinFactor * (identity$1 - particle.spin.radius / maxCanvasSize);
}
function applyPath(particle, delta) {
const particlesOptions = particle.options, pathOptions = particlesOptions.move.path, pathEnabled = pathOptions.enable;
if (!pathEnabled) {
return;
}
const pathDelay = particle.pathDelay ?? defaultPathDelay;
if (particle.lastPathTime <= pathDelay) {
particle.lastPathTime += delta.value;
return;
}
const path = particle.pathGenerator?.generate(particle, delta);
if (path) {
particle.velocity.addTo(path);
}
if (pathOptions.clamp) {
particle.velocity.x = clamp(particle.velocity.x, -identity$1, identity$1);
particle.velocity.y = clamp(particle.velocity.y, -identity$1, identity$1);
}
particle.lastPathTime -= pathDelay;
}
function getProximitySpeedFactor(particle) {
const mod = particle.getModifier("slow");
return mod?.enabled ? (mod.speedFactor ?? identity$1) : identity$1;
}
function initSpin(container, particle) {
const options = particle.options, spinOptions = options.move.spin;
if (!spinOptions.enable) {
return;
}
const spinPos = spinOptions.position ?? { x: 50, y: 50 }, spinFactor = 0.01, spinCenter = {
x: spinPos.x * spinFactor * container.canvas.size.width,
y: spinPos.y * spinFactor * container.canvas.size.height,
}, pos = particle.getPosition(), distance = getDistance(pos, spinCenter), spinAcceleration = getRangeValue(spinOptions.acceleration);
particle.retina.spinAcceleration = spinAcceleration * container.retina.pixelRatio;
particle.spin = {
center: spinCenter,
direction: particle.velocity.x >= minVelocity ? exports.RotateDirection.clockwise : exports.RotateDirection.counterClockwise,
angle: getRandom() * doublePI,
radius: distance,
acceleration: particle.retina.spinAcceleration,
};
}
const defaultSizeFactor = 1, defaultDeltaFactor = 1;
class MovePluginInstance {
availablePathGenerators;
pathGenerators;
#container;
#pluginManager;
constructor(pluginManager, container) {
this.#pluginManager = pluginManager;
this.#container = container;
this.availablePathGenerators = new Map();
this.pathGenerators = new Map();
}
destroy() {
this.availablePathGenerators = new Map();
this.pathGenerators = new Map();
}
isEnabled(particle) {
return !particle.destroyed && particle.options.move.enable;
}
particleCreated(particle) {
const options = particle.options, moveOptions = options.move, gravityOptions = moveOptions.gravity, pathOptions = moveOptions.path;
particle.moveDecay = decayOffset - getRangeValue(moveOptions.decay);
particle.pathDelay = getRangeValue(pathOptions.delay.value) * millisecondsToSeconds;
if (pathOptions.generator) {
let pathGenerator = this.pathGenerators.get(pathOptions.generator);
if (!pathGenerator) {
pathGenerator = this.availablePathGenerators.get(pathOptions.generator);
if (pathGenerator) {
this.pathGenerators.set(pathOptions.generator, pathGenerator);
pathGenerator.init();
}
}
particle.pathGenerator = pathGenerator;
}
particle.gravity = {
enable: gravityOptions.enable,
acceleration: getRangeValue(gravityOptions.acceleration),
inverse: gravityOptions.inverse,
};
initSpin(this.#container, particle);
}
particleDestroyed(particle) {
const pathGenerator = particle.pathGenerator;
pathGenerator?.reset(particle);
}
particleUpdate(particle, delta) {
const particleOptions = particle.options, moveOptions = particleOptions.move;
if (!moveOptions.enable) {
return;
}
const container = this.#container, slowFactor = getProximitySpeedFactor(particle), reduceFactor = container.retina.reduceFactor, baseSpeed = particle.retina.moveSpeed, moveDrift = particle.retina.moveDrift, maxSize = particle.size.max, sizeFactor = moveOptions.size ? particle.getRadius() / maxSize : defaultSizeFactor, deltaFactor = delta.factor || defaultDeltaFactor, moveSpeed = baseSpeed * sizeFactor * slowFactor * deltaFactor * half, maxSpeed = particle.retina.maxSpeed;
if (moveOptions.spin.enable) {
spin(container, particle, moveSpeed, reduceFactor);
}
else {
move(particle, moveOptions, moveSpeed, maxSpeed, moveDrift, reduceFactor, delta);
}
applyDistance(particle);
}
preInit() {
return this.#init();
}
redrawInit() {
return this.#init();
}
update() {
for (const pathGenerator of this.pathGenerators.values()) {
pathGenerator.update();
}
}
async #init() {
const availablePathGenerators = await this.#pluginManager.getPathGenerators?.(this.#container, true);
if (!availablePathGenerators) {
return;
}
this.availablePathGenerators = availablePathGenerators;
this.pathGenerators = new Map();
for (const pathGenerator of this.pathGenerators.values()) {
pathGenerator.init();
}
}
}
var MovePluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
MovePluginInstance: MovePluginInstance
});
class EmittersPluginInstance {
#container;
#instancesManager;
constructor(instancesManager, container) {
this.#instancesManager = instancesManager;
this.#container = container;
this.#instancesManager.initContainer(container);
}
async init() {
const emittersOptions = this.#container.actualOptions.emitters;
if (isArray(emittersOptions)) {
for (const emitterOptions of emittersOptions) {
await this.#instancesManager.addEmitter(this.#container, emitterOptions);
}
}
else {
await this.#instancesManager.addEmitter(this.#container, emittersOptions);
}
}
pause() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.pause();
}
}
play() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.play();
}
}
resize() {
for (const emitter of this.#instancesManager.getArray(this.#container)) {
emitter.resize();
}
}
stop() {
this.#instancesManager.clear(this.#container);
}
update(delta) {
this.#instancesManager.getArray(this.#container).forEach(emitter => {
emitter.update(delta);
});
}
}
var EmittersPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmittersPluginInstance: EmittersPluginInstance
});
const shapeGenerators = new Map();
class ShapeManager {
addShapeGenerator(name, generator) {
if (!this.getShapeGenerator(name)) {
shapeGenerators.set(name, generator);
}
}
getShapeGenerator(name) {
return shapeGenerators.get(name);
}
getSupportedShapeGenerators() {
return shapeGenerators.keys();
}
}
var ShapeManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
ShapeManager: ShapeManager
});
const defaultIndex = 0;
class EmittersInstancesManager {
#containerArrays;
#pluginManager;
constructor(pluginManager) {
this.#containerArrays = new Map();
this.#pluginManager = pluginManager;
}
async addEmitter(container, options, position) {
const emitterOptions = new Emitter();
emitterOptions.load(options);
const { EmitterInstance } = await Promise.resolve().then(function () { return EmitterInstance$1; }), emitter = new EmitterInstance(this.#pluginManager, container, (emitter) => {
this.removeEmitter(container, emitter);
}, emitterOptions, position);
await emitter.init();
this.getArray(container).push(emitter);
return emitter;
}
clear(container) {
this.initContainer(container);
this.#containerArrays.set(container, []);
}
getArray(container) {
this.initContainer(container);
let array = this.#containerArrays.get(container);
if (!array) {
array = [];
this.#containerArrays.set(container, array);
}
return array;
}
initContainer(container) {
if (this.#containerArrays.has(container)) {
return;
}
this.#containerArrays.set(container, []);
container.getEmitter = (idxOrName) => {
const array = this.getArray(container);
return idxOrName === undefined || isNumber(idxOrName)
? array[idxOrName ?? defaultIndex]
: array.find(t => t.name === idxOrName);
};
container.addEmitter = async (options, position) => this.addEmitter(container, options, position);
container.removeEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
this.removeEmitter(container, emitter);
}
};
container.playEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
emitter.externalPlay();
}
};
container.pauseEmitter = (idxOrName) => {
const emitter = container.getEmitter?.(idxOrName);
if (emitter) {
emitter.externalPause();
}
};
}
removeEmitter(container, emitter) {
const index = this.getArray(container).indexOf(emitter), minIndex = 0, deleteCount = 1;
if (index >= minIndex) {
this.getArray(container).splice(index, deleteCount);
}
}
}
var EmittersInstancesManager$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmittersInstancesManager: EmittersInstancesManager
});
var SoundsEventType;
(function (SoundsEventType) {
SoundsEventType["mute"] = "soundsMuted";
SoundsEventType["unmute"] = "soundsUnmuted";
})(SoundsEventType || (SoundsEventType = {}));
var ImageDisplay;
(function (ImageDisplay) {
ImageDisplay["Block"] = "block";
ImageDisplay["None"] = "none";
})(ImageDisplay || (ImageDisplay = {}));
const zIndexOffset = 1, rightOffset = 1, minVolume = 0;
function initImage(data) {
const img = safeDocument().createElement("img"), { clickCb, container, display, iconOptions, margin, options, pos, rightOffsets } = data, { width, path, style, svg } = iconOptions, defaultAccumulator = 0;
setIconStyle(img, pos.top + margin, pos.right -
(margin * (rightOffsets.length + rightOffset) + width + rightOffsets.reduce((a, b) => a + b, defaultAccumulator)), display, options.fullScreen.zIndex + zIndexOffset, width, margin, style);
img.src = path ?? (svg ? `data:image/svg+xml;base64,${btoa(svg)}` : "");
const parent = container.canvas.domElement?.parentNode ?? safeDocument().body;
parent.append(img);
img.addEventListener("click", () => {
void clickCb();
});
return img;
}
function removeImage(image) {
if (!image) {
return;
}
image.remove();
}
function setIconStyle(icon, top, left, display, zIndex, width, margin, style) {
icon.style.userSelect = "none";
icon.style.position = "absolute";
icon.style.top = `${(top + margin).toString()}px`;
icon.style.left = `${(left - margin - width).toString()}px`;
icon.style.display = display;
icon.style.zIndex = (zIndex + zIndexOffset).toString();
icon.style.cssText += style;
}
class SoundsPluginInstance {
#audioMap;
#audioSources;
#container;
#engine;
#gain;
#muteImg;
#unmuteImg;
#volume;
#volumeDownImg;
#volumeUpImg;
constructor(container, engine) {
this.#container = container;
this.#engine = engine;
this.#volume = 0;
this.#audioSources = [];
this.#audioMap = new Map();
}
async init() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions?.enable) {
return;
}
if (soundsOptions.autoPlay && isWindowMuted()) {
const firstClickHandler = () => {
removeEventListener(mouseDownEvent, firstClickHandler);
removeEventListener(touchStartEvent, firstClickHandler);
unmuteWindow();
void this.unmute();
}, listenerOptions = {
capture: true,
once: true,
};
addEventListener(mouseDownEvent, firstClickHandler, listenerOptions);
addEventListener(touchStartEvent, firstClickHandler, listenerOptions);
}
this.#volume = soundsOptions.volume.value;
const events = soundsOptions.events;
this.#audioMap = new Map();
for (const event of events) {
if (!event.audio) {
continue;
}
const promises = executeOnSingleOrMultiple(event.audio, async (audio) => {
const response = await fetch(audio.source);
if (!response.ok) {
return;
}
const arrayBuffer = await response.arrayBuffer(), audioContext = this.#getAudioContext(), audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
this.#audioMap.set(audio.source, audioBuffer);
});
if (promises instanceof Promise) {
await promises;
}
else {
await Promise.allSettled(promises);
}
}
}
async mute() {
if (!this.#container.muted) {
await this.toggleMute();
}
}
async start() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions?.enable || !container.canvas.domElement) {
return;
}
container.muted = true;
const canvas = container.canvas.domElement, pos = {
top: canvas.offsetTop,
right: canvas.offsetLeft + canvas.offsetWidth,
}, { mute, unmute, volumeDown, volumeUp } = soundsOptions.icons, margin = 10, toggleMute = async () => {
await this.toggleMute();
}, enableIcons = soundsOptions.icons.enable, display = enableIcons ? ImageDisplay.Block : ImageDisplay.None;
this.#muteImg = initImage({
container,
options,
pos,
display,
iconOptions: mute,
margin,
rightOffsets: [volumeDown.width, volumeUp.width],
clickCb: toggleMute,
});
this.#unmuteImg = initImage({
container,
options,
pos,
display: ImageDisplay.None,
iconOptions: unmute,
margin,
rightOffsets: [volumeDown.width, volumeUp.width],
clickCb: toggleMute,
});
this.#volumeDownImg = initImage({
container,
options,
pos,
display,
iconOptions: volumeDown,
margin,
rightOffsets: [volumeUp.width],
clickCb: async () => {
await this.volumeDown();
},
});
this.#volumeUpImg = initImage({
container,
options,
pos,
display,
iconOptions: volumeUp,
margin,
rightOffsets: [],
clickCb: async () => {
await this.volumeUp();
},
});
if (!isWindowMuted() && soundsOptions.autoPlay) {
await this.unmute();
}
}
stop() {
this.#container.muted = true;
void (async () => {
await this.#mute();
removeImage(this.#muteImg);
removeImage(this.#unmuteImg);
removeImage(this.#volumeDownImg);
removeImage(this.#volumeUpImg);
})();
}
async toggleMute() {
const container = this.#container;
container.muted = !container.muted;
this.#updateMuteIcons();
await this.#updateMuteStatus();
}
async unmute() {
if (this.#container.muted) {
await this.toggleMute();
}
}
async volumeDown() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
if (container.muted) {
this.#volume = 0;
}
this.#volume -= soundsOptions.volume.step;
await this.#updateVolume();
}
async volumeUp() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
this.#volume += soundsOptions.volume.step;
await this.#updateVolume();
}
#addBuffer(audioCtx) {
const buffer = audioCtx.createBufferSource();
this.#audioSources.push(buffer);
return buffer;
}
#addOscillator(audioCtx) {
const oscillator = audioCtx.createOscillator();
this.#audioSources.push(oscillator);
return oscillator;
}
#getAudioContext() {
const container = this.#container;
container.audioContext ??= new AudioContext();
return container.audioContext;
}
#initEvents() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable || !container.canvas.domElement) {
return;
}
for (const event of soundsOptions.events) {
const cb = (args) => {
if (!args) {
return;
}
void (async () => {
const filterNotValid = event.filter && !event.filter(args);
if (this.#container !== args.container) {
return;
}
if (!!this.#container.muted || this.#container.destroyed) {
executeOnSingleOrMultiple(event.event, item => {
this.#engine.removeEventListener(item, cb);
});
return;
}
if (filterNotValid) {
return;
}
const defaultNoteIndex = 0;
if (event.audio) {
const audio = itemFromSingleOrMultiple(event.audio);
if (!audio) {
return;
}
this.#playBuffer(audio);
}
else if (event.melodies) {
const melody = itemFromArray(event.melodies);
if (!melody) {
return;
}
if (melody.melodies.length) {
await Promise.allSettled(melody.melodies.map(m => this.#playNote(m.notes, defaultNoteIndex, melody.loop)));
}
else {
await this.#playNote(melody.notes, defaultNoteIndex, melody.loop);
}
}
else if (event.notes) {
const note = itemFromArray(event.notes);
if (!note) {
return;
}
await this.#playNote([note], defaultNoteIndex, false);
}
})();
};
executeOnSingleOrMultiple(event.event, item => {
this.#engine.addEventListener(item, cb);
});
}
}
async #mute() {
const container = this.#container, audioContext = this.#getAudioContext();
for (const source of this.#audioSources) {
this.#removeAudioSource(source);
}
if (this.#gain) {
this.#gain.disconnect();
}
await audioContext.close();
container.audioContext = undefined;
this.#container.dispatchEvent(SoundsEventType.mute);
}
#playBuffer(audio) {
const audioBuffer = this.#audioMap.get(audio.source);
if (!audioBuffer) {
return;
}
const audioCtx = this.#container.audioContext;
if (!audioCtx) {
return;
}
const source = this.#addBuffer(audioCtx);
source.loop = audio.loop;
source.buffer = audioBuffer;
source.connect(this.#gain ?? audioCtx.destination);
source.start();
}
async #playFrequency(frequency, duration) {
if (!this.#gain || this.#container.muted) {
return;
}
const audioContext = this.#getAudioContext(), oscillator = this.#addOscillator(audioContext);
oscillator.connect(this.#gain);
oscillator.type = "sine";
oscillator.frequency.value = frequency;
oscillator.start();
return new Promise(resolve => {
setTimeout(() => {
this.#removeAudioSource(oscillator);
resolve();
}, duration);
});
}
#playMuteSound() {
if (this.#container.muted) {
return;
}
const audioContext = this.#getAudioContext(), gain = audioContext.createGain();
gain.connect(audioContext.destination);
gain.gain.value = 0;
const oscillator = audioContext.createOscillator();
oscillator.connect(gain);
oscillator.type = "sine";
oscillator.frequency.value = 1;
oscillator.start();
setTimeout(() => {
oscillator.stop();
oscillator.disconnect();
gain.disconnect();
});
}
async #playNote(notes, noteIdx, loop) {
if (this.#container.muted) {
return;
}
const note = notes[noteIdx];
if (!note) {
return;
}
const value = note.value, promises = executeOnSingleOrMultiple(value, async (_, idx) => {
return this.#playNoteValue(notes, noteIdx, idx);
});
await (isArray(promises) ? Promise.allSettled(promises) : promises);
const indexOffset = 1;
let nextNoteIdx = noteIdx + indexOffset;
if (loop && nextNoteIdx >= notes.length) {
nextNoteIdx = nextNoteIdx % notes.length;
}
await this.#playNote(notes, nextNoteIdx, loop);
}
async #playNoteValue(notes, noteIdx, valueIdx) {
const note = notes[noteIdx];
if (!note) {
return;
}
const value = itemFromSingleOrMultiple(note.value, valueIdx, true);
if (!value) {
return;
}
try {
const freq = getNoteFrequency(value);
if (!isNumber(freq)) {
return;
}
await this.#playFrequency(freq, note.duration);
}
catch (e) {
getLogger().error(e);
}
}
#removeAudioSource(source) {
source.stop();
source.disconnect();
const deleteCount = 1;
this.#audioSources.splice(this.#audioSources.indexOf(source), deleteCount);
}
#unmute() {
const container = this.#container, options = container.actualOptions, soundsOptions = options.sounds;
if (!soundsOptions) {
return;
}
const audioContext = this.#getAudioContext(), gain = audioContext.createGain();
gain.connect(audioContext.destination);
gain.gain.value = soundsOptions.volume.value / percentDenominator;
this.#gain = gain;
this.#initEvents();
this.#container.dispatchEvent(SoundsEventType.unmute);
}
#updateMuteIcons() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable || !soundsOptions.icons.enable) {
return;
}
const muteImg = this.#muteImg, unmuteImg = this.#unmuteImg;
if (muteImg) {
muteImg.style.display = container.muted ? "block" : "none";
}
if (unmuteImg) {
unmuteImg.style.display = container.muted ? "none" : "block";
}
}
async #updateMuteStatus() {
const container = this.#container, audioContext = this.#getAudioContext();
if (container.muted) {
await audioContext.suspend();
await this.#mute();
}
else {
await audioContext.resume();
this.#unmute();
this.#playMuteSound();
}
}
async #updateVolume() {
const container = this.#container, soundsOptions = container.actualOptions.sounds;
if (!soundsOptions?.enable) {
return;
}
clamp(this.#volume, soundsOptions.volume.min, soundsOptions.volume.max);
let stateChanged = false;
if (this.#volume <= minVolume && !container.muted) {
this.#volume = 0;
container.muted = true;
stateChanged = true;
}
else if (this.#volume > minVolume && container.muted) {
container.muted = false;
stateChanged = true;
}
if (stateChanged) {
this.#updateMuteIcons();
await this.#updateMuteStatus();
}
if (this.#gain?.gain) {
this.#gain.gain.value = this.#volume / percentDenominator;
}
}
}
var SoundsPluginInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
SoundsPluginInstance: SoundsPluginInstance
});
const defaultLifeDelay = 0, minLifeCount = 0, defaultSpawnDelay = 0, defaultEmitDelay = 0, defaultLifeCount = -1, defaultColorAnimationFactor = 1, colorFactor = 3.6, defaultStrokeWidth = 1;
function setParticlesOptionsFillColor(particlesOptions, color, opacity, enable) {
const paint = (particlesOptions.paint ??= new Paint());
paint.fill = {
color: AnimatableColor.create(undefined, { value: color }),
enable,
opacity,
};
}
function setParticlesOptionsStrokeColor(particlesOptions, color, opacity, width) {
const paint = (particlesOptions.paint ??= new Paint());
paint.stroke = {
color: AnimatableColor.create(undefined, { value: color }),
opacity,
width,
};
}
class EmitterInstance {
fill;
name;
options;
position;
size;
spawnFillColor;
spawnFillEnabled;
spawnFillOpacity;
spawnStrokeColor;
spawnStrokeOpacity;
spawnStrokeWidth;
#container;
#currentDuration;
#currentEmitDelay;
#currentSpawnDelay;
#duration;
#emitDelay;
#firstSpawn;
#immortal;
#initialPosition;
#lifeCount;
#mutationObserver;
#particlesOptions;
#paused;
#pluginManager;
#removeCallback;
#resizeObserver;
#shape;
#size;
#spawnDelay;
#startParticlesAdded;
constructor(pluginManager, container, removeCallback, options, position) {
this.#pluginManager = pluginManager;
this.#container = container;
this.#removeCallback = removeCallback;
this.#currentDuration = 0;
this.#currentEmitDelay = 0;
this.#currentSpawnDelay = 0;
this.#initialPosition = position;
if (options instanceof Emitter) {
this.options = options;
}
else {
this.options = new Emitter();
this.options.load(options);
}
this.#spawnDelay = container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay) * millisecondsToSeconds) /
container.retina.reduceFactor
: Infinity;
this.position = this.#initialPosition ?? this.#calcPosition();
this.name = this.options.name;
this.fill = this.options.fill;
this.#firstSpawn = !this.options.life.wait;
this.#startParticlesAdded = false;
const particlesOptions = deepExtend({}, this.options.particles);
particlesOptions.move ??= {};
particlesOptions.move.direction ??= this.options.direction;
if (this.options.spawn.fill?.color) {
this.spawnFillColor = rangeColorToHsl(this.#pluginManager, this.options.spawn.fill.color);
}
if (this.options.spawn.stroke?.color) {
this.spawnStrokeColor = rangeColorToHsl(this.#pluginManager, this.options.spawn.stroke.color);
}
this.#paused = !this.options.autoPlay;
this.#particlesOptions = particlesOptions;
this.#size = this.#calcSize();
this.size =
this.#size.mode === exports.PixelMode.percent
? {
width: (this.#size.width / percentDenominator) * this.#container.canvas.size.width,
height: (this.#size.height / percentDenominator) * this.#container.canvas.size.height,
}
: { width: this.#size.width, height: this.#size.height };
this.#lifeCount = this.options.life.count ?? defaultLifeCount;
this.#immortal = this.#lifeCount <= minLifeCount;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
this.#mutationObserver = new MutationObserver(() => {
this.resize();
});
this.#resizeObserver = new ResizeObserver(() => {
this.resize();
});
this.#mutationObserver.observe(element, {
attributes: true,
attributeFilter: ["style", "width", "height"],
});
this.#resizeObserver.observe(element);
}
}
const shapeOptions = this.options.shape, shapeGenerator = this.#pluginManager.emitterShapeManager?.getShapeGenerator(shapeOptions.type);
if (shapeGenerator) {
this.#shape = shapeGenerator.generate(this.#container, this.position, this.size, this.fill, shapeOptions.options);
}
this.#container.dispatchEvent("emitterCreated", {
emitter: this,
});
this.play();
}
externalPause() {
this.#paused = true;
this.pause();
}
externalPlay() {
this.#paused = false;
this.play();
}
async init() {
await this.#shape?.init();
}
pause() {
if (this.#paused) {
return;
}
this.#emitDelay = undefined;
}
play() {
if (this.#paused) {
return;
}
if (!((this.#lifeCount > minLifeCount || this.#immortal || !this.options.life.count) &&
(this.#firstSpawn || this.#currentSpawnDelay >= (this.#spawnDelay ?? defaultSpawnDelay)))) {
return;
}
const container = this.#container;
if (this.#emitDelay === undefined) {
const delay = getRangeValue(this.options.rate.delay);
this.#emitDelay = container.retina.reduceFactor
? (delay * millisecondsToSeconds) / container.retina.reduceFactor
: Infinity;
}
if (this.#lifeCount > minLifeCount || this.#immortal) {
this.#prepareToDie();
}
}
resize() {
const initialPosition = this.#initialPosition, container = this.#container;
this.position =
initialPosition && isPointInside(initialPosition, container.canvas.size, Vector.origin)
? initialPosition
: this.#calcPosition();
this.#size = this.#calcSize();
this.size =
this.#size.mode === exports.PixelMode.percent
? {
width: (this.#size.width / percentDenominator) * container.canvas.size.width,
height: (this.#size.height / percentDenominator) * container.canvas.size.height,
}
: { width: this.#size.width, height: this.#size.height };
this.#shape?.resize(this.position, this.size);
}
update(delta) {
if (this.#paused) {
return;
}
const container = this.#container;
if (this.#firstSpawn) {
this.#firstSpawn = false;
this.#currentSpawnDelay = this.#spawnDelay ?? defaultSpawnDelay;
this.#currentEmitDelay = this.#emitDelay ?? defaultEmitDelay;
}
if (!this.#startParticlesAdded) {
this.#startParticlesAdded = true;
this.#emitParticles(this.options.startCount);
}
if (this.#duration !== undefined) {
this.#currentDuration += delta.value;
if (this.#currentDuration >= this.#duration) {
this.pause();
if (this.#spawnDelay !== undefined) {
this.#spawnDelay = undefined;
}
if (!this.#immortal) {
this.#lifeCount--;
}
if (this.#lifeCount > minLifeCount || this.#immortal) {
this.position = this.#calcPosition();
this.#shape?.resize(this.position, this.size);
this.#spawnDelay = container.retina.reduceFactor
? (getRangeValue(this.options.life.delay ?? defaultLifeDelay) * millisecondsToSeconds) /
container.retina.reduceFactor
: Infinity;
}
else {
this.#destroy();
}
this.#currentDuration -= this.#duration;
this.#duration = undefined;
}
}
if (this.#spawnDelay !== undefined) {
this.#currentSpawnDelay += delta.value;
if (this.#currentSpawnDelay >= this.#spawnDelay) {
this.#container.dispatchEvent("emitterPlay");
this.play();
this.#currentSpawnDelay -= this.#spawnDelay;
this.#spawnDelay = undefined;
}
}
if (this.#emitDelay !== undefined) {
this.#currentEmitDelay += delta.value;
if (this.#currentEmitDelay >= this.#emitDelay) {
this.#emit();
this.#currentEmitDelay -= this.#emitDelay;
}
}
}
#calcPosition() {
const container = this.#container;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
const elRect = element.getBoundingClientRect(), pxRatio = container.retina.pixelRatio;
return {
x: (elRect.x + elRect.width * half) * pxRatio,
y: (elRect.y + elRect.height * half) * pxRatio,
};
}
}
return calcPositionOrRandomFromSizeRanged({
size: container.canvas.size,
position: this.options.position,
});
}
#calcSize() {
const container = this.#container;
if (this.options.domId) {
const element = safeDocument().getElementById(this.options.domId);
if (element) {
const elRect = element.getBoundingClientRect();
return {
width: elRect.width * container.retina.pixelRatio,
height: elRect.height * container.retina.pixelRatio,
mode: exports.PixelMode.precise,
};
}
}
return (this.options.size ??
(() => {
const size = new EmitterSize();
size.load({
height: 0,
mode: exports.PixelMode.percent,
width: 0,
});
return size;
})());
}
#destroy() {
this.#mutationObserver?.disconnect();
this.#mutationObserver = undefined;
this.#resizeObserver?.disconnect();
this.#resizeObserver = undefined;
this.#removeCallback(this);
this.#container.dispatchEvent("emitterDestroyed", {
emitter: this,
});
}
#emit() {
if (this.#paused) {
return;
}
const quantity = getRangeValue(this.options.rate.quantity);
this.#emitParticles(quantity);
}
#emitParticles(quantity) {
const singleParticlesOptions = (itemFromSingleOrMultiple(this.#particlesOptions) ??
{}), fillHslAnimation = this.options.spawn.fill?.color?.animation, fillEnabled = this.options.spawn.fill?.enable ?? !!this.options.spawn.fill?.color, fillOpacity = this.options.spawn.fill?.opacity === undefined
? defaultOpacity$1
: getRangeValue(this.options.spawn.fill.opacity), strokeHslAnimation = this.options.spawn.stroke?.color?.animation, strokeOpacity = this.options.spawn.stroke?.opacity === undefined
? defaultOpacity$1
: getRangeValue(this.options.spawn.stroke.opacity), strokeWidth = this.options.spawn.stroke?.width === undefined
? defaultStrokeWidth
: getRangeValue(this.options.spawn.stroke.width), reduceFactor = this.#container.retina.reduceFactor, needsFillColorAnimation = !!fillHslAnimation, needsStrokeColorAnimation = !!strokeHslAnimation, needsShapeData = !!this.#shape, needsColorAnimation = needsFillColorAnimation || needsStrokeColorAnimation, needsCopy = needsColorAnimation || needsShapeData, maxValues = needsColorAnimation ? { h: hMax, s: sMax, l: lMax } : null, shapeOptions = this.options.shape;
for (let i = 0; i < quantity * reduceFactor; i++) {
const particlesOptions = needsCopy
? deepExtend({}, singleParticlesOptions)
: singleParticlesOptions;
this.spawnFillOpacity = fillOpacity;
this.spawnFillEnabled = fillEnabled;
this.spawnStrokeOpacity = strokeOpacity;
this.spawnStrokeWidth = strokeWidth;
if (this.spawnFillColor) {
if (fillHslAnimation && maxValues) {
this.spawnFillColor.h = this.#setColorAnimation(fillHslAnimation.h, this.spawnFillColor.h, maxValues.h, colorFactor);
this.spawnFillColor.s = this.#setColorAnimation(fillHslAnimation.s, this.spawnFillColor.s, maxValues.s);
this.spawnFillColor.l = this.#setColorAnimation(fillHslAnimation.l, this.spawnFillColor.l, maxValues.l);
}
setParticlesOptionsFillColor(particlesOptions, this.spawnFillColor, this.spawnFillOpacity, this.spawnFillEnabled);
}
if (this.spawnStrokeColor) {
if (strokeHslAnimation && maxValues) {
this.spawnStrokeColor.h = this.#setColorAnimation(strokeHslAnimation.h, this.spawnStrokeColor.h, maxValues.h, colorFactor);
this.spawnStrokeColor.s = this.#setColorAnimation(strokeHslAnimation.s, this.spawnStrokeColor.s, maxValues.s);
this.spawnStrokeColor.l = this.#setColorAnimation(strokeHslAnimation.l, this.spawnStrokeColor.l, maxValues.l);
}
setParticlesOptionsStrokeColor(particlesOptions, this.spawnStrokeColor, this.spawnStrokeOpacity, this.spawnStrokeWidth);
}
let position = this.position;
if (this.#shape) {
const shapePosData = this.#shape.randomPosition();
if (shapePosData) {
position = shapePosData.position;
const replaceData = shapeOptions.replace;
if (replaceData.color && shapePosData.color) {
setParticlesOptionsFillColor(particlesOptions, shapePosData.color, replaceData.opacity ? (shapePosData.opacity ?? defaultOpacity$1) : defaultOpacity$1, true);
}
}
else {
position = null;
}
}
if (position) {
this.#container.particles.addParticle(position, particlesOptions);
}
}
}
#prepareToDie() {
if (this.#paused) {
return;
}
const duration = this.options.life.duration !== undefined ? getRangeValue(this.options.life.duration) : undefined, minDuration = 0, minLifeCount = 0;
if ((this.#lifeCount > minLifeCount || this.#immortal) && duration !== undefined && duration > minDuration) {
this.#duration = duration * millisecondsToSeconds;
}
}
#setColorAnimation(animation, initValue, maxValue, factor = defaultColorAnimationFactor) {
const container = this.#container;
if (!animation.enable) {
return initValue;
}
const colorOffset = randomInRangeValue(animation.offset), delay = getRangeValue(this.options.rate.delay), emitFactor = container.retina.reduceFactor
? (delay * millisecondsToSeconds) / container.retina.reduceFactor
: Infinity, colorSpeed = getRangeValue(animation.speed);
return (initValue + (colorSpeed * container.fpsLimit) / emitFactor + colorOffset * factor) % maxValue;
}
}
var EmitterInstance$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
EmitterInstance: EmitterInstance
});
exports.AnimatableColor = AnimatableColor;
exports.AnimationOptions = AnimationOptions;
exports.AnimationValueWithRandom = AnimationValueWithRandom;
exports.Background = Background;
exports.BaseRange = BaseRange;
exports.Circle = Circle;
exports.ColorAnimation = ColorAnimation;
exports.Fill = Fill;
exports.FullScreen = FullScreen;
exports.HDROptions = HDROptions;
exports.HslAnimation = HslAnimation;
exports.Move = Move;
exports.MoveAngle = MoveAngle;
exports.MoveCenter = MoveCenter;
exports.MoveGravity = MoveGravity;
exports.MovePath = MovePath;
exports.OptionLoader = OptionLoader;
exports.Options = Options;
exports.OptionsColor = OptionsColor;
exports.OutModes = OutModes;
exports.Paint = Paint;
exports.ParticlesBounce = ParticlesBounce;
exports.ParticlesBounceFactor = ParticlesBounceFactor;
exports.ParticlesDensity = ParticlesDensity;
exports.ParticlesNumber = ParticlesNumber;
exports.ParticlesNumberLimit = ParticlesNumberLimit;
exports.ParticlesOptions = ParticlesOptions;
exports.RangedAnimationOptions = RangedAnimationOptions;
exports.RangedAnimationValueWithRandom = RangedAnimationValueWithRandom;
exports.Rectangle = Rectangle;
exports.ResizeEvent = ResizeEvent;
exports.Shape = Shape;
exports.Spin = Spin;
exports.Stroke = Stroke;
exports.ValueWithRandom = ValueWithRandom;
exports.Vector = Vector;
exports.Vector3d = Vector3d;
exports.ZIndex = ZIndex;
exports.alterHsl = alterHsl;
exports.animate = animate;
exports.areBoundsInside = areBoundsInside;
exports.calcExactPositionOrRandomFromSize = calcExactPositionOrRandomFromSize;
exports.calcPositionFromSize = calcPositionFromSize;
exports.calcPositionOrRandomFromSize = calcPositionOrRandomFromSize;
exports.calcPositionOrRandomFromSizeRanged = calcPositionOrRandomFromSizeRanged;
exports.calculateBounds = calculateBounds;
exports.cancelAnimation = cancelAnimation;
exports.canvasFirstIndex = canvasFirstIndex;
exports.canvasTag = canvasTag;
exports.checkDistance = checkDistance;
exports.circleBounce = circleBounce;
exports.circleBounceDataFromParticle = circleBounceDataFromParticle;
exports.clamp = clamp;
exports.cloneStyle = cloneStyle;
exports.collisionVelocity = collisionVelocity;
exports.colorMix = colorMix;
exports.colorToRgb = colorToRgb;
exports.countOffset = countOffset;
exports.decayOffset = decayOffset;
exports.deepExtend = deepExtend;
exports.defaultAlpha = defaultAlpha$1;
exports.defaultAngle = defaultAngle;
exports.defaultCompositeValue = defaultCompositeValue;
exports.defaultDensityFactor = defaultDensityFactor;
exports.defaultFps = defaultFps;
exports.defaultFpsLimit = defaultFpsLimit;
exports.defaultLoops = defaultLoops;
exports.defaultOpacity = defaultOpacity$1;
exports.defaultRatio = defaultRatio;
exports.defaultReduceFactor = defaultReduceFactor;
exports.defaultRemoveQuantity = defaultRemoveQuantity;
exports.defaultRetryCount = defaultRetryCount;
exports.defaultRgbMin = defaultRgbMin;
exports.defaultTime = defaultTime;
exports.defaultTransform = defaultTransform;
exports.defaultTransformValue = defaultTransformValue;
exports.defaultVelocity = defaultVelocity;
exports.defaultZoom = defaultZoom;
exports.degToRad = degToRad;
exports.deleteCount = deleteCount;
exports.double = double;
exports.doublePI = doublePI;
exports.empty = empty;
exports.executeOnSingleOrMultiple = executeOnSingleOrMultiple;
exports.fireworks = fireworks;
exports.generatedAttribute = generatedAttribute;
exports.generatedFalse = generatedFalse;
exports.generatedTrue = generatedTrue;
exports.getDistance = getDistance;
exports.getDistanceSq = getDistanceSq;
exports.getDistances = getDistances;
exports.getFullScreenStyle = getFullScreenStyle;
exports.getHslAnimationFromHsl = getHslAnimationFromHsl;
exports.getHslFromAnimation = getHslFromAnimation;
exports.getItemMapFromInitializer = getItemMapFromInitializer;
exports.getItemsFromInitializer = getItemsFromInitializer;
exports.getLinkColor = getLinkColor;
exports.getLinkRandomColor = getLinkRandomColor;
exports.getLogger = getLogger;
exports.getParticleBaseVelocity = getParticleBaseVelocity;
exports.getParticleDirectionAngle = getParticleDirectionAngle;
exports.getPosition = getPosition;
exports.getRandom = getRandom;
exports.getRandomFn = getRandomFn;
exports.getRandomInRange = getRandomInRange;
exports.getRandomRgbColor = getRandomRgbColor;
exports.getRangeMax = getRangeMax;
exports.getRangeMin = getRangeMin;
exports.getRangeValue = getRangeValue;
exports.getStyleFromHsl = getStyleFromHsl;
exports.getStyleFromRgb = getStyleFromRgb;
exports.hMax = hMax;
exports.hMin = hMin;
exports.hPhase = hPhase;
exports.half = half;
exports.hslToRgb = hslToRgb;
exports.hslToRgbFloat = hslToRgbFloat;
exports.hslaToRgba = hslaToRgba;
exports.identity = identity$1;
exports.inverseFactorNumerator = inverseFactorNumerator;
exports.isArray = isArray;
exports.isBoolean = isBoolean;
exports.isFunction = isFunction;
exports.isInArray = isInArray;
exports.isNull = isNull;
exports.isNumber = isNumber;
exports.isObject = isObject;
exports.isPointInside = isPointInside;
exports.isString = isString;
exports.itemFromArray = itemFromArray;
exports.itemFromSingleOrMultiple = itemFromSingleOrMultiple;
exports.lFactor = lFactor;
exports.lMax = lMax;
exports.lMin = lMin;
exports.lengthOffset = lengthOffset;
exports.loadExtendProperty = loadExtendProperty;
exports.loadLazyProperty = loadLazyProperty;
exports.loadMinIndex = loadMinIndex;
exports.loadNestedProperty = loadNestedProperty;
exports.loadOptionProperty = loadOptionProperty;
exports.loadOptions = loadOptions;
exports.loadParticlesOptions = loadParticlesOptions;
exports.loadProperty = loadProperty;
exports.loadRandomFactor = loadRandomFactor;
exports.loadRangeProperty = loadRangeProperty;
exports.manageListener = manageListener;
exports.maxNits = maxNits;
exports.midColorValue = midColorValue;
exports.millisecondsToSeconds = millisecondsToSeconds;
exports.minCount = minCount;
exports.minDistance = minDistance;
exports.minFpsLimit = minFpsLimit;
exports.minIndex = minIndex;
exports.minLimit = minLimit;
exports.minRadius = minRadius$1;
exports.minStrokeWidth = minStrokeWidth;
exports.minVelocity = minVelocity;
exports.minZ = minZ;
exports.minimumSize = minimumSize;
exports.mix = mix;
exports.none = none;
exports.one = one;
exports.originPoint = originPoint;
exports.parseAlpha = parseAlpha;
exports.percentDenominator = percentDenominator;
exports.phaseNumerator = phaseNumerator;
exports.quarter = quarter;
exports.randomColorValue = randomColorValue;
exports.randomInRangeValue = randomInRangeValue;
exports.rangeColorToHsl = rangeColorToHsl;
exports.rangeColorToRgb = rangeColorToRgb;
exports.removeDeleteCount = removeDeleteCount;
exports.removeMinIndex = removeMinIndex;
exports.resizeEvent = resizeEvent;
exports.rgbMax = rgbMax;
exports.rgbToHsl = rgbToHsl;
exports.sMax = sMax;
exports.sMin = sMin;
exports.sNormalizedOffset = sNormalizedOffset;
exports.safeDocument = safeDocument;
exports.safeMatchMedia = safeMatchMedia;
exports.safeMutationObserver = safeMutationObserver;
exports.setAnimationFunctions = setAnimationFunctions;
exports.setLogger = setLogger;
exports.setRandom = setRandom;
exports.setRangeValue = setRangeValue;
exports.sextuple = sextuple;
exports.spatialHashGridCellSize = spatialHashGridCellSize;
exports.squareExp = squareExp;
exports.stringToAlpha = stringToAlpha;
exports.stringToRgb = stringToRgb;
exports.threeQuarter = threeQuarter;
exports.triple = triple;
exports.tryCountIncrement = tryCountIncrement;
exports.tsParticles = tsParticles;
exports.updateColor = updateColor;
exports.updateColorValue = updateColorValue;
exports.visibilityChangeEvent = visibilityChangeEvent;
exports.zIndexFactorOffset = zIndexFactorOffset;
}));
Object.assign(globalThis.window || globalThis, { fireworks: (globalThis.__tsParticlesInternals.bundles.fireworks || {}).fireworks });
delete (globalThis.window || globalThis).tsparticlesInternalExports;