Предварительная загрузка изображений с помощью 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
обратите внимание, что это не будет работать, когда Chrome Devtool открыт и «отключить Chache» включен на панели сети
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/

clintgh
источник
3
Там нет onloadобработчика для любого из изображений
Бенни
12
Не могли бы вы объяснить, почему это лучше?
JJJ
2
@BeNice Я думаю, что вы не понимаете, загрузка изображений асинхронна, поэтому вы должны обрабатывать onloadсостояние, или вы просто создаете изображения в памяти.
Бенни
3
Если вы используете это решение, не забудьте оператор var для i-переменной. В противном случае он будет установлен глобально, что может вызвать ошибку, которую действительно трудно решить (если только вы не знаете, что забыли оператор var.for (var i = 0.....
Мохаммер
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могут не загружаться предварительно. Возможно видимость: скрытый будет работать лучше, но я не проверял это. Спасибо Marco Del Valle за указание на это

mplungjan
источник
Спасибо, mplungjan. Хотя это не помогает мне в этом конкретном случае, это полезно знать.
Франциск
5
Правильно ли я говорю, что это замедлит загрузку страницы, потому что эти изображения должны быть загружены до того, как страница сможет запустить событие загрузки?
Якубисон
3
Я не верю, что они будут работать на всех браузерах. Я знаю, что на Chrome изображения не загружаются, пока они не видны. Нужно будет снять дисплей: нет; и вместо этого попытайтесь расположить их так, чтобы их не было видно. Затем можно спрятать с JS после того, как все загрузится, если это необходимо.
Bullyen
3
Фоновые изображения в элементе с display: noneне будут предварительно загружаться.
Маркиззо
1
Этот метод работал у меня только я (1) не использовал display: none , (2) не использовал width/height: 0, (3) пут no-repeatи положение, такое, что изображение будет снаружи ( -<width>px -<height>pxприведет вас туда, так что если ваши изображения все 100px в высота или меньше, вы можете использовать 0 -100px, другими словами url(...) no-repeat 0 -100px.
Alexis Вильке
11

Я рекомендую вам использовать try / catch, чтобы предотвратить некоторые возможные проблемы:

ООП:

    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, поэтому избегайте этого вообще ИМХО вопреки вкладу freedev. Image () имеет лучшую поддержку в старых мусорных браузерах.

Дейв
источник
7
Я не думаю, что именно так вы ловите ошибки при загрузке Image в javascript - есть что-то вроде _img.onerror, которое можно (нужно?) Использовать.
Грегори
3
Каковы "возможные проблемы"?
Киссаки
Такие вопросы, как поддержка команды. Зайдите на сайт «Могу ли я использовать», чтобы узнать, поддерживает ли браузер, который вы должны поддерживать, команду javascript.
Дэйв
10

Этот подход немного сложнее. Здесь вы храните все предварительно загруженные изображения в контейнере, может быть div. И после вы можете показать изображения или переместить его в 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;
}

Попробуйте здесь , я также добавил несколько комментариев

freedev
источник
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)))
david_adler
источник
5

Да, это будет работать, однако браузеры будут ограничивать (между 4-8) фактические вызовы и, таким образом, не будут кэшировать / предварительно загружать все нужные изображения.

Лучший способ сделать это - вызвать onload перед использованием изображения следующим образом:

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);
    }
};
naeluh
источник
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);
}

Подробнее об обещаниях .

Подробнее о функциях "Async" .

Подробнее о деструктурирующем задании .

Подробнее о ECMAScript 2015 .

Подробнее о ECMAScript 2017 .

Амин НАИРИ
источник
2

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

  • Chrome 74
  • Safari 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">
fominmax
источник
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);
    });
}
Брендан Фаркухарсон
источник