Vorladen von Bildern mit JavaScript

263

Reicht die unten beschriebene Funktion aus, um Bilder in den meisten, wenn nicht allen heute gebräuchlichen Browsern vorzuladen?

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

Ich habe eine Reihe von Bild-URLs, die ich durchlaufe und die preloadImageFunktion für jede URL aufrufe.

Francisc
quelle
19
Beachten Sie, dass einige (alle?) Browser das Bild nach einigen Sekunden freigeben, wenn Sie es nicht verwendet haben. Um dies zu vermeiden, behalten Sie einen Verweis auf das imgObjekt bei, z. B. in einem Array im übergeordneten Bereich.
Tamlyn
3
Was meinst du mit "Bild freigeben"? Wenn es vom Browser zwischengespeichert wurde, bleibt es dort, oder?
Francisc
56
Etwas kürzer:(new Image()).src = url;
Mrzmyr
20
Beachten Sie, dass dies nicht funktioniert, wenn Chrome Devtool geöffnet ist und "Chache deaktivieren" im Netzwerkfenster aktiviert ist
Wayou
6
Noch kürzer:new Image().src = url;
Daniel X Moore

Antworten:

162

Ja. Dies sollte in allen gängigen Browsern funktionieren.

Huzi --- Javiator
quelle
48
Moderator Hinweis: Bitte hören Sie auf, dies als "keine Antwort" zu kennzeichnen. Es ist in der Tat eine direkte Antwort auf die gestellte Frage. Wenn Sie der Meinung sind, dass die Antwort falsch ist oder nicht ausreichend mit Beweisen belegt ist, stimmen Sie sie ab.
Cody Gray
43

Versuchen Sie das, ich denke, das ist besser.

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

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

Clintgh
quelle
3
Es gibt keinen onloadHandler für eines der Bilder
Benny
12
Können Sie erklären, warum dies besser ist?
JJJ
2
@BeNice Ich denke, Sie haben ein Missverständnis, das Laden von Bildern ist asynchron, daher müssen Sie mit dem onloadStatus umgehen oder Sie instanziieren nur Bilder im Speicher.
Benny
3
Wenn Sie diese Lösung verwenden, vergessen Sie nicht die var-Anweisung für die i-Variable. Andernfalls wird es global festgelegt, was zu Fehlern führen kann, die wirklich schwer zu lösen sind (es sei denn, Sie wissen, dass Sie die var-Anweisung vergessen haben.for (var i = 0.....
Mohammer,
2
@ Mohammer danke dafür, ich habe gerade den Code von dem Link kopiert, den ich bereitgestellt habe. Ich werde den Code jetzt bearbeiten, um dasvar
clintgh hinzuzufügen
26

In meinem Fall war es nützlich, Ihrer Funktion einen Rückruf für das onloadEreignis hinzuzufügen :

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

Und dann wickeln Sie es für den Fall ein Array von URLs zu Bildern ein, die mit Rückruf auf alle vorinstalliert werden sollen: 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');
});
Alexander
quelle
19

CSS2-Alternative: 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-Alternative: 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);
}

HINWEIS: Bilder in einem Container mit werden display:nonemöglicherweise nicht vorgeladen. Vielleicht Sichtbarkeit: versteckt funktioniert besser, aber ich habe dies nicht getestet. Vielen Dank an Marco Del Valle für diesen Hinweis

mplungjan
quelle
Danke mplungjan. Obwohl es mir in diesem speziellen Fall nicht hilft, ist es gut zu wissen.
Francisc
5
Habe ich Recht, wenn ich sage, dass dies das Laden der Seite verlangsamt, da diese Bilder heruntergeladen werden müssen, bevor die Seite das Ladeereignis starten kann?
Jakubiszon
3
Ich glaube nicht, dass dies auf allen Browsern funktioniert. Ich weiß, dass auf Chrome Bilder erst geladen werden, wenn sie sichtbar sind. Muss die Anzeige entfernen: keine; und versuchen Sie stattdessen, sie so zu positionieren, dass sie nicht gesehen werden können. Kann sich dann bei JS verstecken, nachdem bei Bedarf alles geladen wurde.
Bullyen
3
Hintergrundbilder in einem Element mit display: nonewerden nicht vorgeladen.
Marquizzo
1
Diese Methode hat nur für mich funktioniert. Ich habe (1) nicht verwendet display: none, (2) habe nicht verwendet width/height: 0, (3) gesetzt no-repeatund eine Position, bei der sich das Bild außerhalb befindet ( -<width>px -<height>pxbringt Sie dorthin, wenn Ihre Bilder alle 100 Pixel groß sind) Höhe oder weniger können Sie mit 0 -100pxanderen Worten verwenden url(...) no-repeat 0 -100px.
Alexis Wilke
11

Ich empfehle Ihnen, try / catch zu verwenden, um mögliche Probleme zu vermeiden:

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

Auch wenn ich DOM liebe, können alte dumme Browser Probleme mit der Verwendung von DOM haben. Vermeiden Sie es daher IMHO im Gegensatz zu freedevs Beitrag. Image () unterstützt in alten Papierkorbbrowsern besser.

Dave
quelle
7
Ich glaube nicht, dass Sie auf diese Weise Fehler beim Laden von Image in Javascript feststellen - es gibt so etwas wie _img.onerror, das verwendet werden kann (sollte?).
Greg0ry
3
Was sind "mögliche Probleme"?
Kissaki
Probleme wie die Unterstützung des Befehls. Überprüfen Sie auf der Website "Kann ich verwenden", ob ein von Ihnen unterstützter Browser einen Javascript-Befehl unterstützt.
Dave
10

Dieser Ansatz ist etwas ausgefeilter. Hier speichern Sie alle vorinstallierten Bilder in einem Container, möglicherweise ein div. Und nachdem Sie die Bilder anzeigen oder innerhalb des DOM an die richtige Position verschieben konnten.

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

Probieren Sie es hier aus , ich habe auch einige Kommentare hinzugefügt

freedev
quelle
6
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
quelle
5

Ja dies funktionieren wird, jedoch wird Browser begrenzen (zwischen 4-8) die tatsächlichen Anrufe und somit nicht cache / Vorbelastung alle gewünschten Bilder.

Eine bessere Möglichkeit, dies zu tun, besteht darin, onload aufzurufen, bevor Sie das Bild wie folgt verwenden:

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;
}
Robin
quelle
Können Sie bitte einen Verweis auf dieses Browser-Limit verlinken?
nulll
Es ist schwierig, eine Referenz zu finden (zumindest habe ich nie eine gefunden), aber ich habe mein eigenes Projekt verwendet, um dies zu testen, während ich mir genau angesehen habe, was aus Cache- und Serveraufrufen mithilfe der Registerkarte "Netzwerk" von Chrome in den Chrome-Entwicklertools geladen wurde (f12). Ich habe das gleiche Verhalten in Firefox und bei der Verwendung von Mobilgeräten festgestellt.
Robin
1
-1, weil ich die Behauptung im ersten Satz hier sorgfältig getestet und festgestellt habe, dass sie zumindest in modernen Browsern falsch ist. Siehe stackoverflow.com/a/56012084/1709587. Ja, die maximale Anzahl paralleler HTTP-Anforderungen, die der Browser senden möchte, ist begrenzt. Schließlich wird jedoch jede URL angefordert, unter der Sie pretoadImage aufrufen, selbst wenn Sie dies wie in der Frage gezeigt tun.
Mark Amery
5

Hier ist mein Ansatz:

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
quelle
4

Lösung für ECMAScript 2017-kompatible Browser

Hinweis: Dies funktioniert auch, wenn Sie einen Transpiler wie Babel verwenden.

'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();

Sie hätten auch warten können, bis alle Bilder geladen sind.

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

Oder verwenden Sie Promise.all, um sie parallel zu laden.

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

Mehr über Versprechen .

Weitere Informationen zu "Async" -Funktionen .

Mehr zum Destrukturierungsauftrag .

Mehr zu ECMAScript 2015 .

Mehr zu ECMAScript 2017 .

Amin NAIRI
quelle
2

Ich kann bestätigen, dass der Ansatz in der Frage ausreicht, um das Herunterladen und Zwischenspeichern der Bilder auszulösen (es sei denn, Sie haben dem Browser dies über Ihre Antwortheader verboten), zumindest in:

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

Um dies zu testen, habe ich eine kleine Webanwendung mit mehreren Endpunkten erstellt, die jeweils 10 Sekunden lang schlafen, bevor ich ein Bild eines Kätzchens serviere. Dann habe ich zwei Webseiten hinzugefügt, von denen eine ein <script>Tag enthielt, auf dem jedes der Kätzchen mit der preloadImageFunktion aus der Frage vorinstalliert ist , und die andere alle Kätzchen auf der Seite mit <img>Tags enthält.

In allen oben genannten Browsern stellte ich fest, dass <img>meine Kätzchen sofort gerendert wurden , wenn ich zuerst die Preloader-Seite besuchte, eine Weile wartete und dann zur Seite mit den Tags ging. Dies zeigt, dass der Preloader die Kätzchen in allen getesteten Browsern erfolgreich in den Cache geladen hat.

Sie können die Anwendung, mit der ich dies getestet habe, unter https://github.com/ExplodingCabbage/preloadImage-test sehen oder ausprobieren .

Beachten Sie insbesondere, dass diese Technik in den oben genannten Browsern auch dann funktioniert, wenn die Anzahl der Bilder, die durchlaufen werden, die Anzahl der parallelen Anforderungen überschreitet, die der Browser gleichzeitig bereit ist, entgegen Robins Antwort . Die Geschwindigkeit , mit der Sie Ihre Bilder Vorspannung wird natürlich durch , wie viele parallele begrenzt werden ersucht den Browser zu senden bereit ist, aber es wird verlangen schließlich jedes Bild URL Sie rufen preloadImage()auf.

Mark Amery
quelle
Ist es wichtig, ob die Bilder im Speicher oder auf der Festplatte zwischengespeichert werden? Es scheint, dass die OP-Methode auf der Festplatte zwischengespeichert wird, im Gegensatz zur @ clintgh-Methode, die im Speicher zwischengespeichert wird.
Chris L
1

Sie können diesen Code in die Datei index.html verschieben, um Bilder von einer beliebigen URL vorab zu laden

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

Der Browser funktioniert am besten mit dem Link-Tag im Kopf.

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);
    });
}
Brendan Farquharson
quelle