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。

在网页工作器中使用 createImageBitmap()

createImageBitmap() 的一大优势在于,它还可在工作器中使用,这意味着您现在可以在任何位置解码图片。如果您有大量您认为不重要的图片需要解码,则可以将其网址发送到 Web 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、样式计算、布局、绘制或合成。

帮助程序库

为了简化操作,我创建了一个辅助库,用于在 worker 上处理解码,并将解码后的图片发回主线程,然后将其绘制到画布中。当然,您可以随意对其进行逆向工程,并将模型应用于您自己的应用。主要优势是控制力更强,但与使用 <img> 元素相比,这意味着需要编写更多代码、进行更多调试,并考虑更多边缘情况。

如果您需要更好地控制图片解码,createImageBitmap() 就是您的新好友。请在 Chrome 50 中试用此功能,并告诉我们您的使用体验!