UNPKG

@arwes/bgs

Version:

Futuristic Sci-Fi UI Web Framework

189 lines (188 loc) 6.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createBackgroundPuffs = void 0; const animated_1 = require("@arwes/animated"); const defaultProps = { color: '#777', quantity: 10, padding: 50, xOffset: [0, 0], yOffset: [-10, -100], radiusInitial: 4, radiusOffset: [4, 40], sets: 5 }; const minmaxOverflow01 = (value) => Math.min(1, Math.max(0, value === 1 ? 1 : value % 1)); const createBackgroundPuffs = (props) => { const { canvas, animator } = props; const ctx = canvas.getContext('2d'); if (!ctx) { return { cancel: () => { } }; } let resizeObserver; let transitionControl; let runningControl; let unsubscribe; let runningControlTimeoutId; let puffsSets = []; const getSettings = () => ({ ...defaultProps, ...props.settingsRef.current }); const createPuff = (width, height) => { const { padding, xOffset, yOffset, radiusInitial, radiusOffset } = getSettings(); const x = padding + Math.random() * (width - padding * 2); const y = padding + Math.random() * (height - padding * 2); const r = radiusInitial; const xo = xOffset[0] + Math.random() * xOffset[1]; const yo = yOffset[0] + Math.random() * yOffset[1]; const ro = radiusOffset[0] + Math.random() * radiusOffset[1]; return { x, y, r, xo, yo, ro }; }; const createPuffsSets = (width, height) => { const { quantity, sets } = getSettings(); const puffsSetQuantity = Math.round(quantity / sets); return Array(sets) .fill(null) .map(() => Array(puffsSetQuantity) .fill(null) .map(() => createPuff(width, height))); }; const resize = () => { const dpr = Math.min(window.devicePixelRatio || 2, 2); const { width, height } = canvas.getBoundingClientRect(); if (canvas.width !== width * dpr || canvas.height !== height * dpr) { canvas.width = width * dpr; canvas.height = height * dpr; } ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.scale(dpr, dpr); puffsSets = createPuffsSets(width, height); }; const drawPuffs = (puffs, progress) => { const { color } = getSettings(); ctx.globalAlpha = progress <= 0.5 ? progress * 2 : -2 * progress + 2; puffs.forEach((puff) => { const x = puff.x + progress * puff.xo; const y = puff.y + progress * puff.yo; const r = puff.r + progress * puff.ro; const grd = ctx.createRadialGradient(x, y, 0, x, y, r); grd.addColorStop(0, color); grd.addColorStop(1, 'transparent'); ctx.beginPath(); ctx.fillStyle = grd; ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.fill(); ctx.closePath(); }); }; const draw = (progress) => { const { sets } = getSettings(); const { width, height } = canvas; const puffsSetOffset = 1 / sets; ctx.clearRect(0, 0, width, height); puffsSets.forEach((puffs, index) => { const puffsOffset = puffsSetOffset * index; const puffsProgress = minmaxOverflow01(progress + puffsOffset); drawPuffs(puffs, animated_1.easing.outSine(puffsProgress)); }); }; const run = () => { if (!animator) { return; } const { duration: { interval = 2, intervalPause = 0 } } = animator.settings; runningControl?.cancel(); runningControl = (0, animated_1.createAnimation)({ duration: interval, easing: 'linear', onUpdate(progress) { draw(progress); }, onFinish() { const emptyDuration = intervalPause * 1000; window.clearTimeout(runningControlTimeoutId); runningControlTimeoutId = window.setTimeout(run, emptyDuration); } }); }; const setup = () => { if (typeof window !== 'undefined' && !resizeObserver) { resizeObserver = new window.ResizeObserver(() => { resize(); if (!animator) { draw(1); } }); resizeObserver.observe(canvas); resize(); } }; const stop = () => { resizeObserver?.disconnect(); resizeObserver = undefined; transitionControl?.cancel(); transitionControl = undefined; runningControl?.cancel(); runningControl = undefined; window.clearTimeout(runningControlTimeoutId); runningControlTimeoutId = undefined; }; const start = () => { if (!animator) { setup(); draw(1); canvas.style.opacity = '1'; return; } unsubscribe = animator.subscribe((node) => { switch (node.state) { case 'entering': { setup(); if (runningControl === undefined) { run(); } transitionControl?.cancel(); transitionControl = (0, animated_1.createAnimation)({ duration: node.settings.duration.enter, onUpdate(progress) { canvas.style.opacity = String(progress); } }); break; } case 'entered': { setup(); if (runningControl === undefined) { run(); } canvas.style.opacity = '1'; break; } case 'exiting': { transitionControl?.cancel(); transitionControl = (0, animated_1.createAnimation)({ duration: node.settings.duration.exit, onUpdate(progress) { canvas.style.opacity = String(1 - progress); } }); break; } case 'exited': { stop(); canvas.style.opacity = '0'; break; } } }); }; const cancel = () => { unsubscribe?.(); stop(); canvas.style.opacity = '0'; }; start(); return Object.freeze({ cancel }); }; exports.createBackgroundPuffs = createBackgroundPuffs;