Должна ли настройка изображения src для URL данных быть доступна немедленно? - PullRequest
71 голосов
/ 24 января 2011

Рассмотрим следующий (хрупкий) код 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-фу не справился с этой задачей.

Безопасная игра
Для тех, кто не понимает, почему вышеизложенное может быть опасно, знайте, что вы должны использовать подобные изображения, чтобы быть в безопасности:

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

Ответы [ 2 ]

52 голосов
/ 26 января 2011

Поскольку никто еще не нашел никаких спецификаций о том, как должен вести себя, мы должны быть удовлетворены тем, как он ведет себя . Ниже приведены результаты моих тестов.

            | is image data usable right  | does onload callback set after src
            |     after setting src?      |   is changed still get invoked?
------------+-----------------------------+-------------------------------------
Safari  5   |             yes             |                  yes                
------------+-----------------------------+-------------------------------------
Chrome  8   | **no** (1st page load), but |                  yes                
FireFox 3.6 |  yes thereafter (cached)    |                                     
------------+-----------------------------+-------------------------------------
IE8         |    yes (32kB data limit)    |                  yes         
------------+-----------------------------+-------------------------------------
IE9b        |             yes             |                 **no**              
------------+-----------------------------+-------------------------------------

В итоге:

  • Вы не можете предполагать, что данные изображения будут доступны сразу после установки data-uri; вы должны дождаться события onload.
  • Из-за IE9 вы не можете установить обработчик onload после установки src и ожидать его запуска.
  • Рекомендации в «Безопасная игра» (из приведенного выше вопроса) - единственный способ обеспечить правильное поведение.

Если кто-то сможет найти спецификации, обсуждающие это, я с радостью приму их ответ.

6 голосов
/ 24 января 2011

После возврата управления движку рендеринга браузера отчет об испытаниях 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 тест ширины / высоты всегда оценивается в true.

В обоих случаях изображениене появляется в первый раз, только после перезагрузки страницы.

...