tsparticles
Version:
Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.
323 lines (322 loc) • 13.7 kB
JavaScript
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Particles_engine;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Particles = void 0;
const Utils_1 = require("./Utils");
const Utils_2 = require("../Utils");
const Particle_1 = require("./Particle");
const ParticlesOptions_1 = require("../Options/Classes/Particles/ParticlesOptions");
class Particles {
constructor(engine, container) {
this.container = container;
_Particles_engine.set(this, void 0);
__classPrivateFieldSet(this, _Particles_engine, engine, "f");
this.nextId = 0;
this.array = [];
this.zArray = [];
this.mover = new Utils_1.ParticlesMover(container);
this.limit = 0;
this.needsSort = false;
this.lastZIndex = 0;
this.freqs = {
links: new Map(),
triangles: new Map(),
};
this.interactionManager = new Utils_1.InteractionManager(__classPrivateFieldGet(this, _Particles_engine, "f"), container);
const canvasSize = this.container.canvas.size;
this.linksColors = new Map();
this.quadTree = new Utils_1.QuadTree(new Utils_1.Rectangle(-canvasSize.width / 4, -canvasSize.height / 4, (canvasSize.width * 3) / 2, (canvasSize.height * 3) / 2), 4);
this.updaters = __classPrivateFieldGet(this, _Particles_engine, "f").plugins.getUpdaters(container, true);
}
get count() {
return this.array.length;
}
init() {
var _a;
const container = this.container;
const options = container.actualOptions;
this.lastZIndex = 0;
this.needsSort = false;
this.freqs.links = new Map();
this.freqs.triangles = new Map();
let handled = false;
this.updaters = __classPrivateFieldGet(this, _Particles_engine, "f").plugins.getUpdaters(container, true);
this.interactionManager.init();
for (const [, plugin] of container.plugins) {
if (plugin.particlesInitialization !== undefined) {
handled = plugin.particlesInitialization();
}
if (handled) {
break;
}
}
this.addManualParticles();
if (!handled) {
for (const group in options.particles.groups) {
const groupOptions = options.particles.groups[group];
for (let i = this.count, j = 0; j < ((_a = groupOptions.number) === null || _a === void 0 ? void 0 : _a.value) && i < options.particles.number.value; i++, j++) {
this.addParticle(undefined, groupOptions, group);
}
}
for (let i = this.count; i < options.particles.number.value; i++) {
this.addParticle();
}
}
container.pathGenerator.init(container);
}
async redraw() {
this.clear();
this.init();
await this.draw({ value: 0, factor: 0 });
}
removeAt(index, quantity = 1, group, override) {
if (!(index >= 0 && index <= this.count)) {
return;
}
let deleted = 0;
for (let i = index; deleted < quantity && i < this.count; i++) {
const particle = this.array[i];
if (!particle || particle.group !== group) {
continue;
}
particle.destroy(override);
this.array.splice(i--, 1);
const zIdx = this.zArray.indexOf(particle);
this.zArray.splice(zIdx, 1);
deleted++;
}
}
remove(particle, group, override) {
this.removeAt(this.array.indexOf(particle), undefined, group, override);
}
async update(delta) {
const container = this.container;
const particlesToDelete = [];
container.pathGenerator.update();
for (const [, plugin] of container.plugins) {
if (plugin.update !== undefined) {
plugin.update(delta);
}
}
for (const particle of this.array) {
const resizeFactor = container.canvas.resizeFactor;
if (resizeFactor && !particle.ignoresResizeRatio) {
particle.position.x *= resizeFactor.width;
particle.position.y *= resizeFactor.height;
}
particle.ignoresResizeRatio = false;
particle.bubble.inRange = false;
for (const [, plugin] of this.container.plugins) {
if (particle.destroyed) {
break;
}
if (plugin.particleUpdate) {
plugin.particleUpdate(particle, delta);
}
}
this.mover.move(particle, delta);
if (particle.destroyed) {
particlesToDelete.push(particle);
continue;
}
this.quadTree.insert(new Utils_1.Point(particle.getPosition(), particle));
}
for (const particle of particlesToDelete) {
this.remove(particle);
}
await this.interactionManager.externalInteract(delta);
for (const particle of container.particles.array) {
for (const updater of this.updaters) {
updater.update(particle, delta);
}
if (!particle.destroyed && !particle.spawning) {
await this.interactionManager.particlesInteract(particle, delta);
}
}
delete container.canvas.resizeFactor;
}
async draw(delta) {
const container = this.container;
container.canvas.clear();
const canvasSize = this.container.canvas.size;
this.quadTree = new Utils_1.QuadTree(new Utils_1.Rectangle(-canvasSize.width / 4, -canvasSize.height / 4, (canvasSize.width * 3) / 2, (canvasSize.height * 3) / 2), 4);
await this.update(delta);
if (this.needsSort) {
this.zArray.sort((a, b) => b.position.z - a.position.z || a.id - b.id);
this.lastZIndex = this.zArray[this.zArray.length - 1].position.z;
this.needsSort = false;
}
for (const [, plugin] of container.plugins) {
container.canvas.drawPlugin(plugin, delta);
}
for (const p of this.zArray) {
p.draw(delta);
}
}
clear() {
this.array = [];
this.zArray = [];
}
push(nb, mouse, overrideOptions, group) {
this.pushing = true;
for (let i = 0; i < nb; i++) {
this.addParticle(mouse === null || mouse === void 0 ? void 0 : mouse.position, overrideOptions, group);
}
this.pushing = false;
}
addParticle(position, overrideOptions, group) {
const container = this.container, options = container.actualOptions, limit = options.particles.number.limit * container.density;
if (limit > 0) {
const countToRemove = this.count + 1 - limit;
if (countToRemove > 0) {
this.removeQuantity(countToRemove);
}
}
return this.pushParticle(position, overrideOptions, group);
}
addSplitParticle(parent) {
const splitOptions = parent.options.destroy.split, options = new ParticlesOptions_1.ParticlesOptions();
options.load(parent.options);
const factor = (0, Utils_2.getRangeValue)(splitOptions.factor.value);
options.color.load({
value: {
hsl: parent.getFillColor(),
},
});
if (typeof options.size.value === "number") {
options.size.value /= factor;
}
else {
options.size.value.min /= factor;
options.size.value.max /= factor;
}
options.load(splitOptions.particles);
const offset = splitOptions.sizeOffset ? (0, Utils_2.setRangeValue)(-parent.size.value, parent.size.value) : 0;
const position = {
x: parent.position.x + (0, Utils_2.randomInRange)(offset),
y: parent.position.y + (0, Utils_2.randomInRange)(offset),
};
return this.pushParticle(position, options, parent.group, (particle) => {
if (particle.size.value < 0.5) {
return false;
}
particle.velocity.length = (0, Utils_2.randomInRange)((0, Utils_2.setRangeValue)(parent.velocity.length, particle.velocity.length));
particle.splitCount = parent.splitCount + 1;
particle.unbreakable = true;
setTimeout(() => {
particle.unbreakable = false;
}, 500);
return true;
});
}
removeQuantity(quantity, group) {
this.removeAt(0, quantity, group);
}
getLinkFrequency(p1, p2) {
const range = (0, Utils_2.setRangeValue)(p1.id, p2.id), key = `${(0, Utils_2.getRangeMin)(range)}_${(0, Utils_2.getRangeMax)(range)}`;
let res = this.freqs.links.get(key);
if (res === undefined) {
res = Math.random();
this.freqs.links.set(key, res);
}
return res;
}
getTriangleFrequency(p1, p2, p3) {
let [id1, id2, id3] = [p1.id, p2.id, p3.id];
if (id1 > id2) {
[id2, id1] = [id1, id2];
}
if (id2 > id3) {
[id3, id2] = [id2, id3];
}
if (id1 > id3) {
[id3, id1] = [id1, id3];
}
const key = `${id1}_${id2}_${id3}`;
let res = this.freqs.triangles.get(key);
if (res === undefined) {
res = Math.random();
this.freqs.triangles.set(key, res);
}
return res;
}
addManualParticles() {
const container = this.container, options = container.actualOptions;
for (const particle of options.manualParticles) {
const pos = particle.position
? {
x: (particle.position.x * container.canvas.size.width) / 100,
y: (particle.position.y * container.canvas.size.height) / 100,
}
: undefined;
this.addParticle(pos, particle.options);
}
}
setDensity() {
const options = this.container.actualOptions;
for (const group in options.particles.groups) {
this.applyDensity(options.particles.groups[group], 0, group);
}
this.applyDensity(options.particles, options.manualParticles.length);
}
applyDensity(options, manualCount, group) {
var _a;
if (!((_a = options.number.density) === null || _a === void 0 ? void 0 : _a.enable)) {
return;
}
const numberOptions = options.number;
const densityFactor = this.initDensityFactor(numberOptions.density);
const optParticlesNumber = numberOptions.value;
const optParticlesLimit = numberOptions.limit > 0 ? numberOptions.limit : optParticlesNumber;
const particlesNumber = Math.min(optParticlesNumber, optParticlesLimit) * densityFactor + manualCount;
const particlesCount = Math.min(this.count, this.array.filter((t) => t.group === group).length);
this.limit = numberOptions.limit * densityFactor;
if (particlesCount < particlesNumber) {
this.push(Math.abs(particlesNumber - particlesCount), undefined, options, group);
}
else if (particlesCount > particlesNumber) {
this.removeQuantity(particlesCount - particlesNumber, group);
}
}
initDensityFactor(densityOptions) {
const container = this.container;
if (!container.canvas.element || !densityOptions.enable) {
return 1;
}
const canvas = container.canvas.element, pxRatio = container.retina.pixelRatio;
return (canvas.width * canvas.height) / (densityOptions.factor * pxRatio ** 2 * densityOptions.area);
}
pushParticle(position, overrideOptions, group, initializer) {
try {
const particle = new Particle_1.Particle(__classPrivateFieldGet(this, _Particles_engine, "f"), this.nextId, this.container, position, overrideOptions, group);
let canAdd = true;
if (initializer) {
canAdd = initializer(particle);
}
if (!canAdd) {
return;
}
this.array.push(particle);
this.zArray.push(particle);
this.nextId++;
return particle;
}
catch (e) {
console.warn(`error adding particle: ${e}`);
return;
}
}
}
exports.Particles = Particles;
_Particles_engine = new WeakMap();