UNPKG

webp-hero

Version:

webp image format polyfill for browsers

94 lines 3.25 kB
import { Webp } from "../libwebp/dist/webp.js"; import { loadBinaryData } from "./load-binary-data.js"; import { detectWebpSupport } from "./detect-webp-support.js"; import { convertDataURIToBinary, isBase64Url } from "./convert-binary-data.js"; const relax = () => new Promise(resolve => requestAnimationFrame(resolve)); export class WebpMachineError extends Error { } export const defaultDetectWebpImage = (image) => /\.webp.*$/i.test(image.src); /** * Webp Machine * - decode and polyfill webp images * - can only decode images one-at-a-time (otherwise will throw busy error) */ export class WebpMachine { constructor({ webp = new Webp(), webpSupport = detectWebpSupport(), detectWebpImage = defaultDetectWebpImage } = {}) { this.busy = false; this.cache = {}; this.webp = webp; this.webpSupport = webpSupport; this.detectWebpImage = detectWebpImage; } /** * Decode raw webp data into a png data url */ async decode(webpData) { if (this.busy) throw new WebpMachineError("cannot decode when already busy"); this.busy = true; try { await relax(); const canvas = document.createElement("canvas"); this.webp.setCanvas(canvas); this.webp.webpToSdl(webpData, webpData.length); this.busy = false; return canvas.toDataURL(); } catch (error) { this.busy = false; error.name = WebpMachineError.name; error.message = `failed to decode webp image: ${error.message}`; throw error; } } /** * Polyfill the webp format on the given <img> element */ async polyfillImage(image) { if (await this.webpSupport) return; const { src } = image; if (this.detectWebpImage(image)) { if (this.cache[src]) { image.src = this.cache[src]; return; } try { const webpData = isBase64Url(src) ? convertDataURIToBinary(src) : await loadBinaryData(src); const pngData = await this.decode(webpData); image.src = this.cache[src] = pngData; } catch (error) { error.name = WebpMachineError.name; error.message = `failed to polyfill image "${src}": ${error.message}`; throw error; } } } /** * Polyfill webp format on the entire web page */ async polyfillDocument({ document = window.document } = {}) { if (await this.webpSupport) return null; for (const image of Array.from(document.querySelectorAll("img"))) { try { await this.polyfillImage(image); } catch (error) { error.name = WebpMachineError.name; error.message = `webp image polyfill failed for url "${image.src}": ${error}`; throw error; } } } /** * Manually wipe the cache to save memory */ clearCache() { this.cache = {}; } } //# sourceMappingURL=webp-machine.js.map