Попередня завантаження зображень за допомогою 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.