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


75

考虑以下(脆弱的)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 = '...';

1
设置onload之前,您肯定需要进行设置src。但是,如果有时在当前星座图中触发加载事件,那么设置“数据” URI确实是异步的-这会让我感到惊讶。有趣!
Pekka

假设否,在浏览器有机会重绘自身后再次超时并继续运行,是否更安全?
mP。

@mP是的,的确是这样;我编辑了我的问题,以便在您发表评论前5分钟就可以将其清除:)但是,这不会使问题无效。假设Chrome和FF中的行为是允许的(不是错误),那么实际上,安全运行至关重要。
Phrogz

@Pekka请参阅下面的“答案”;IE9要求在设置src之前先设置onload,但其他浏览器都不需要。(!)确实令人惊讶。
Phrogz

@phrogz您好,Phrogz,此功能对我来说非常重要;但是,在Android浏览器(现在是最新版本)上,即使通过“安全播放”方法将dataURL分配给图像源也无法正常工作。你能给我更多建议吗?
Alston 2012年

Answers:


53

由于没有一个目前尚未发现这是怎么任何规格应该的行为,我们将不得不与它是如何满足循规蹈矩。以下是我的测试结果。

            | 图像数据可用吗?在src之后设置onload回调
            | 设置src后?| 被改变还是被调用?
------------ + ----------------------------- ++ ------ ------------------------------
Safari 5 | 是的 是                
------------ + ----------------------------- ++ ------ ------------------------------
Chrome 8 | **否**(第一页加载),但是| 是                
FireFox 3.6 | 之后是(缓存)                                     
------------ + ----------------------------- ++ ------ ------------------------------
IE8 | 是(32kB数据限制)| 是         
------------ + ----------------------------- ++ ------ ------------------------------
IE9b | 是的 **没有**              
------------ + ----------------------------- ++ ------ ------------------------------

综上所述:

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

如果有人可以找到讨论此问题的规范,我会很乐意接受他们的回答。


4
感谢您在这里所做的研究。我一直在为可能需要使用画布进行报告的脱机webapp强制执行此行为而苦苦挣扎,因此我进行了更多研究。现行的WHATWG规范实际上确实将缓存的情况称为同步,将未缓存的情况称为异步。在此处找到的规范的src部分中查看第7步:whatwg.org/specs/web-apps/current-work/multipage/…。如果图像在文档的“可用图像列表”中,则显示它是同步完成的,或者这就是我阅读的方式。
J Trana

@Phrogz您在哪个阶段或文档状态下进行了该测试?我观察到Safari 5.1具有Chrome行为,但是那是在window.load活动开始之前。您的测试是在加载之后还是之前进行的?你还记得吗?
hexalys 2015年

7

将控制权交还给浏览器的呈现引擎后,该测试true将以FF 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宽度/高度测试总是evualuates到true

在这两种情况下,仅在重新加载页面后图像才第一次显示。


您是在告诉我,如果不进行这些更改,您将在FF和Chromium中看到“故障”?我的问题不是如何使它起作用—在回调onload之前明确设置src并且仅引用该图像将起作用。我的问题是是否存在描述该如何工作的规范。
Phrogz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.