Чи слід встановлювати зображення 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 є допустимою, а не помилкою, то насправді життєво важливо захищати її.
Фрогц

@Pekka Дивіться мою "відповідь" нижче; IE9 вимагає встановити завантаження перед налаштуванням src, але жоден інший браузер цього не робить. (!) Дійсно.
Фрогц

@phrogz Привіт, Phrogz, ця функціональність дуже важлива для мене; однак у браузері Android (остання версія) присвоєння dataURL джерелу зображення просто не може працювати належним чином навіть за допомогою методу "Відтворити безпечно". Не могли б ви дати мені більше пропозицій?
Alston,

Відповіді:


53

Оскільки жоден досі не знайдено ніяких специфікацій про те , як це передбачається вести себе, ми повинні бути задоволені тим , як вона робить себе вести. Ось результати моїх тестів.

            | - чи можна використовувати дані зображень право | робить зворотний виклик onload, встановлений після src
            | після встановлення src? | змінено все ще викликати?
------------ + ----------------------------- + ------- ------------------------------
Safari 5 | так | так                
------------ + ----------------------------- + ------- ------------------------------
Chrome 8 | ** ні ** (завантаження 1-ї сторінки), але | так                
FireFox 3.6 | так після цього (кешоване) |                                     
------------ + ----------------------------- + ------- ------------------------------
IE8 | так (ліміт даних 32 кБ) | так         
------------ + ----------------------------- + ------- ------------------------------
IE9b | так | **ні**              
------------ + ----------------------------- + ------- ------------------------------

Підсумовуючи:

  • Ви не можете припустити, що дані зображення будуть доступні відразу після встановлення uri даних; ви повинні почекати onloadподії.
  • Через IE9 ви не можете встановити onloadобробник після встановлення srcі очікувати його виклику.
  • Рекомендації в розділі "Надійно захищати" (із запитання вище) - це єдиний спосіб забезпечити правильну поведінку.

Якщо хтось зможе знайти специфікації, які обговорюють це, я із задоволенням прийму їх відповідь.


4
Дякую за дослідження, яке ви тут провели. Я боровся з можливістю примусити таку поведінку для офлайн-веб-додатків, де мені потрібно повідомити, використовуючи полотно, тому я розробив трохи більше. Жива специфікація WHATWG насправді називає кешований регістр синхронним, а некешований регістр - асинхронним. Ознайомтесь із кроком 7 у розділі src специфікації, що знаходиться тут: whatwg.org/specs/web-apps/current-work/multipage/… . Здається, якщо зображення є в "списку доступних зображень" Документа, воно заповнюється синхронно, або саме так я його читаю.
J Trana

@Phrogz На якому етапі чи в документі ви проводили цей тест? Я спостерігаю Safari 5.1, щоб мати поведінку Chrome, але це до window.loadподії. Ваші тести проводились після навантаження чи раніше? Ти пам'ятаєш?
hexalys

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тесті ширини / висоти завжди виявляється true.

В обох випадках зображення не відображається вперше, лише після перезавантаження сторінки.


Ви хочете сказати мені, що без цих змін ви бачите "збої" у FF та Chromium? Моє питання не в тому, як змусити цю роботу - чітко встановити onloadдо srcі лише посилання на зображення у зворотному виклику. Моє питання полягає в тому, чи існують специфікації, що описують, як це має працювати.
Phrogz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.