/** Obrázky stylu Hell Girl (plamen + symbol ohně) — server je cachuje, viz server/src/confetti.ts. */ const HELLGIRL_IMAGES_URL = '/api/confetti/hellgirl'; export type HellGirlImages = { flame: string, symbol: string }; let imagesPromise: Promise | undefined; /** Načte URL obrázků (jednou za běh aplikace). Při chybě vrátí undefined a příště to zkusí znovu. */ export const loadHellGirlImages = (): Promise => { imagesPromise ??= fetch(HELLGIRL_IMAGES_URL) .then(r => r.ok ? r.json() : undefined) .then(images => images?.flame && images?.symbol ? images as HellGirlImages : undefined) .catch(() => undefined) .then(images => { if (!images) imagesPromise = undefined; return images; }); return imagesPromise; }; /** * Z obrázku symbolu (rudý tvar na bílém pozadí) udělá masku — průhlednost podle toho, jak * moc pixel není bílý (zelený kanál). Barvu pak symbolu dodá CSS gradientem v paletě stylu. */ export const createSymbolMask = (src: string, size = 96): Promise => new Promise(resolve => { const img = new Image(); img.onload = () => { try { const canvas = document.createElement('canvas'); canvas.width = canvas.height = size; const ctx = canvas.getContext('2d'); if (!ctx) return resolve(undefined); ctx.drawImage(img, 0, 0, size, size); const pixels = ctx.getImageData(0, 0, size, size); const data = pixels.data; for (let i = 0; i < data.length; i += 4) { data[i + 3] = 255 - data[i + 1]; data[i] = data[i + 1] = data[i + 2] = 0; } ctx.putImageData(pixels, 0, 0); resolve(canvas.toDataURL('image/png')); } catch { resolve(undefined); } }; img.onerror = () => resolve(undefined); img.src = src; });