Skip to content

Instantly share code, notes, and snippets.

@ahtcx
Created September 15, 2020 21:46
Show Gist options
  • Save ahtcx/d4d5c65d660ca2fddc22e75793497a64 to your computer and use it in GitHub Desktop.
Save ahtcx/d4d5c65d660ca2fddc22e75793497a64 to your computer and use it in GitHub Desktop.
export type UseRasterizedImageUrlParameters = [imageUrl: string, width: number, height?: number];
export interface UseRasterizedImageUrlCache {
promise: Promise<void>;
inputs: UseRasterizedImageUrlParameters;
error?: unknown;
rasterizedImageUrl?: string;
}
const caches: UseRasterizedImageUrlCache[] = [];
export const getRasterizedImageUrl = (imageUrl: string, width: number, height: number = width) =>
new Promise<string>(async (resolve) => {
const domParser = new DOMParser();
const xmlSerializer = new XMLSerializer();
const canvasElement = window.document.createElement("canvas");
canvasElement.width = width;
canvasElement.height = height;
const imageUrlResponse = await fetch(imageUrl);
const svgElement = domParser.parseFromString(await imageUrlResponse.text(), "image/svg+xml").querySelector("svg");
if (!svgElement) {
throw new Error();
}
if (svgElement.getAttribute("width") === null) {
svgElement.setAttribute("width", `${svgElement.viewBox.baseVal.width ?? width}px`);
}
if (svgElement.getAttribute("height") === null) {
svgElement.setAttribute("height", `${svgElement.viewBox.baseVal.height ?? height}px`);
}
const image = new Image();
image.onload = () => {
canvasElement.getContext("2d")?.drawImage(image, 0, 0, width, height);
resolve(canvasElement.toDataURL("image/png"));
};
image.src = `data:image/svg+xml;base64,${btoa(xmlSerializer.serializeToString(svgElement))}`;
});
export const useRasterizedImageUrl = (...inputs: UseRasterizedImageUrlParameters) => {
for (const cache of caches) {
if (cache.inputs.every((input) => inputs.includes(input))) {
if (Object.prototype.hasOwnProperty.call(cache, "error")) {
throw cache.error;
}
if (Object.prototype.hasOwnProperty.call(cache, "rasterizedImageUrl")) {
return cache.rasterizedImageUrl!;
}
throw cache.promise;
}
}
const cache: UseRasterizedImageUrlCache = {
promise: new Promise(async (resolve, reject) => {
try {
cache.rasterizedImageUrl = await getRasterizedImageUrl(...inputs);
resolve();
} catch (error) {
cache.error = error;
reject(error);
}
}),
inputs,
};
caches.push(cache);
throw cache.promise;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment