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));

此方法也适用于以 Blob 形式存储在 IndexedDB 中的图片,使 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、样式计算、布局、绘制或合成。

帮助程序库

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

如果您在图像解码方面需要更多控制权,createImageBitmap() 是您的新最好伙伴。欢迎使用 Chrome 50 立即试用,并告诉我们您的使用效果!