应该立即设置一个图像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!'); };
问题(S)
- 图像宽度和高度是否应该立即正确?
- canvas是否应立即绘制?
- 应该
onload
callback(在src之后设置)吗?
实验testing
我用类似的代码创build了一个简单的testing页面 。 在Safari中,我的第一个testing是通过在本地打开HTML页面( file:///
url)并从我的服务器加载它,显示了所有的工作:高度和宽度是正确的,canvas绘制工作, onload
也触发。
在Firefox v3.6(OS X)中,启动浏览器后加载页面显示高度/宽度在设置后立即不正确, drawImage()
失败。 (但是, onload
处理程序确实会触发。)但是,重新加载页面会在设置后立即显示宽度/高度, drawImage()
正常工作。 Firefox似乎将数据URL的内容caching为图像,并且在同一会话中使用时立即可用。
在Chrome v8(OS X)中,我看到了与Firefox中相同的结果:图像不是立即可用,但需要一些时间从数据URLasynchronous“加载”。
除了上面的浏览器做了什么或不工作的实validation明之外,我真的很喜欢关于这个应该如何performance的规范的链接。 到目前为止,我的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 = '...';
由于还没有人find有关这种行为的规范,我们必须对它的行为表示满意。 以下是我的testing结果。
| 是可以使用的图像数据 在src之后的onloadcallback设置 | 设置src后? | 改变仍然被调用? ------------ + ----------------------------- + ------- ------------------------------ Safari 5 | 是| 是 ------------ + ----------------------------- + ------- ------------------------------ Chrome 8 | **没有**(第一页加载),但| 是 FireFox 3.6 | 之后是(caching)| ------------ + ----------------------------- + ------- ------------------------------ IE8 | 是(32kB数据限制)| 是 ------------ + ----------------------------- + ------- ------------------------------ IE9b | 是| **没有** ------------ + ----------------------------- + ------- ------------------------------
综上所述:
- 设置data-uri后,您不能假定图像数据可用。 你必须等待
onload
事件。 - 由于IE9,你不能在设置
src
之后设置onload
处理程序,并期望它被调用。 - “保证安全”的build议(从上面的问题)是确保正确行为的唯一途径。
如果有人能find这个讨论的规格,我会乐意接受他们的答案。
将控制权交给浏览器的渲染引擎后,testing结果在FF 4b9和Chromium 8.0.552.237中报告为true
。 我修改了这些部分:
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);
更新:是的,我明白这个“答案”更像是一个评论,但只是想指出来。 经过testing,我得到了可重现的结果:
- 没有
setTimeout
,在两个浏览器中,我第一次打开页面时为true
仅在打开F5后才为false
。 明确清除caching后,重新加载后都会再次声明为false
。 - 用
setTimeout
的宽度/高度testing总是performance为true
。
在这两种情况下,只有在重新加载页面后,图像才会第一次显示。