Wstępne ładowanie obrazów za pomocą jQuery

689

Szukam szybkiego i łatwego sposobu na wstępne załadowanie obrazów za pomocą JavaScript. Używam jQuery, jeśli to ważne.

Widziałem to tutaj ( http: //nettuts.com ... ):

function complexLoad(config, fileNames) {
  for (var x = 0; x < fileNames.length; x++) {
    $("<img>").attr({
      id: fileNames[x],
      src: config.imgDir + fileNames[x] + config.imgFormat,
      title: "The " + fileNames[x] + " nebula"
    }).appendTo("#" + config.imgContainer).css({ display: "none" });
  }
};

Ale wygląda trochę przesadnie jak na to, czego chcę!

Wiem, że istnieją wtyczki jQuery, które to robią, ale wszystkie wydają się nieco duże (pod względem wielkości); Potrzebuję tylko szybkiego, łatwego i krótkiego sposobu wstępnego ładowania zdjęć!


źródło
10
$.each(arguments,function(){(new Image).src=this});
David Hellsing

Odpowiedzi:

969

Szybko i łatwo:

function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('<img/>')[0].src = this;
        // Alternatively you could use:
        // (new Image()).src = this;
    });
}

// Usage:

preload([
    'img/imageName.jpg',
    'img/anotherOne.jpg',
    'img/blahblahblah.jpg'
]);

Lub, jeśli chcesz mieć wtyczkę jQuery:

$.fn.preload = function() {
    this.each(function(){
        $('<img/>')[0].src = this;
    });
}

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();
James
źródło
25
Czy element obrazu nie musi być wstawiany do DOM, aby mieć pewność, że przeglądarka go buforuje?
JoshNaro,
8
Wierzę, że $('<img />')po prostu tworzy element obrazu w pamięci (patrz link ). Wygląda na to, '$('<img src="' + this + '" />')że faktycznie stworzyłby element w ukrytym DIV, ponieważ jest bardziej „skomplikowany”. Jednak nie sądzę, że jest to potrzebne w większości przeglądarek.
JoshNaro
104
To dziwny sposób pisania wtyczki jQuery. Dlaczego nie $.preload(['img1.jpg', 'img2.jpg'])?
alex
12
Pamiętaj, aby wywołać to w środku, $(window).load(function(){/*preload here*/});ponieważ w ten sposób wszystkie obrazy w dokumencie są ładowane jako pierwsze, prawdopodobnie są one najpierw potrzebne.
Jasper Kennis
12
@RegionalC - Może być nieco bezpieczniej ustawić loadzdarzenie przed ustawieniem zdarzenia na wypadek, srcgdyby obraz zakończył ładowanie przed ustawieniem zdarzenia ładowania? $('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
sparebytes
102

Oto poprawiona wersja pierwszej odpowiedzi, która faktycznie ładuje obrazy do DOM i domyślnie je ukrywa.

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}
Dennis Rongo
źródło
38
hide()jest bardziej zwięzły niż css('display', 'none').
alex
6
Jaka jest zaleta wstawiania ich do DOM?
alex
Chciałbym również poznać zaletę wstawiania ich do DOM.
jedmao
11
Z mojego doświadczenia wynika, że ​​wstępne ładowanie obrazu do DOM informuje przeglądarkę o jego istnieniu i poprawnym buforowaniu. W przeciwnym razie obraz istnieje tylko w pamięci, która działa tylko w przypadku aplikacji jednostronicowych.
Dennis Rongo
Dennis Rongo. Dołączanie obrazów do DOM naprawiono losowe ponowne ładowanie w Chrome. Dzięki!
tmorell
52

Użyj obiektu JavaScript Image .

Ta funkcja umożliwia wywołanie zwrotne po załadowaniu wszystkich zdjęć. Należy jednak pamiętać, że nigdy nie wyzwoli oddzwonienia, jeśli przynajmniej jeden zasób nie zostanie załadowany. Można to łatwo naprawić, implementując onerrorwywołanie zwrotne i zwiększając loadedwartość lub obsługując błąd.

var preloadPictures = function(pictureUrls, callback) {
    var i,
        j,
        loaded = 0;

    for (i = 0, j = pictureUrls.length; i < j; i++) {
        (function (img, src) {
            img.onload = function () {                               
                if (++loaded == pictureUrls.length && callback) {
                    callback();
                }
            };

            // Use the following callback methods to debug
            // in case of an unexpected behavior.
            img.onerror = function () {};
            img.onabort = function () {};

            img.src = src;
        } (new Image(), pictureUrls[i]));
    }
};

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('a');
});

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('b');
});
Gajus
źródło
4
Zrób dobrze, użyj obiektu JavaScript Image. czy zauważyłeś, że ludzie robią coś złego w tych odpowiedziach?
alex,
Genialny kod. Czy mam rację sądząc, że spowoduje to onloadzdarzenie, nawet jeśli obraz zostanie buforowany? (Ponieważ img.onloadjest zadeklarowany jako pierwszy). Tak pokazały moje testy.
Startec
3
@alex potencjalnie, tak. Jeśli celem jest wstępne ładowanie (co sugeruje kolejność działania), wolałbym zobaczyć surową opcję JS zamiast opcji zależnych od jQuery.
Charlie Schliesser
Podobało mi się to rozwiązanie, ale właśnie odkryłem, że nie działa ono w moim Firefoksie. Czy to tylko ja, czy inni mają ten sam problem?
Gazillion,
@Gazillion podaj więcej szczegółów. Jak definiujesz „nie działa”? Co to jest wersja FF?
Gajus,
35

JP, po sprawdzeniu twojego rozwiązania nadal miałem problemy z Firefoksem, w którym nie ładowałoby zdjęć przed przejściem wraz z ładowaniem strony. Odkryłem to, umieszczając trochę sleep(5)w skrypcie po stronie serwera. Wdrożyłem następujące rozwiązanie oparte na twoim, które wydaje się rozwiązać ten problem.

Zasadniczo dodałem wywołanie zwrotne do wtyczki wstępnego ładowania jQuery, aby była wywoływana po prawidłowym załadowaniu wszystkich obrazów.

// Helper function, used below.
// Usage: ['img1.jpg','img2.jpg'].remove('img1.jpg');
Array.prototype.remove = function(element) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == element) { this.splice(i,1); }
  }
};

// Usage: $(['img1.jpg','img2.jpg']).preloadImages(function(){ ... });
// Callback function gets called after all images are preloaded
$.fn.preloadImages = function(callback) {
  checklist = this.toArray();
  this.each(function() {
    $('<img>').attr({ src: this }).load(function() {
      checklist.remove($(this).attr('src'));
      if (checklist.length == 0) { callback(); }
    });
  });
};

Z mojego zainteresowania korzystam w następujący sposób:

$.post('/submit_stuff', { id: 123 }, function(response) {
  $([response.imgsrc1, response.imgsrc2]).preloadImages(function(){
    // Update page with response data
  });
});

Mam nadzieję, że pomoże to komuś, kto wejdzie na tę stronę z Google (tak jak ja), szukając rozwiązania w zakresie wstępnego ładowania zdjęć w połączeniach Ajax.

Dave
źródło
2
Dla tych, którzy nie są zainteresowani zepsuciem prototypu Array., zamiast tego możesz zrobić: checklist = this.lengthI w funkcji onload: checklist--Następnie:if (checklist == 0) callback();
MiniGod
3
Wszystko byłoby dobrze, jednak dodawanie nowych lub usuwanie istniejących metod do obiektu, którego nie jesteś właścicielem, jest jedną z najgorszych praktyk w JavaScript.
Rihards,
Ładnie i szybko, ale ten kod nigdy nie uruchomi oddzwaniania, jeśli jeden z obrazów jest uszkodzony lub nie można go załadować.
SammyK,
.attr({ src: this }).load(function() {...})w .load(function() {...}).attr({ src: this })przeciwnym razie będziesz mieć problemy z buforowaniem.
Kevin B
25

Ten jednowierszowy kod jQuery tworzy (i ładuje) element DOM img, nie pokazując go:

$('<img src="img/1.jpg"/>');
rkcell
źródło
4
@huzzah - Lepiej będzie po prostu użyć duszka. Mniej żądań HTTP. :)
Alex K
22
$.fn.preload = function (callback) {
  var length = this.length;
  var iterator = 0;

  return this.each(function () {
    var self = this;
    var tmp = new Image();

    if (callback) tmp.onload = function () {
      callback.call(self, 100 * ++iterator / length, iterator === length);
    };

    tmp.src = this.src;
  });
};

Użycie jest dość proste:

$('img').preload(function(perc, done) {
  console.log(this, perc, done);
});

http://jsfiddle.net/yckart/ACbTK/

yckart
źródło
To jest naprawdę dobra odpowiedź, naprawdę powinna być najlepiej głosowaną odpowiedzią imho.
sleepycal
1
Kilka słów wyjaśniających byłoby miło.
robsch
Dobra odpowiedź, ale sugeruję użycie picsum.photos zamiast lorempixel.com
Aleksandar,
19

Mam małą wtyczkę, która to obsługuje.

Nazywa się waitForImages i może obsługiwać imgelementy lub dowolny element z odniesieniem do obrazu w CSS, np div { background: url(img.png) }.

Jeśli chcesz po prostu załadować wszystkie obrazy, w tym te, do których odwołuje się CSS, oto jak to zrobić :)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});
alex
źródło
Czy ta wtyczka powoduje ładowanie obrazów, które jeszcze nie pojawiły się na stronie, czy tylko dołącza zdarzenia do obrazów, które już miały zostać załadowane?
Dave Cameron
@DaveCameron Nie ma znaczenia, czy obrazy są widoczne, czy nie. Możesz łatwo go rozwidlić i dokonać tej zmiany - wystarczy dodać :visibledo niestandardowego selektora.
alex
Ta wtyczka wygląda bardzo interesująco. Jednak dokumentacja jquery podkreśla, że loadzdarzenie zastosowane do obrazów: „nie działa konsekwentnie ani niezawodnie w różnych przeglądarkach”. Jak wtyczka poradziła sobie z tym?
EleventyOne
@EleventyOne Sprawdź źródło w odniesieniu do selektora niestandardowego.
alex
@alex W jaki sposób ta wtyczka ładuje obrazy ustawione w css na: najechaniu na element? To nie działa dla mnie
Philipp Hofmann
13

możesz załadować obrazy do swojego html gdzieś za pomocą display:none;reguły css , a następnie pokazać je w dowolnym momencie za pomocą js lub jquery

nie używaj funkcji js lub jquery do wstępnego ładowania to tylko reguła css W porównaniu z wieloma wierszami js do wykonania

przykład: HTML

<img src="someimg.png" class="hide" alt=""/>

Css:

.hide{
display:none;
}

jQuery:

//if want to show img 
$('.hide').show();
//if want to hide
$('.hide').hide();

Wstępne ładowanie obrazów za pomocą jquery / javascript nie jest dobre, ponieważ obrazy ładują się na stronie w kilka milisekund + masz milisekundy na przeanalizowanie i wykonanie skryptu, zwłaszcza jeśli są to duże obrazy, więc ukrywanie ich w hml jest lepsze również ze względu na wydajność, ponieważ obraz jest naprawdę wstępnie załadowany bez widoczności, dopóki tego nie pokażesz!

to ja
źródło
2
Więcej informacji na temat tego podejścia można znaleźć tutaj: perishablepress.com/…
gdelfino
3
Należy jednak pamiętać, że technika ta ma poważną wadę: strona nie zostanie całkowicie załadowana, dopóki nie zostaną załadowane wszystkie obrazy. W zależności od liczby zdjęć do załadowania i ich rozmiaru może to zająć trochę czasu. Co gorsza, jeśli znacznik <img> nie określa wysokości i szerokości, niektóre przeglądarki mogą poczekać, aż obraz zostanie pobrany, zanim wyrenderuje resztę strony.
@Alex, w każdym razie musisz załadować obraz, możesz wybrać, czy załadować je za pomocą html i uniknąć wszelkiego rodzaju migających i w połowie załadowanych obrazów, lub jeśli chcesz uzyskać większą prędkość, aby uzyskać niestabilne rozwiązanie
to jest
też naprawdę myślę, że tworzenie tagów HTML za pomocą javascript jest w ogóle nieczytelne, tylko moje 2 centy
to
1
Potrzebowałem bardzo szybkiego rozwiązania dla bardzo złożonego projektu i to było to.
Germstorm,
13

Ten jQuery imageLoader plugin jest tylko 1.39kb

stosowanie:

$({}).imageLoader({
    images: [src1,src2,src3...],
    allcomplete:function(e,ui){
        //images are ready here
        //your code - site.fadeIn() or something like that
    }
});

istnieją również inne opcje, takie jak to, czy chcesz ładować obrazy synchronicznie, czy asychronicznie oraz kompletne zdarzenie dla każdego pojedynczego obrazu.

alzclarke
źródło
@AamirAfridi Dlaczego tak jest?
Ian
1
@ Ian, ponieważ dokument jest już gotowy do czasu uruchomienia oddzwaniania. $ (dokument) .ready służy do upewnienia się, że twój DOM jest załadowany. Jeśli chodzi o wywołanie zwrotne, oznacza to, że wszystkie obrazy są ładowane, co oznacza, że ​​yoru DOM jest ładowany, więc nie ma potrzeby document.ready wewnątrz wywołania zwrotnego.
Aamir Afridi
1
@AirirAfridi Nie ma gwarancji, że dokument jest gotowy do oddzwaniania ... skąd wnioskujesz? Na stronie wtyczki nie ma nic, co mówi, że allcompleteoddzwanianie jest wykonywane, gdy DOM jest gotowy. Jest całkiem spora szansa, że ​​obrazy zakończą się ładować po tym, jak DOM jest gotowy, ale nie ma powodu, aby zakładać. Nie wiem, dlaczego uważasz, że wywołania zwrotne są magiczne i są wykonywane, gdy DOM jest gotowy ... czy możesz wyjaśnić, skąd to się bierze? To, że obrazy są załadowane, nie oznacza, że ​​DOM jest gotowy
Ian
@AamirAfridi A dokumentacja wtyczki mówi nawet: NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!... bezpośrednio zaleca to, co pokazuje ta odpowiedź.
Ian
5
@AamirAfridi To nie ma sensu w tej sytuacji. Wtyczka jest modułem wstępnego ładowania. Chcesz go wykonać jak najszybciej. I jest wiele rzeczy, które nie zależą od DOM, które chcesz odpalić tak szybko, jak to możliwe, a następnie, gdy DOM jest gotowy, zrób coś.
Ian
9

Szybkim, pozbawionym wtyczek sposobem na wstępne ładowanie obrazów w jQuery i uzyskanie funkcji wywołania zwrotnego jest utworzenie wielu imgznaczników jednocześnie i policzenie odpowiedzi, np.

function preload(files, cb) {
    var len = files.length;
    $(files.map(function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}

preload(["one.jpg", "two.png", "three.png"], function() {
    /* Code here is called once all files are loaded. */
});
    

Pamiętaj, że jeśli chcesz obsługiwać IE7, musisz użyć tej nieco mniej ładnej wersji (która działa również w innych przeglądarkach):

function preload(files, cb) {
    var len = files.length;
    $($.map(files, function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}
izb
źródło
7

Dzięki za to! Chciałbym dodać trochę riffu do odpowiedzi JP - nie wiem, czy to komukolwiek pomoże, ale w ten sposób nie musisz tworzyć szeregu obrazów i możesz załadować wszystkie duże obrazy, jeśli nazwij swoje kciuki poprawnie. Jest to przydatne, ponieważ mam kogoś, kto pisze wszystkie strony w HTML, i zapewnia im o jeden krok mniej do zrobienia - eliminując potrzebę tworzenia tablicy obrazów i kolejny krok, w którym wszystko może się popsuć.

$("img").each(function(){
    var imgsrc = $(this).attr('src');
    if(imgsrc.match('_th.jpg') || imgsrc.match('_home.jpg')){
      imgsrc = thumbToLarge(imgsrc);
      (new Image()).src = imgsrc;   
    }
});

Zasadniczo dla każdego obrazu na stronie pobiera plik src każdego obrazu, jeśli spełnia określone kryteria (jest to kciuk lub obraz strony głównej), zmienia nazwę (ciąg podstawowy zamienia w obrazie src), a następnie ładuje obrazy .

W moim przypadku strona była pełna obrazów kciuka, wszystkie o nazwach podobnych do image_th.jpg, a wszystkie odpowiadające im duże obrazy są nazwane image_lg.jpg. Kciuk do dużego po prostu zamienia _th.jpg na _lg.jpg, a następnie wstępnie ładuje wszystkie duże obrazy.

Mam nadzieję, że to komuś pomoże.

dgig
źródło
3
    jQuery.preloadImage=function(src,onSuccess,onError)
    {
        var img = new Image()
        img.src=src;
        var error=false;
        img.onerror=function(){
            error=true;
            if(onError)onError.call(img);
        }
        if(error==false)    
        setTimeout(function(){
            if(img.height>0&&img.width>0){ 
                if(onSuccess)onSuccess.call(img);
                return img;
            }   else {
                setTimeout(arguments.callee,5);
            }   
        },0);
        return img; 
    }

    jQuery.preloadImages=function(arrayOfImages){
        jQuery.each(arrayOfImages,function(){
            jQuery.preloadImage(this);
        })
    }
 // example   
    jQuery.preloadImage(
        'img/someimage.jpg',
        function(){
            /*complete
            this.width!=0 == true
            */
        },
        function(){
            /*error*/
        }
    )
Alex
źródło
7
Witamy w Stack Overflow! Zamiast pisać tylko blok kodu, wyjaśnij, dlaczego ten kod rozwiązuje postawiony problem. Bez wyjaśnienia nie jest to odpowiedź.
Martijn Pieters
3

Używam następującego kodu:

$("#myImage").attr("src","img/spinner.gif");

var img = new Image();
$(img).load(function() {
    $("#myImage").attr("src",img.src);
});
img.src = "http://example.com/imageToPreload.jpg";
Cristan
źródło
Najpierw powiąż ze zdarzeniem ładowania, a następnie ustaw src.
Kevin B
1

Korzystam z pliku manifestu, aby powiedzieć (nowoczesnym) przeglądarkom internetowym, aby ładowały również wszystkie odpowiednie obrazy i buforowały je. Użyj Grunt i mruk-manifest, aby zrobić to automatycznie i nigdy więcej nie martw się o skrypty ładowania wstępnego, unieważniające pamięć podręczną, CDN itp.

https://github.com/gunta/grunt-manifest

Christian Landgren
źródło
0

Działa to dla mnie nawet w IE9:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });
Zymotik
źródło
1
To ostatecznie nie powiedzie się z powodu buforowania, zawsze należy powiązać zdarzenie load przed ustawieniem atrybutu src.
Kevin B
0

Chciałem to zrobić z niestandardową nakładką interfejsu API Map Google. Ich przykładowy kod po prostu używa JS do wstawiania elementów IMG, a pole symbolu zastępczego obrazu jest wyświetlane do momentu załadowania obrazu. Znalazłem tutaj odpowiedź, która zadziałała dla mnie: https://stackoverflow.com/a/10863680/2095698 .

$('<img src="'+ imgPaht +'">').load(function() {
$(this).width(some).height(some).appendTo('#some_target');
});

Spowoduje to wstępne załadowanie obrazu, jak sugerowano wcześniej, a następnie użycie modułu obsługi do dołączenia obiektu img po załadowaniu adresu URL img. Dokumentacja jQuery ostrzega, że ​​buforowane obrazy nie działają dobrze z tym kodem zdarzenia / obsługi, ale działa dla mnie w FireFox i Chrome i nie muszę się martwić o IE.

bwinchester
źródło
Jak się dowiedziałeś, nie zadziała to dobrze z obrazami z pamięci podręcznej. obejściem tego problemu jest najpierw powiązanie zdarzenia ładowania, a następnie ustawienie atrybutu src.
Kevin B
-1
function preload(imgs) {
    $(imgs).each(function(index, value) {
        $('<img />').attr('src', value).appendTo('body').css('display', 'none');
    });
}

.attr('src',value) nie .attr('src',this)

żeby to podkreślić :)

Whisher
źródło
Zakres thiswewnątrz przekazywanego wywołania zwrotnego $.eachjest przypisywany do iterowanej wartości.
Oybek
? $ (['img1.jpg', 'img2.jpg', 'img3.jpg']). each (funkcja (indeks, wartość) {console.log (wartość); // img1.jpg console.log (this) ; // String {0 = "i", 1 = "m", 2 = "g", more ...} $ ('<img />'). Attr ('src', this) .appendTo (' body ”). css („ display ”,„ none ”);});
Whisher
Hm Chyba tu jesteś. Na przykład $("div").each(function(i, el){ console.log(el == this);});generuje wszystkie trues; Iteracja nad tablicą wydaje się zachowywać inaczej.
Oybek
-2

5 linii w coffeescript

array = ['/img/movie1.png','/img/movie2.png','/img/movie3.png']

$(document).ready ->
  for index, image of array
    img[index] = new Image()
    img[index].src = image
Guy Morita
źródło
2
Czy możesz rozwinąć sposób działania rozwiązania, aby rozwiązać pytanie w PO? A może komentujesz swój kod, aby inni mogli go łatwiej zrozumieć?
Ro Yo Mi
-4

Dla tych, którzy znają trochę skryptów akcji, możesz przy minimalnym wysiłku sprawdzić Flash Playera i zrobić Flash Preloader, który możesz również wyeksportować do HTML5 / Javascript / Jquery. Aby użyć, jeśli Flash Player nie zostanie wykryty, sprawdź przykłady, jak to zrobić z rolą YouTube z powrotem do html5 player :) I stwórz własną. Nie mam szczegółów, ponieważ jeszcze nie zacząłem, jeśli nie zapomnę, opublikuję to później i wypróbuję jakiś własny kod Jquery do mojego.

Peter Gruppelaar
źródło
To nie jest rozwiązanie. Przeglądarka domyślnie nie obsługuje odtwarzacza Flash. Można to łatwo osiągnąć za pomocą JavaScript, który jest rodzimym językiem przeglądarki.
Usman Ahmed
Pamiętam hejterów Flash z 2012 roku ... to nie było normalne ... gdziekolwiek poszedłem, zostałem zaatakowany, nawet kiedy użyłem słowa Flash.
Peter Gruppelaar,