Должна ли настройка изображения 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()
выходит из строя. (The onload
однако, обработчик срабатывает.) Загрузка страницы снова, однако, показывает правильность ширины / высоты сразу после установки, и drawImage()
за работой. Похоже, что Firefox кэширует содержимое URL-адреса данных в виде изображения и сразу же получает его при использовании в том же сеансе.
В Chrome v8 (OS X) я вижу те же результаты, что и в Firefox: изображение не доступно сразу, но требуется некоторое время для асинхронной "загрузки" из URL-адреса данных.
В дополнение к экспериментальному доказательству того, что браузеры вышеупомянутые работают или не работают, я действительно хотел бы ссылки на спецификации о том, как это должно себя вести. Пока что мой гугл-фу не справился с этой задачей.
Играя Безопасно
Для тех, кто не понимает, почему вышеупомянутое может быть опасным, знайте, что вы должны использовать изображения, подобные этим, чтобы быть в безопасности:
// 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 ответа
Поскольку никто еще не нашел никаких спецификаций о том, как это должно себя вести, мы должны быть удовлетворены тем, как оно себя ведет. Ниже приведены результаты моих тестов.
| данные изображения можно использовать правильно | выполняет обратный вызов onload после src | после настройки src? | изменено все еще вызывается? ------------+-----------------------------+------------------------------------- Safari 5 | да | да ------------+-----------------------------+------------------------------------- Chrome 8 | ** нет ** (загрузка 1-й страницы), но | да FireFox 3.6 | да после этого (кэшируется) | ------------+-----------------------------+------------------------------------- IE8 | да (ограничение данных 32 КБ) | да ------------+-----------------------------+------------------------------------- IE9b | да | нет ** нет ** ------------+-----------------------------+-------------------------------------
В итоге:
- Вы не можете предполагать, что данные изображения будут доступны сразу после установки data-uri; ты должен ждать
onload
событие. - Из-за IE9 вы не можете установить
onload
обработчик после установкиsrc
и ожидать, что это будет вызвано. - Рекомендации в "Безопасной игре" (из приведенного выше вопроса) являются единственным способом обеспечения правильного поведения.
Если кто-то сможет найти спецификации, обсуждающие это, я с радостью приму их ответ.
После возврата управления движку рендеринга браузера отчеты об испытаниях true
в FF 4b9 и хром 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
,
В обоих случаях изображение не появляется в первый раз, только после перезагрузки страницы.