webp-hero
Version:
webp image format polyfill for browsers
94 lines • 3.25 kB
JavaScript
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