我从零经验的网络工作者到现在工作的时间比我愿意承认的要长,这让我失败了。
我想将一个长任务
canvas.toBlob(/* jpeg stuff */)
移至工作人员,但向工作人员发送 SVG,将其绘制到屏幕外画布上,然后转换为 JPEG 并将其发送回主线程。
这是我的代码:
self.onmessage = async event => {
const { imageSVG, width, height } = event.data;
try {
// Set up the offscreen canvas
const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext('2d');
// Create the image and draw to canvas
const imageSrc = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(imageSVG)))}`;
const imgBlob = await fetch(imageSrc)
.then(response => response.blob());
const img = await createImageBitmap(imgBlob);
ctx.drawImage(img, 0, 0, width, height);
offscreen.convertToBlob({
type: 'image/jpeg',
quality: 1.0
}).then(blob => {
postMessage({ blob: blob });
});
}
catch(error) {
postMessage(`Error: ${error}`);
}
};
我无法克服的错误是
InvalidStateError: The source image could not be decoded.
,我在网上找不到太多关于它的含义的信息。
我确实遇到过一条 github 评论,现在丢失了一百万个关闭的选项卡,建议将数据 URI 包装在 SVGImageElement 中,但我不知道这意味着什么,而且他们没有详细说明。
虽然规范目前表示应该支持这一点,但没有浏览器实现从
Blob
以及在 Worker
中对 SVG 进行解码,而且他们也不打算这样做。我什至写了一个 PR,以便将其从规范中删除。
他们不想这样做的原因是他们需要将所有 DOM 路径公开给工作人员,而目前工作人员不需要它。
至于解决方法,您必须通过 <img>
:
从 UI 上下文中解码图像
if (blob.type === "image/svg+xml") {
const img = new Image();
img.src = URL.createObjectURL(blob);
await img.decode();
URL.revokeObjectURL(img.src);
bmp = await createImageBitmap(img);
worker.postMessage(bmp, [bmp]);
}
可能会注意到,大部分
的工作应该在另一个线程上完成,并且不应该阻塞您的UI,但为了100%安全,您可以使用跨源隔离的<iframe>
,它将在另一个线程上运行线程,尽管设置要复杂得多。