是否应该立即将图像 src 设置为数据 URL?

考虑以下(脆弱的) JavaScript 代码:

var img = new Image;
img.src = "data:image/png;base64,..."; // Assume valid data


// Danger(?) Attempting to use image immediately after setting src
console.log( img.width, img.height );
someCanvasContext.drawImage( img, 0, 0 );


// Danger(?) Setting onload after setting src
img.onload = function(){ console.log('I ran!'); };

问题

  • 图像的宽度和高度是否应该立即正确?
  • 画布是否应该立即画出作品?
  • 是否应该调用 onload回调函数(在 src 更改后设置) ?

实验测试
我用类似的代码创建了一个 简单测试页。在 Safari 中,我的第一个测试,通过在本地打开 HTML 页面(file:/// url)并从我的服务器加载它,显示了所有工作: 高度和宽度是正确的,画布绘制工作,还有onload也触发。

在 Firefox v3.6(OS X)中,启动浏览器后加载页面显示设置后立即出现的高度/宽度不正确,drawImage()失败。(但是,onload处理程序确实会触发。)然而,再次加载页面时,显示宽度/高度在设置后立即正确,并且 drawImage()正常工作。Firefox 似乎将数据 URL 的内容缓存为图像,并在同一会话中使用时立即可用。

在 Chrome v8(OS X)中,我看到了与 Firefox 相同的结果: 图像不能立即显示,但需要一些时间从数据 URL 异步“加载”。

除了实验证明上面的浏览器是否正常工作之外,我真的很喜欢链接到关于它应该如何工作的规范。到目前为止,我的谷歌功能还不能胜任这项任务。

谨慎行事
对于那些不明白为什么上面的图片可能是危险的人,你应该知道,为了安全起见,你应该使用这样的图片:

// First create/find the image
var img = new Image;


// Then, set the onload handler
img.onload = function(){
// Use the for-sure-loaded img here
};


// THEN, set the src
img.src = '...';
46012 次浏览

After giving control back to the browser's rendering engine, the test reports true in FF 4b9 and Chromium 8.0.552.237. I modified these parts:

img.onload = function(){   // put this above img.src = …
document.getElementById('onload').innerHTML = 'yes';
};
img.src = img_src;
…
window.setTimeout(function () {   // put this inside setTimeout
document.getElementById('wh').innerHTML = (img.height==img.width) && (img.height==128);
}, 0);

Update: Yes, I understand this 'answer' is more like a comment, but just wanted to point it out. And after testing I get reproducible results:

  • without setTimeout, in both browsers I get false the first time I open the page and true only after hitting F5. After explicitly clearing the cache both say false again after reloading.
  • with setTimeout the width/height test always evualuates to true.

In both cases the image doesn't show up the first time, only after reloading the page.

As no one has yet found any specifications about how this is supposed to behave, we will have to be satisfied with how it does behave. Following are the results of my tests.

| is image data usable right  | does onload callback set after src
|     after setting src?      |   is changed still get invoked?
------------+-----------------------------+-------------------------------------
Safari  5   |             yes             |                  yes
------------+-----------------------------+-------------------------------------
Chrome  8   | **no** (1st page load), but |                  yes
FireFox 3.6 |  yes thereafter (cached)    |
------------+-----------------------------+-------------------------------------
IE8         |    yes (32kB data limit)    |                  yes
------------+-----------------------------+-------------------------------------
IE9b        |             yes             |                 **no**
------------+-----------------------------+-------------------------------------

In summary:

  • You cannot assume that the image data will be available right after setting a data-uri; you must wait for the onload event.
  • Due to IE9, you cannot set the onload handler after setting the src and expect it to be invoked.
  • The recommendations in "Playing it Safe" (from the question above) are the only way to ensure correct behavior.

If anyone can find specs discussing this, I will happily accept their answer instead.