UNPKG

@tsparticles/engine

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.

304 lines (303 loc) 10.8 kB
import { clear, drawParticle, drawParticlePlugin, paintBase, paintImage } from "../Utils/CanvasUtils.js"; import { defaultCompositeValue, defaultTransformValue, minimumSize, zIndexFactorOffset } from "./Utils/Constants.js"; import { getStyleFromHsl, rangeColorToHsl } from "../Utils/ColorUtils.js"; const fColorIndex = 0, sColorIndex = 1; function setTransformValue(factor, newFactor, key) { const newValue = newFactor[key]; if (newValue !== undefined) { factor[key] = (factor[key] ?? defaultTransformValue) * newValue; } } export class RenderManager { #canvasClearPlugins; #canvasManager; #canvasPaintPlugins; #clearDrawPlugins; #colorPlugins; #container; #context; #contextSettings; #drawParticlePlugins; #drawParticlesCleanupPlugins; #drawParticlesSetupPlugins; #drawPlugins; #drawSettingsCleanupPlugins; #drawSettingsSetupPlugins; #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.#preDrawUpdaters = []; this.#postDrawUpdaters = []; this.#colorPlugins = []; this.#canvasClearPlugins = []; this.#canvasPaintPlugins = []; this.#clearDrawPlugins = []; this.#drawParticlePlugins = []; this.#drawParticlesCleanupPlugins = []; this.#drawParticlesSetupPlugins = []; this.#drawPlugins = []; this.#drawSettingsSetupPlugins = []; this.#drawSettingsCleanupPlugins = []; } get settings() { return this.#contextSettings; } canvasClear() { if (!this.#container.actualOptions.clear) { return; } this.draw(ctx => { clear(ctx, this.#canvasManager.size); }); } clear() { let pluginHandled = false; for (const plugin of this.#canvasClearPlugins) { pluginHandled = plugin.canvasClear?.() ?? false; if (pluginHandled) { break; } } if (pluginHandled) { return; } this.canvasClear(); } destroy() { this.stop(); this.#preDrawUpdaters = []; this.#postDrawUpdaters = []; this.#colorPlugins = []; this.#canvasClearPlugins = []; this.#canvasPaintPlugins = []; this.#clearDrawPlugins = []; this.#drawParticlePlugins = []; this.#drawParticlesCleanupPlugins = []; this.#drawParticlesSetupPlugins = []; this.#drawPlugins = []; this.#drawSettingsSetupPlugins = []; this.#drawSettingsCleanupPlugins = []; } 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) : undefined, stroke = sColor ? getStyleFromHsl(sColor, container.hdr, strokeOpacity * opacity) : 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); 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) { drawParticlePlugin(ctx, plugin, particle, delta); } }); } drawParticles(delta) { const { particles } = this.#container; this.clear(); particles.update(delta); this.draw(ctx => { for (const plugin of this.#drawSettingsSetupPlugins) { plugin.drawSettingsSetup?.(ctx, delta); } for (const plugin of this.#drawPlugins) { plugin.draw?.(ctx, delta); } particles.drawParticles(delta); for (const plugin of this.#clearDrawPlugins) { plugin.clearDraw?.(ctx, delta); } for (const plugin of this.#drawSettingsCleanupPlugins) { plugin.drawSettingsCleanup?.(ctx, delta); } }); } init() { this.initUpdaters(); this.initPlugins(); this.paint(); } initPlugins() { this.#colorPlugins = []; this.#canvasClearPlugins = []; this.#canvasPaintPlugins = []; this.#clearDrawPlugins = []; this.#drawParticlePlugins = []; this.#drawParticlesSetupPlugins = []; this.#drawParticlesCleanupPlugins = []; this.#drawPlugins = []; this.#drawSettingsSetupPlugins = []; this.#drawSettingsCleanupPlugins = []; for (const plugin of this.#container.plugins) { if (plugin.particleFillColor ?? plugin.particleStrokeColor) { this.#colorPlugins.push(plugin); } if (plugin.canvasClear) { this.#canvasClearPlugins.push(plugin); } if (plugin.canvasPaint) { this.#canvasPaintPlugins.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.draw) { this.#drawPlugins.push(plugin); } if (plugin.drawSettingsSetup) { this.#drawSettingsSetupPlugins.push(plugin); } if (plugin.drawSettingsCleanup) { this.#drawSettingsCleanupPlugins.push(plugin); } if (plugin.clearDraw) { this.#clearDrawPlugins.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.#canvasPaintPlugins) { handled = plugin.canvasPaint?.() ?? false; if (handled) { break; } } if (handled) { return; } this.paintBase(); } paintBase(baseColor) { this.draw(ctx => { paintBase(ctx, this.#canvasManager.size, baseColor); }); } paintImage(image, opacity) { this.draw(ctx => { paintImage(ctx, this.#canvasManager.size, image, opacity); }); } setContext(context) { this.#context = context; if (this.#context) { this.#context.globalCompositeOperation = defaultCompositeValue; } } setContextSettings(settings) { this.#contextSettings = settings; } stop() { this.draw(ctx => { clear(ctx, this.#canvasManager.size); }); } #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); } }; #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; }; }