53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import type { ExportRequest, WorkerResult } from "./types";
|
|
let worker: Worker | undefined;
|
|
let nextId = 0;
|
|
const pending = new Map<
|
|
number,
|
|
{
|
|
resolve: (blob: Blob) => void;
|
|
reject: (error: Error) => void;
|
|
format: string;
|
|
}
|
|
>();
|
|
export function renderImage(request: ExportRequest): Promise<Blob> {
|
|
if (!worker) {
|
|
worker = new Worker(new URL("./export.worker.ts", import.meta.url), {
|
|
type: "module",
|
|
});
|
|
worker.onmessage = ({ data }: MessageEvent<WorkerResult>) => {
|
|
const job = pending.get(data.id);
|
|
if (!job) return;
|
|
pending.delete(data.id);
|
|
if (data.error !== undefined) job.reject(new Error(data.error));
|
|
else job.resolve(new Blob([data.bytes], { type: `image/${job.format}` }));
|
|
};
|
|
worker.onerror = () => {
|
|
worker?.terminate();
|
|
worker = undefined;
|
|
for (const job of pending.values())
|
|
job.reject(new Error("Image renderer failed"));
|
|
pending.clear();
|
|
};
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const id = ++nextId;
|
|
pending.set(id, { resolve, reject, format: request.format });
|
|
try {
|
|
worker!.postMessage({ id, request, origin: window.location.origin });
|
|
} catch (error) {
|
|
pending.delete(id);
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
export function downloadImage(blob: Blob, fileName: string) {
|
|
const url = URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.download = fileName.replace(/[\\/:*?"<>|]/gu, "-");
|
|
link.href = url;
|
|
document.body.append(link);
|
|
link.click();
|
|
link.remove();
|
|
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
}
|