Precaricamento delle immagini con JavaScript


263

La funzione che ho scritto di seguito è sufficiente per precaricare le immagini nella maggior parte, se non in tutti, i browser comunemente usati oggi?

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

Ho una serie di URL di immagini che ricopro e chiamo la preloadImagefunzione per ciascun URL.


19
Nota che alcuni browser (tutti?) Rilasceranno l'immagine dopo alcuni secondi se non l'hai utilizzata. Per evitare ciò, mantenere un riferimento imgall'oggetto, ad esempio in un array nell'ambito genitore.
Tamlyn,

3
Cosa intendi con "rilascia l'immagine"? Se è stato memorizzato nella cache dal browser, rimarrà lì, giusto?
Francisc,

56
Un po 'più breve:(new Image()).src = url;
mrzmyr,

20
nota che questo non funzionerà quando Chrome Devtool è aperto e "Disabilita chache" è abilitato nel pannello di rete
Wayou,

6
Ancora più breve:new Image().src = url;
Daniel X Moore il

Risposte:


163

Sì. Questo dovrebbe funzionare su tutti i principali browser.


48
Nota del moderatore: interrompi la segnalazione come "non una risposta". È, infatti, una risposta diretta alla domanda che è stata posta. Se ritieni che la risposta sia errata o non sufficientemente supportata da prove, allora riduci il voto .
Cody Gray

43

Prova questo, penso che sia meglio.

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"
)

Fonte: http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/


3
non c'è nessun onloadgestore per nessuna delle immagini
Benny,

12
Potresti spiegare perché questo è meglio?
JJJ,

2
@BeNice Penso che tu sia malinteso, il caricamento delle immagini è asincrono, quindi devi gestire lo onloadstato o stai solo istanziando le immagini in memoria.
Benny

3
Se usi questa soluzione, non dimenticare l'istruzione var per la i-variabile. Altrimenti sarà impostato a livello globale che può causare errori che sono davvero difficili da risolvere (a meno che tu non sappia che hai dimenticato la dichiarazione var.for (var i = 0.....
Mohammer

2
@Mohammer grazie per questo, ho appena copiato il codice dal link che ho fornito. var
Modificherò

26

Nel mio caso è stato utile aggiungere un callback alla funzione per l' onloadevento:

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

E poi lo avvolge per il caso di una serie di URL per le immagini da precaricare con callback su tutto: 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

Alternativa 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; 
}

Alternativa 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);
}

NOTA: le immagini in un contenitore con display:nonepotrebbero non essere precaricate. Forse visibilità: nascosto funzionerà meglio ma non l'ho provato. Grazie Marco Del Valle per averlo segnalato


Grazie mplungjan. Anche se non mi aiuta in questo caso particolare, è bene saperlo.
Francisc,

5
Sto giustamente dicendo che questo rallenterà il caricamento della pagina perché queste immagini devono essere scaricate prima che la pagina possa avviare l'evento load?
Jakubiszon,

3
Non credo che funzioneranno su tutti i browser. So che su Chrome le immagini non vengono caricate fino a quando non sono visibili. Sarà necessario rimuovere il display: nessuno; e invece cerca di posizionarli in modo che non possano essere visti. Può quindi nascondersi con JS dopo che tutto è stato caricato, se necessario.
Bullyen,

3
Le immagini di sfondo in un elemento con display: nonenon verranno precaricate.
Marquizzo,

1
Questo metodo ha funzionato per me solo io (1) non ho usato display: none, (2) non ho usato il width/height: 0, (3) put no-repeate una posizione tale che l'immagine sarebbe esterna ( -<width>px -<height>pxti porterò lì, quindi se le tue immagini sono tutte 100px in altezza o meno, puoi usare 0 -100px, in altre parole url(...) no-repeat 0 -100px.
Alexis Wilke

11

Ti consiglio di usare un tentativo / cattura per prevenire alcuni possibili problemi:

OOP:

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

Standard:

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

Inoltre, anche se adoro DOM, i vecchi stupidi browser potrebbero avere problemi con l'utilizzo di DOM, quindi evitalo del tutto IMHO contrariamente al contributo di freedev. Image () ha un supporto migliore nei vecchi browser cestino.


7
Non penso che sia così che si rilevano errori quando si carica Image in javascript: esiste qualcosa come _img.onerror che può (dovrebbe?) Essere usato.
Greg0ry,

3
Quali sono i "possibili problemi"?
Kissaki,

Problemi come il supporto per il comando. Dai un'occhiata al sito "posso usare" per vedere se un browser che devi supportare ha il supporto per un comando javascript.
Dave,

10

Questo approccio è un po 'più elaborato. Qui memorizzi tutte le immagini precaricate in un contenitore, può essere un div. E dopo potresti mostrare le immagini o spostarle all'interno del DOM nella posizione corretta.

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;
}

Provalo qui , ho anche aggiunto alcuni commenti


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

Sì, questo funzionerà, tuttavia i browser limiteranno (tra 4-8) le chiamate effettive e quindi non memorizzeranno nella cache / precaricano tutte le immagini desiderate.

Un modo migliore per farlo è chiamare onload prima di usare l'immagine in questo modo:

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;
}

Potete per favore collegare alcuni riferimenti su questo limite del browser?
nulll,

È difficile trovare un riferimento (almeno non ne ho mai trovato uno), ma ho usato il mio progetto per testarlo osservando attentamente ciò che veniva caricato dalla cache e dalle chiamate del server utilizzando la scheda di rete di Chrome negli strumenti di sviluppo di Chrome (f12). Ho trovato lo stesso comportamento in Firefox e usando il cellulare.
Robin,

1
-1 perché ho testato attentamente l'affermazione nella prima frase qui e l'ho trovata falsa, almeno nei browser moderni. Vedi stackoverflow.com/a/56012084/1709587. Sì, esiste un limite al numero massimo di richieste HTTP parallele che il browser è disposto a inviare, ma alla fine si arriva a richiedere tutti gli URL su cui si chiama precaricamento immagine, anche se lo si fa nel modo indicato nella domanda.
Mark Amery,

5

Ecco il mio approccio:

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

Soluzione per browser compatibili con ECMAScript 2017

Nota: questo funzionerà anche se stai usando un transpiler come 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();

Potresti anche aver atteso il caricamento di tutte le immagini.

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);
}

Oppure utilizzare Promise.allper caricarli in parallelo.

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);
}

Maggiori informazioni sulle promesse .

Ulteriori informazioni sulle funzioni "Asincrono" .

Maggiori informazioni sull'incarico di destrutturazione .

Maggiori informazioni su ECMAScript 2015 .

Maggiori informazioni su ECMAScript 2017 .


2

Posso confermare che l'approccio nella domanda è sufficiente per attivare le immagini da scaricare e memorizzare nella cache (a meno che tu non abbia proibito al browser di farlo tramite le intestazioni di risposta) almeno in:

  • Chrome 74
  • Safari 12
  • Firefox 66
  • Bordo 17

Per provare questo, ho realizzato una piccola webapp con diversi endpoint che dormono ciascuno per 10 secondi prima di servire l'immagine di un gattino. Quindi ho aggiunto due pagine Web, una delle quali conteneva un <script>tag in cui ciascuno dei cuccioli è precaricato usando la preloadImagefunzione della domanda, e l' altro dei quali include tutti i cuccioli sulla pagina usando i <img>tag.

In tutti i browser sopra, ho scoperto che se ho visitato prima la pagina del precaricatore, ho aspettato un po 'e poi sono andato alla pagina con i <img>tag, i miei cuccioli sono stati resi immediatamente. Ciò dimostra che il preloader ha caricato correttamente i gattini nella cache in tutti i browser testati.

Puoi vedere o provare l'applicazione che ho usato per testarlo su https://github.com/ExplodingCabbage/preloadImage-test .

Si noti in particolare che questa tecnica funziona nei browser precedenti anche se il numero di immagini in loop supera il numero di richieste parallele che il browser è disposto a fare alla volta, contrariamente a quanto suggerisce la risposta di Robin . La velocità con cui il vostro immagini precarico sarà ovviamente limitata da quanti parallelo richiede il browser è disposti a mandare, ma sarà eventualmente richiedere ogni URL immagine si chiama preloadImage()via.


Importa se le immagini sono memorizzate nella cache o nella memoria? sembra che il metodo OP sia memorizzato nella cache sul disco invece del metodo @clintgh che viene memorizzato nella cache.
Chris L

1

Puoi spostare questo codice in index.html per le immagini di precaricamento da qualsiasi URL

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

0

Il browser funzionerà meglio usando il tag link nella testa.

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);
    });
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.