76

考虑以下(脆弱的)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 异步“加载”。

除了以上浏览器的工作或不工作的实验证明之外,我真的很喜欢关于它应该如何表现的规范的链接。到目前为止,我的 Google-fu 还没有完成这项任务。

安全
起见对于那些不明白为什么上述内容可能很危险的人,请知道您应该使用这样的图像以确保安全:

// 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 = '...';
4

2 回答 2

53

由于还没有人找到任何关于它应该如何表现的规范,我们必须对它的表现感到满意。以下是我的测试结果。

            | 图像数据可以使用吗?在 src 之后设置 onload 回调
            | 设置src后?| 改变了仍然被调用?
------------+-----------------+------ ------------------------------
野生动物园 5 | 是的 | 是的                
------------+-----------------+------ ------------------------------
铬 8 | **没有**(第一页加载),但是 | 是的                
火狐3.6 | 是,此后(缓存)|                                     
------------+-----------------+------ ------------------------------
IE8 | 是(32kB 数据限制)| 是的         
------------+-----------------+------ ------------------------------
IE9b | 是的 | **不**              
------------+-----------------+------ ------------------------------

总之:

  • 您不能假设在设置 data-uri 后图像数据将立即可用;你必须等待onload事件。
  • 由于 IE9,您无法onload在设置后设置处理程序src并期望调用它。
  • “安全行事”中的建议(来自上述问题)是确保正确行为的唯一方法。

如果有人能找到讨论这个的规范,我会很乐意接受他们的回答。

于 2011-01-26T16:33:12.603 回答
7

将控制权交还给浏览器的渲染引擎后,trueFF 4b9 和 Chromium 8.0.552.237 中的测试报告。我修改了这些部分:

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

更新:是的,我知道这个“答案”更像是评论,但只是想指出这一点。经过测试,我得到了可重复的结果:

  • 没有setTimeout,在这两种浏览器中,我false第一次打开页面时才得到,true只有在点击之后F5。明确清除缓存false后,重新加载后都再说一遍。
  • setTimeout宽度/高度测试总是评估为true.

在这两种情况下,图像都不会第一次出现,只有在重新加载页面后才会出现。

于 2011-01-23T21:50:35.653 回答