Попередня завантаження зображень за допомогою JavaScript


263

Чи є функція, про яку я писав нижче, достатньо для попереднього завантаження зображень у більшості, якщо не у всіх браузерах, які часто використовуються сьогодні?

function preloadImage(url)
{
    var img=new Image();
    img.src=url;
}

У мене є масив URL-адрес зображень, на які я перекидаю цикл і викликаю preloadImageфункцію для кожної URL-адреси.


19
Зауважте, що деякі (усі?) Браузери відпустять зображення через кілька секунд, якщо ви не використовували його. Щоб уникнути цього, зберігайте посилання на imgоб'єкт, наприклад, у масиві в батьківській області.
Тамлін

3
Що ви маєте на увазі під "відпустити зображення"? Якщо він був кешований браузером, він залишиться там, правда?
Франциск

56
Трохи коротше:(new Image()).src = url;
mrzmyr

20
зауважте, це не спрацює, коли відкрито хромований devtool і ввімкнено функцію "відключити чаш" на панелі мережі
Wayou

6
Ще коротше:new Image().src = url;
Даніель Х Мур

Відповіді:


163

Так. Це має працювати у всіх основних браузерах.


48
Примітка модератора: Будь ласка, припиніть позначити це як "не відповідь". Насправді це пряма відповідь на поставлене питання. Якщо ви вважаєте, що відповідь неправильна або недостатньо підкріплена доказами, тоді оскаржуйте її.
Коді Грей

43

Спробуйте це, я думаю, це краще.

var images = [];
function preload() {
    for (var i = 0; i < arguments.length; i++) {
        images[i] = new Image();
        images[i].src = preload.arguments[i];
    }
}

//-- usage --//
preload(
    "http://domain.tld/gallery/image-001.jpg",
    "http://domain.tld/gallery/image-002.jpg",
    "http://domain.tld/gallery/image-003.jpg"
)

Джерело: http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/


3
немає жодного onloadобробника жодного із зображень
Benny

12
Чи можете ви пояснити, чому це краще?
JJJ

2
@BeNice Я думаю, що ви нерозумієте, завантаження зображень є асинхронним, тому вам потрібно обробляти onloadстан або ви просто створюєте зображення в пам'яті.
Бенні

3
Якщо ви використовуєте це рішення, не забудьте оператор var для i-змінної. Інакше він буде встановлений у всьому світі, що може спричинити помилки, які справді важко вирішити (якщо ви не знаєте, що ви забули var заяву.for (var i = 0.....
Mohammer

2
@Mohammer спасибі за це, я просто скопіював код із наданого посилання. Я відредагую код зараз, щоб додатиvar
clintgh

26

У моєму випадку було корисно додати зворотний виклик до вашої функції для onloadподії:

function preloadImage(url, callback)
{
    var img=new Image();
    img.src=url;
    img.onload = callback;
}

А потім оберніть його на випадок масиву URL-адрес для зображень, які попередньо завантажуються із зворотним дзвоном на все: https://jsfiddle.net/4r0Luoy7/

function preloadImages(urls, allImagesLoadedCallback){
    var loadedCounter = 0;
  var toBeLoadedNumber = urls.length;
  urls.forEach(function(url){
    preloadImage(url, function(){
        loadedCounter++;
            console.log('Number of loaded images: ' + loadedCounter);
      if(loadedCounter == toBeLoadedNumber){
        allImagesLoadedCallback();
      }
    });
  });
  function preloadImage(url, anImageLoadedCallback){
      var img = new Image();
      img.onload = anImageLoadedCallback;
      img.src = url;
  }
}

// Let's call it:
preloadImages([
    '//upload.wikimedia.org/wikipedia/commons/d/da/Internet2.jpg',
  '//www.csee.umbc.edu/wp-content/uploads/2011/08/www.jpg'
], function(){
    console.log('All images were loaded');
});

19

CSS2 Альтернатива: http://www.thecssninja.com/css/even-better-image-preloading-with-css2

body:after {
  content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
  display: none; 
}

CSS3 Альтернатива: https://perishablepress.com/preload-images-css3/ (H / T Linh Dam)

.preload-images {
  display: none; 
  width: 0;
  height: 0;
  background: url(img01.jpg),
              url(img02.jpg),
              url(img03.jpg);
}

ПРИМІТКА. Зображення в контейнері з, display:noneможливо, не завантажуються. Можливо видимість: прихований буде працювати краще, але я цього не перевіряв. Дякую Марко Дель Валле за те, що вказав на це


Дякую mplungjan. Хоча це не допомагає мені у цьому конкретному випадку, добре це знати.
Франциск

5
Я правильно кажу, що це призведе до уповільнення завантаження сторінки, оскільки ці зображення потрібно завантажити, перш ніж сторінка може запустити подію завантаження?
jakubiszon

3
Я не вірю, що це буде працювати на всіх браузерах. Я знаю, що зображення на Chrome не завантажуються, поки їх не видно. Потрібно буде зняти дисплей: немає; а натомість спробуйте розмістити їх так, щоб їх не було видно. Потім можна сховатися за допомогою JS після того, як все завантажиться, якщо потрібно.
Буллієн

3
Фонові зображення в елементі з display: noneне завантажуються.
Маркіццо

1
Цей метод працював для мене тільки я (1) не використовував display: none, (2) не використовував width/height: 0, (3) ставив no-repeatі таке положення, щоб зображення було зовні ( -<width>px -<height>pxви отримаєте вас там, тож якщо ваші зображення всі 100 пікселів у висота або менше, ви можете використовувати 0 -100px, іншими словами url(...) no-repeat 0 -100px.
Alexis Вільке

11

Рекомендую скористатися спробувати / catch, щоб запобігти можливим проблемам:

OOP:

    var preloadImage = function (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

Стандарт:

    function preloadImage (url) {
        try {
            var _img = new Image();
            _img.src = url;
        } catch (e) { }
    }

Крім того, хоча я люблю DOM, у старих дурних браузерів можуть виникнути проблеми з використанням DOM, тому уникайте цього взагалі IMHO всупереч внеску Freedev. Image () має кращу підтримку у старих веб-переглядачах.


7
Я не думаю, що саме так ви вловлюєте помилки під час завантаження зображення у javascript - є щось на зразок _img.onerror, яке можна (слід?) Використовувати.
Greg0ry

3
Які "можливі питання"?
Кіссакі

Такі питання, як підтримка команди. Перегляньте сайт "Чи можу я використовувати", щоб побачити, чи потрібний браузер, який потрібно підтримувати, для команди Javascript.
Дейв

10

Цей підхід трохи більш детальний. Тут ви зберігаєте всі попередньо завантажені зображення в контейнер, може бути дівом. І після того, як ви змогли показати зображення або перемістити їх у DOM у потрібне місце.

function preloadImg(containerId, imgUrl, imageId) {
    var i = document.createElement('img'); // or new Image()
    i.id = imageId;
    i.onload = function() {
         var container = document.getElementById(containerId);
         container.appendChild(this);
    };
    i.src = imgUrl;
}

Спробуйте тут , я також додав кілька коментарів


7
const preloadImage = src => 
  new Promise(r => {
    const image = new Image()
    image.onload = r
    image.onerror = r
    image.src = src
  })


// Preload an image
await preloadImage('https://picsum.photos/100/100')

// Preload a bunch of images in parallel 
await Promise.all(images.map(x => preloadImage(x.src)))

5

Так, це спрацює, однак браузери обмежуватимуть (між 4-8) фактичні виклики і, таким чином, не кешувати / попередньо завантажувати всі потрібні зображення.

Кращий спосіб зробити це - зателефонувати до завантаження, перш ніж використовувати зображення таким чином:

function (imageUrls, index) {  
    var img = new Image();

    img.onload = function () {
        console.log('isCached: ' + isCached(imageUrls[index]));
        *DoSomething..*

    img.src = imageUrls[index]
}

function isCached(imgUrl) {
    var img = new Image();
    img.src = imgUrl;
    return img.complete || (img .width + img .height) > 0;
}

Чи можете ви зв’язати будь-яку посилання на цей ліміт веб-переглядачів?
nulll

Важко знайти посилання (принаймні, я його ніколи не знаходив), але я використовував власний проект, щоб перевірити це, уважно переглядаючи, що завантажено з кеш-пам'яті та викликів сервера за допомогою мережевої вкладки Chrome в інструментах для розробників Chrome (f12). Я виявив таку ж поведінку у Firefox та за допомогою мобільного.
Робін

1
-1 тому, що я ретельно перевірив претензію в першому реченні тут і виявив її помилковою, принаймні, в сучасних браузерах. Дивіться stackoverflow.com/a/56012084/1709587. Так, існує обмеження на максимальну кількість паралельних запитів HTTP, який браузер готовий надіслати, але він врешті-решт обходиться запитом кожної URL-адреси, на яку ви викликаєте preloadImage, навіть якщо ви робите це так, як показано в питанні.
Марк

5

Ось мій підхід:

var preloadImages = function (srcs, imgs, callback) {
    var img;
    var remaining = srcs.length;
    for (var i = 0; i < srcs.length; i++) {
        img = new Image;
        img.onload = function () {
            --remaining;
            if (remaining <= 0) {
                callback();
            }
        };
        img.src = srcs[i];
        imgs.push(img);
    }
};

4

Рішення для браузерів, сумісних із ECMAScript 2017

Примітка. Це також спрацює, якщо ви використовуєте транспілятор, як Babel.

'use strict';

function imageLoaded(src, alt = '') {
    return new Promise(function(resolve) {
        const image = document.createElement('img');

        image.setAttribute('alt', alt);
        image.setAttribute('src', src);

        image.addEventListener('load', function() {
            resolve(image);
        });
    });
}

async function runExample() {
    console.log("Fetching my cat's image...");

    const myCat = await imageLoaded('https://placekitten.com/500');

    console.log("My cat's image is ready! Now is the time to load my dog's image...");

    const myDog = await imageLoaded('https://placedog.net/500');

    console.log('Whoa! This is now the time to enable my galery.');

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

runExample();

Ви також могли чекати завантаження всіх зображень.

async function runExample() {
    const [myCat, myDog] = [
        await imageLoaded('https://placekitten.com/500'),
        await imageLoaded('https://placedog.net/500')
    ];

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

Або використовуйте Promise.allдля завантаження їх паралельно.

async function runExample() {
    const [myCat, myDog] = await Promise.all([
        imageLoaded('https://placekitten.com/500'),
        imageLoaded('https://placedog.net/500')
    ]);

    document.body.appendChild(myCat);
    document.body.appendChild(myDog);
}

Більше про Обіцянки .

Детальніше про функції "Асинхронізація" .

Докладніше про завдання руйнування .

Більше про ECMAScript 2015 .

Більше про ECMAScript 2017 .


2

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

  • Chrome 74
  • Сафарі 12
  • Firefox 66
  • Край 17

Щоб перевірити це, я зробив невеликий веб-перелік з кількома кінцевими точками, які кожен спить протягом 10 секунд, перш ніж подати фотографію кошеня. Потім я додав дві веб-сторінки, на одній з яких містився <script>тег, у якому кожен з кошенят попередньо завантажений за допомогою preloadImageфункції із запитання, а інший включає всі кошенята на сторінці за допомогою <img>тегів.

У всіх браузерах, що були вище, я виявив, що якщо я спершу завітав на сторінку попереднього завантаження, зачекав деякий час, а потім перейшов на сторінку з <img>тегами, мої кошенята видалися миттєво. Це свідчить про те, що попередній завантажувач успішно завантажив кошенят у кеш у всіх перевірених браузерах.

Ви можете переглянути або випробувати додаток, який я використовував для тестування цього, на https://github.com/ExplodingCabbage/preloadImage-test .

Зокрема, зауважте, що ця методика працює у браузерах вище, навіть якщо кількість зображень, що перебувають у циклі, перевищує кількість паралельних запитів, які браузер готовий зробити за раз, всупереч тому, що пропонує відповідь Робіна . Швидкість, з якою попереднє завантаження ваших зображень, звичайно, буде обмежена тим, скільки паралельних запитів готовий надіслати веб-переглядач, але в кінцевому підсумку він запитає кожну URL-адресу зображення, на яку ви телефонуєте preloadImage().


Чи має значення, чи зберігаються зображення в кеш-пам'яті чи диску? здається, що метод OP кешується на диску на відміну від методу @clintgh, який отримує кешування в пам'яті.
Кріс Л

1

Ви можете перемістити цей код до index.html для попереднього завантаження зображень з будь-якого URL-адреси

<link rel="preload" href="https://via.placeholder.com/160" as="image">

0

Найкраще браузер працюватиме, використовуючи тег посилання в голові.

export function preloadImages (imageSources: string[]): void {
  imageSources
    .forEach(i => {
      const linkEl = document.createElement('link');
      linkEl.setAttribute('rel', 'preload');
      linkEl.setAttribute('href', i);
      linkEl.setAttribute('as', 'image');
      document.head.appendChild(linkEl);
    });
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.