web-dev-qa-db-ja.com

画像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から非同期に「ロード」するには時間がかかります。

上記のブラウザが動作するか動作しないかの実験的証拠に加えて、これがどのように動作するかについての仕様へのリンクが大好きです。これまでのところ、私の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 = '...';
_
71
Phrogz

これがどのように想定されるかについての仕様を誰もまだ見つけていないので、どのようにdoesに満足する必要があります振る舞います。以下は私のテストの結果です。

 |画像データは使用可能です| src 
の後に設定されたonloadコールバックを行います| srcを設定した後? |変更はまだ呼び出されますか?
 ------------ + -------------------------- --- + ------------------------------------- 
 Safari 5 |はい|はい
 ------------ + ----------------------------- +- ------------------------------------ 
 Chrome 8 | **いいえ**(最初のページの読み込み)、しかし|はい
 FireFox 3.6 |はい(キャッシュ済み)| 
 ------------ + ----------------------------- +- ----------------------------------- 
 IE8 |はい(32kBデータ制限)|はい
 ------------ + ----------------------------- +- ------------------------------------ 
 IE9b |はい| **いいえ** 
 ------------ + --------------------------- -+ ------------------------------------- 

要約すれば:

  • Data-uriを設定した直後に画像データが利用可能になると想定することはできません。 onloadイベントを待つ必要があります。
  • IE9により、onloadを設定した後にsrcハンドラーを設定できず、呼び出されることを期待できません。
  • "Playing it Safe"(上記の質問から)の推奨事項は、正しい動作を保証する唯一の方法です。

誰かがこれについて議論している仕様を見つけることができるならば、私は彼らの答えを代わりに喜んで受け入れます。

52
Phrogz

ブラウザのレンダリングエンジンに制御を戻した後、テストは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);

更新:はい、この「回答」はコメントのようなものですが、指摘したいだけです。そして、テスト後、再現可能な結果が得られます。

  • setTimeoutなしで、両方のブラウザで最初にページを開いたときにfalseを、ヒットした後にのみtrueを取得します F5。キャッシュを明示的にクリアした後、両方ともリロード後に再度falseと言います。
  • setTimeoutを使用すると、幅/高さのテストは常にtrueになります。

どちらの場合も、ページをリロードした後に初めて画像が表示されます。

6
Marcel Korpel