Afbeeldingen vooraf laden met JavaScript

Is de functie die ik hieronder heb geschreven voldoende om afbeeldingen vooraf te laden in de meeste, zo niet alle, browsers die tegenwoordig veel worden gebruikt?

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

Ik heb een reeks afbeeldings-URL’s die ik doorloop en voor elke URL de functie preloadImageaanroep.


Antwoord 1, autoriteit 100%

Ja. Dit zou in alle belangrijke browsers moeten werken.


Antwoord 2, autoriteit 22%

Probeer dit, ik denk dat dit beter is.

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

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


Antwoord 3, autoriteit 15%

In mijn geval was het handig om een callback aan je functie toe te voegen voor de gebeurtenis onload:

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

En verpak het dan voor het geval van een array van URL’s naar afbeeldingen die vooraf moeten worden geladen met callback op alles is gedaan:
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');
});

Antwoord 4, autoriteit 9%

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

OPMERKING: Afbeeldingen in een container met display:noneworden mogelijk niet vooraf geladen.
Misschien werkt zichtbaarheid:hidden beter, maar dit heb ik niet getest. Bedankt Marco Del Valle voor het erop wijzen


Antwoord 5, Autoriteit 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)))

Antwoord 6, Autoriteit 6%

Ik raad u aan om een ​​poging / vangst te gebruiken om eventuele problemen te voorkomen:

OOP:

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

Standaard:

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

Ook, terwijl ik dol op DOM, oude domme browsers kunnen problemen hebben met u met DOM, dus vermijd het al het in totaal in strijd met de bijdrage van Freedev. Afbeelding () heeft betere ondersteuning in oude vuilnisbrowsers.


Antwoord 7, Autoriteit 5%

Deze aanpak is iets uitgebreider. Hier slaat u alle vooraf geladen afbeeldingen in een container op, kan een div zijn. En nadat u de afbeeldingen kunt tonen of deze binnen de DOM naar de juiste positie verplaatst.

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

Probeer het hier, ik heb ook enkele opmerkingen toegevoegd


Antwoord 8, autoriteit 4%

U kunt deze code naar index.html verplaatsen om afbeeldingen van elke url vooraf te laden

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

Antwoord 9, autoriteit 3%

Werkende oplossing vanaf 2020

De meeste antwoorden op dit bericht werken niet meer – (tenminste in Firefox)

Hier is mijn oplossing:

var cache = document.createElement("CACHE");
cache.style = "position:absolute;z-index:-1000;opacity:0;";
document.body.appendChild(cache);
function preloadImage(url) {
    var img = new Image();
    img.src = url;
    img.style = "position:absolute";
    cache.appendChild(img);
}

Gebruik:

preloadImage("example.com/yourimage.png");

Het is duidelijk dat <cache>geen “gedefinieerd” element is, dus je zou een <div>kunnen gebruiken als je dat zou willen.

Gebruik dit in uw CSS, in plaats van het kenmerk styletoe te passen:

cache {
    position: absolute;
    z-index: -1000;
    opacity: 0;
}
cache image {
    position: absolute;
}

Als je dit hebt getest, laat dan een reactie achter.

Opmerkingen:

  • Pas display: none;NIET toe op de cache – hierdoor wordt de . niet geladen
    afbeelding.
  • Verklein het afbeeldingselement niet, omdat dit ook van invloed is op de
    kwaliteit van de geladen afbeelding wanneer u deze gaat gebruiken.
  • INSTELLEN position: absoluteNaar de afbeelding is noodzakelijk, omdat de beeldelementen uiteindelijk buiten de viewport zijn – waardoor ze niet laden en van invloed zijn op de prestaties.

Update

Terwijl boven de oplossing werkt, is hier een kleine update die ik heb gemaakt om het goed te structureren:

(dit accepteert nu ook meerdere afbeeldingen in één functie)

var cache = document.createElement("CACHE");
document.body.appendChild(cache);
function preloadImage() {
    for (var i=0; i<arguments.length; i++) {
        var img = new Image();
        img.src = arguments[i];
        var parent = arguments[i].split("/")[1]; // Set to index of folder name
        if ($(`cache #${parent}`).length == 0) {
            var ele = document.createElement("DIV");
            ele.id = parent;
            cache.appendChild(ele);
        }
        $(`cache #${parent}`)[0].appendChild(img);
        console.log(parent);
    }
}
preloadImage(
    "assets/office/58.png",
    "assets/leftbutton/124.png",
    "assets/leftbutton/125.png",
    "assets/leftbutton/130.png",
    "assets/leftbutton/122.png",
    "assets/leftbutton/124.png"
);

voorbeeld:

Opmerkingen:

  • Probeer niet te veel afbeeldingen vooraf te houden op hetzelfde moment (dit kan belangrijke prestatiekwesties veroorzaken) – Ik heb dit hierbij gereden door afbeeldingen te verbergen, wat ik wist dat ik tijdens bepaalde evenementen niet zichtbaar zou zijn. Toon dan natuurlijk opnieuw wanneer ik het nodig had.

Antwoord 10, Autoriteit 3%

Oplossing voor Ecmascript 2017-compatibele browsers

Opmerking: dit werkt ook als je een transpiler zoals Babel gebruikt.

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

Je had ook kunnen wachten tot alle afbeeldingen waren geladen.

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

Of gebruik Promise.allom ze parallel te 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);
}

Meer over beloften.

Meer over “Async”-functies.

Meer over de destructieve opdracht.

Meer over ECMAScript 2015.

Meer over ECMAScript 2017.


Antwoord 11, autoriteit 2%

Hier is mijn aanpak:

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

Antwoord 12

Ja, dit zal werken, echterbrowsers zullen beperken(tussen 4-8) de daadwerkelijke oproepen en dus niet alle gewenste afbeeldingen in cache/preloaden.

Een betere manier om dit te doen, is door onload te bellen voordat u de afbeelding als volgt gebruikt:

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

Antwoord 13

Ik kan bevestigen dat de benadering in de vraag voldoende is om de afbeeldingen te activeren om te downloaden en in de cache te plaatsen (tenzij je de browser hebt verboden dit te doen via je antwoordheaders) in ten minste:

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

Om dit te testen, heb ik een kleine webapp gemaakt met verschillende eindpunten die elk 10 seconden slapen voordat een foto van een kitten wordt weergegeven. Vervolgens heb ik twee webpagina’s toegevoegd, waarvan er één een <script>-tag bevat waarin elk van de kittens is voorgeladen met behulp van de functie preloadImageuit de vraag, en de anderewaaronder alle kittens op de pagina met behulp van <img>tags.

In alle bovenstaande browsers ontdekte ik dat als ik eerst de preloader-pagina bezocht, even wachtte en vervolgens naar de pagina met de <img>-tags ging, mijn kittens onmiddellijk werden weergegeven. Dit toont aan dat de preloader met succes de kittens in de cache heeft geladen in alle geteste browsers.

Je kunt de applicatie die ik heb gebruikt om dit te testen bekijken of uitproberen op https://github.com /ExplodingCabbage/preloadImage-test.

Merk in het bijzonder op dat deze techniek in de bovenstaande browsers werkt, zelfs als het aantal afbeeldingen dat wordt doorgelust groter is dan het aantal parallelle verzoeken dat de browser tegelijk wil doen, in tegenstelling tot wat Robins antwoordsuggereert. De snelheid waarmee uw afbeeldingen vooraf worden geladen, wordt natuurlijk beperkt door het aantal parallelle verzoeken dat de browser wil verzenden, maar zaluiteindelijk elke afbeeldings-URL opvragen die u preloadImage()aan.


Antwoord 14

De browser werkt het beste met de link-tag in de kop.

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

Antwoord 15

Volgens de w3-specificatie kunt u nu als volgt laden met javascript:

var res = document.createElement("link");
res.rel = "preload";
res.as = "image";
res.href = "https://example.com/image.png";
document.head.appendChild(res);

https://www.w3.org/TR/preload/#introduction


Antwoord 16

Dit is het originele antwoord, maar met een modernere ES-syntaxis:

let preloadedImages = [];
export function preloadImages(urls) {
    preloadedImages = urls.map(url => {
        let img = new Image();
        img.src = url;
        img.onload = () => console.log(`image url [${url}] has been loaded successfully`);
        return img;
    });
}

Other episodes