Chrome 浏览器在 Chrome 50 中支持 createImageBitmap()

Paul Lewis

无论是要允许用户自定义头像、剪裁图片,还是仅放大图片,解码图片以在画布中使用都是很常见的。解码图像的问题是会占用大量 CPU 资源,有时意味着卡顿或棋盘格。从 Chrome 50(以及 Firefox 42 及更高版本)开始,您有另一个选项:createImageBitmap()。它允许您在后台解码图片,以及访问新的 ImageBitmap 基元,您可以像绘制 <img> 元素、其他画布或视频一样将其绘制到画布中。

使用 createImageBitmap() 绘制 blob

假设您使用 fetch()(或 XHR)下载了一个 blob 图像,并希望将其绘制到画布中。如果没有 createImageBitmap(),您必须创建图片元素和 Blob 网址,才能将图片转换为您可以使用的格式。通过它,您可以更直接地进行绘画:

fetch(url)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));

此方法也适用于 IndexedDB 中作为 blob 存储的图片,使 blob 成为一种方便的中间格式。恰巧的是,Chrome 50 也支持对画布元素使用 .toBlob() 方法,这意味着您可以执行多种操作,例如从画布元素生成 blob。

在 Web Worker 中使用 createImageBitmap()

createImageBitmap() 最棒的功能之一是它在 worker 中也可用,这意味着您现在可以随时随地解码图像。如果您要解码大量的图像,而且您认为这些图像不是必需图像,则可将这些图像的网址发送给 Web Worker,后者会在时间允许的情况下下载并解码这些图像。然后,它会将这些任务移回主线程,以将其绘制到画布中。

使用 createImageBitmap 和 Web 工作器的数据流。

执行此操作的代码可能如下所示:

// In the worker.
fetch(imageURL)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => {
    // Transfer the imageBitmap back to main thread.
    self.postMessage({ imageBitmap }, [imageBitmap]);
    }, err => {
    self.postMessage({ err });
    });

// In the main thread.
worker.onmessage = (evt) => {
    if (evt.data.err)
    throw new Error(evt.data.err);

    canvasContext.drawImage(evt.data.imageBitmap, 0, 0);
}

现在,如果您在主线程上调用 createImageBitmap(),这将正是解码的位置。不过,我们计划让 Chrome 在另一个线程中自动解码,从而帮助降低主线程的工作负载。同时,您应注意在主线程上解码,因为这是密集型工作,可能会阻止其他基本任务(如 JavaScript、样式计算、布局、绘制或合成)。

帮助程序库

为了简化工作,我创建了一个辅助程序库,用于处理工作器的解码,并将解码后的图片发回到主线程,并将其绘制到画布中。当然,您可以随意对其进行逆向工程,并将模型应用于您自己的应用。主要好处是具有更大的控制权,但与使用 <img> 元素相比,这(和往常一样)需要更多代码、更多需要调试的代码,以及更多需要考虑的极端情况。

如果您需要通过图像解码实现更多控制,createImageBitmap() 将成为您的新伙伴。欢迎在 Chrome 50 中体验此功能,并告知我们您的进展!