web-dev-qa-db-fra.com

La définition d'un src d'image sur une URL de données devrait-elle être disponible immédiatement?

Considérez le code JavaScript (fragile) suivant:

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!'); };

La (les) question (s)

  • La largeur et la hauteur de l'image doivent-elles être correctes immédiatement?
  • La toile devrait-elle fonctionner immédiatement?
  • Le rappel onload (défini après la modification du src) doit-il être invoqué?

Tests expérimentaux
J'ai créé un page de test simple avec un code similaire. Dans Safari, mes premiers tests, à la fois en ouvrant la page HTML localement (URL file:///) Et en la chargeant depuis mon serveur, ont montré que tout fonctionnait: la hauteur et la largeur sont correctes, le dessin du canevas fonctionne, et le onload se déclenche également.

Dans Firefox v3.6 (OS X), le chargement de la page après le lancement du navigateur montre que la hauteur/largeur n'est pas correcte immédiatement après le réglage, drawImage() échoue. (Le gestionnaire onload se déclenche cependant.) Le chargement de la page, cependant, montre que la largeur/hauteur est correcte immédiatement après le réglage et que drawImage() fonctionne. Il semblerait que Firefox met en cache le contenu de l'URL de données sous forme d'image et qu'il soit disponible immédiatement lorsqu'il est utilisé dans la même session.

Dans Chrome v8 (OS X), je vois les mêmes résultats que dans Firefox: l'image n'est pas disponible immédiatement, mais prend un certain temps pour se charger de manière asynchrone à partir de l'URL de données.

En plus de la preuve expérimentale de ce que les navigateurs ci-dessus fonctionnent ou ne fonctionnent pas, j'aimerais vraiment des liens vers des spécifications sur la façon dont cela doit se comporter. Jusqu'à présent, mon Google-fu n'a pas été à la hauteur.

Jouer en toute sécurité
Pour ceux qui ne comprennent pas pourquoi ce qui précède pourrait être dangereux, sachez que vous devez utiliser des images comme celle-ci pour être en sécurité:

// 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

Comme personne n'a encore trouvé de spécifications sur la façon dont cela ( est supposé se comporter, nous devrons être satisfaits de la façon dont cela se comporte . Voici les résultats de mes tests.

 | les données d'image sont-elles utilisables à droite | effectue le rappel onload défini après src 
 | après avoir défini src? | est changé toujours invoqué? 
 ------------ + -------------------------- --- + ------------------------------------- 
 Safari 5 | oui | oui 
 ------------ + ----------------------------- + - ------------------------------------ 
 Chrome 8 | ** non ** (chargement de la 1ère page), mais | oui 
 FireFox 3.6 | oui par la suite (mis en cache) | 
 ------------ + ----------------------------- + - ----------------------------------- 
 IE8 | oui (limite de données de 32 Ko) | oui 
 ------------ + ----------------------------- + - ------------------------------------ 
 IE9b | oui | ** non ** 
 ------------ + --------------------------- - + ------------------------------------- 

En résumé:

  • Vous ne pouvez pas supposer que les données d'image seront disponibles juste après avoir défini un uri de données; vous devez attendre l'événement onload.
  • En raison d'IE9, vous ne pouvez pas définir le gestionnaire onload après avoir défini le src et vous attendre à ce qu'il soit appelé.
  • Les recommandations de "Jouer prudemment" (de la question ci-dessus) sont le seul moyen de garantir un comportement correct.

Si quelqu'un peut trouver des spécifications en discutant, j'accepterai volontiers sa réponse à la place.

52
Phrogz

Après avoir redonné le contrôle au moteur de rendu du navigateur, le test rapporte true dans FF 4b9 et Chromium 8.0.552.237. J'ai modifié ces parties:

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

pdate: Oui, je comprends que cette "réponse" ressemble plus à un commentaire, mais je voulais juste le souligner. Et après les tests, j'obtiens des résultats reproductibles:

  • sans setTimeout, dans les deux navigateurs j'obtiens false la première fois que j'ouvre la page et true seulement après avoir appuyé F5. Après avoir effacé explicitement le cache, les deux disent à nouveau false après le rechargement.
  • avec setTimeout le test largeur/hauteur s'évalue toujours à true.

Dans les deux cas, l'image n'apparaît pas la première fois, uniquement après le rechargement de la page.

6
Marcel Korpel