UNPKG

tsparticles

Version:

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

266 lines (265 loc) 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Particles = void 0; const Particle_1 = require("./Particle"); const Utils_1 = require("../Utils"); const InteractionManager_1 = require("./Particle/InteractionManager"); const ParticlesOptions_1 = require("../Options/Classes/Particles/ParticlesOptions"); class Particles { constructor(container) { this.container = container; this.nextId = 0; this.array = []; this.limit = 0; this.linksFreq = new Map(); this.trianglesFreq = new Map(); this.interactionManager = new InteractionManager_1.InteractionManager(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); } get count() { return this.array.length; } init() { const container = this.container; const options = container.actualOptions; this.linksFreq = new Map(); this.trianglesFreq = new Map(); let handled = false; 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); } for (const [, plugin] of container.plugins) { if (plugin.particlesInitialization !== undefined) { handled = plugin.particlesInitialization(); } if (handled) { break; } } if (!handled) { for (let i = this.count; i < options.particles.number.value; i++) { this.addParticle(); } } if (options.infection.enable) { for (let i = 0; i < options.infection.infections; i++) { const notInfected = this.array.filter((p) => p.infecter.infectionStage === undefined); const infected = Utils_1.Utils.itemFromArray(notInfected); infected.infecter.startInfection(0); } } this.interactionManager.init(); container.pathGenerator.init(); } redraw() { this.clear(); this.init(); this.draw({ value: 0, factor: 0 }); } removeAt(index, quantity, override) { if (index >= 0 && index <= this.count) { for (const particle of this.array.splice(index, quantity !== null && quantity !== void 0 ? quantity : 1)) { particle.destroy(override); } } } remove(particle, override) { this.removeAt(this.array.indexOf(particle), undefined, override); } 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 = this.container.canvas.resizeFactor; if (resizeFactor) { particle.position.x *= resizeFactor.width; particle.position.y *= resizeFactor.height; } particle.move(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); } this.interactionManager.externalInteract(delta); for (const particle of this.container.particles.array) { particle.update(delta); if (!particle.destroyed && !particle.spawning) { this.interactionManager.particlesInteract(particle, delta); } } delete container.canvas.resizeFactor; } 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); this.update(delta); for (const [, plugin] of container.plugins) { container.canvas.drawPlugin(plugin, delta); } for (const p of this.array) { p.draw(delta); } } clear() { this.array = []; } push(nb, mouse, overrideOptions) { const container = this.container; const options = container.actualOptions; const limit = options.particles.number.limit * container.density; this.pushing = true; if (limit > 0) { const countToRemove = this.count + nb - limit; if (countToRemove > 0) { this.removeQuantity(countToRemove); } } for (let i = 0; i < nb; i++) { this.addParticle(mouse === null || mouse === void 0 ? void 0 : mouse.position, overrideOptions); } this.pushing = false; } addParticle(position, overrideOptions) { return this.pushParticle(position, overrideOptions); } addSplitParticle(parent) { const splitOptions = parent.options.destroy.split; const options = new ParticlesOptions_1.ParticlesOptions(); options.load(parent.options); const factor = Utils_1.NumberUtils.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 = Utils_1.NumberUtils.setRangeValue(-parent.size.value, parent.size.value); const position = { x: parent.position.x + Utils_1.NumberUtils.randomInRange(offset), y: parent.position.y + Utils_1.NumberUtils.randomInRange(offset), }; return this.pushParticle(position, options, (particle) => { if (particle.size.value < 0.5) { return false; } particle.velocity.length = Utils_1.NumberUtils.randomInRange(Utils_1.NumberUtils.setRangeValue(parent.velocity.length, particle.velocity.length)); particle.splitCount = parent.splitCount + 1; particle.unbreakable = true; setTimeout(() => { particle.unbreakable = false; }, 500); return true; }); } removeQuantity(quantity) { this.removeAt(0, quantity); } getLinkFrequency(p1, p2) { const key = `${Math.min(p1.id, p2.id)}_${Math.max(p1.id, p2.id)}`; let res = this.linksFreq.get(key); if (res === undefined) { res = Math.random(); this.linksFreq.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.trianglesFreq.get(key); if (res === undefined) { res = Math.random(); this.trianglesFreq.set(key, res); } return res; } setDensity() { const options = this.container.actualOptions; this.applyDensity(options.particles); } applyDensity(options) { 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; const particlesCount = this.count; this.limit = numberOptions.limit * densityFactor; if (particlesCount < particlesNumber) { this.push(Math.abs(particlesNumber - particlesCount), undefined, options); } else if (particlesCount > particlesNumber) { this.removeQuantity(particlesCount - particlesNumber); } } initDensityFactor(densityOptions) { const container = this.container; if (!container.canvas.element || !densityOptions.enable) { return 1; } const canvas = container.canvas.element; const pxRatio = container.retina.pixelRatio; return (canvas.width * canvas.height) / (densityOptions.factor * pxRatio * pxRatio * densityOptions.area); } pushParticle(position, overrideOptions, initializer) { try { const particle = new Particle_1.Particle(this.nextId, this.container, position, overrideOptions); let canAdd = true; if (initializer) { canAdd = initializer(particle); } if (!canAdd) { return; } this.array.push(particle); this.nextId++; return particle; } catch (e) { console.warn(`error adding particle: ${e}`); return; } } } exports.Particles = Particles;