JavaScript ile görüntüleri önceden yükleme


263

Aşağıda yazdığım işlev, günümüzde yaygın olarak kullanılan tarayıcıların çoğunda olmasa da, görüntülerin çoğunu önceden yüklemek için yeterli mi?

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

Ben döngü ve preloadImageher URL işlevi çağırmak bir dizi görüntü URL'leri var .


19
Bazı (tümü?) Tarayıcıların, kullanmadığınız takdirde görüntüyü birkaç saniye sonra serbest bırakacağını unutmayın. Bundan kaçınmak için, imgnesneye, örneğin üst kapsamdaki bir dizide başvuru tutun .
Tamlyn

3
"Görüntüyü serbest bırak" ile ne demek istiyorsun? Tarayıcı tarafından önbelleğe alınmışsa, orada kalacak, değil mi?
Francisc

56
Biraz daha kısa:(new Image()).src = url;
mrzmyr

20
krom devtool açık olduğunda ve ağ panelinde 'chache'yi devre dışı bırak' etkinleştirildiğinde bunun işe yaramayacağını unutmayın
Wayou

6
Daha da kısa:new Image().src = url;
Daniel X Moore

Yanıtlar:


163

Evet. Bu, tüm büyük tarayıcılarda çalışmalıdır.


48
Moderatör Not: Lütfen bunu "cevap değil" olarak işaretlemeyi bırakın. Aslında, sorulan soruya doğrudan bir cevaptır. Cevabın yanlış olduğunu veya kanıtlarla yeteri kadar desteklenmediğini düşünüyorsanız, onu aşağı indirin .
Cody Gray

43

Bunu dene, bence bu daha iyi.

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

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


3
görüntülerin hiçbir onloadişleyicisi yok
Benny

12
Bunun neden daha iyi olduğunu açıklayabilir misiniz?
JJJ

2
@BeNice Sanırım yanlış anlıyorsunuz, görüntüleri yüklemek zaman uyumsuz, bu nedenle onloaddurumu ele almanız gerekiyor ya da sadece görüntüleri hafızada somutlaştırıyorsunuz.
Benny

3
Bu çözümü kullanırsanız, i değişkeni için var deyimini unutmayın. Aksi takdirde, çözülmesi gerçekten zor olan hataya neden olabilecek küresel olarak ayarlanacaktır (var ifadesini unuttuğunuzu bilmiyorsanızfor (var i = 0.....
Mohammer

2
@Mohammer bunun için teşekkürler, sadece sağladığım bağlantıdan kodu kopyaladım. Ben eklemek için şimdi kodunu düzenlemek olacaktırvar
clintgh

26

Benim durumumda, onloadetkinlik için işlevinize bir geri arama eklemek yararlı oldu :

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

Ve sonra geri arama ile önceden yüklenecek resimlere bir dizi URL olması için sarın: 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 Alternatifi: 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 Alternatifi: https://perishablepress.com/preload-images-css3/ (Y / T Linh Barajı)

.preload-images {
  display: none; 
  width: 0;
  height: 0;
  background: url(img01.jpg),
              url(img02.jpg),
              url(img03.jpg);
}

NOT: Bir kaptaki görüntüler display:noneönceden yüklenmeyebilir. Belki görünürlük: gizli daha iyi çalışır ama bunu test etmedim. Bunu işaret ettiği için Marco Del Valle'ya teşekkürler


Teşekkürler mplungjan. Bu özel davada bana yardımcı olmasa da, bilmek güzel.
Francisc

5
Bunun sayfanın yüklenmesini yavaşlatacağını mı söylüyorum, çünkü sayfanın load olayını başlatmadan önce bu görüntülerin indirilmesi gerekiyor mu?
jakubiszon

3
Bunların tüm tarayıcılarda çalışacağına inanmıyorum. Chrome görüntülerinde görünür olana kadar yüklenmediğini biliyorum. Ekranı kaldırmanız gerekecek: yok; bunun yerine onları görülemeyecek şekilde konumlandırmayı deneyin. Daha sonra gerekirse her şey yüklendikten sonra JS ile gizlenebilir.
Bullyen

3
Öğesindeki arka plan görüntüleri display: noneönceden yüklenmez.
Marquizzo

1
Bu yöntem benim için çalıştı sadece ben (1) kullanmadım display: none, (2) kullanmadım width/height: 0, (3) koymak no-repeatve görüntü dışında -<width>px -<height>pxolacak bir pozisyon ( sizi oraya götürecek, böylece resimleriniz 100 piksel yükseklik ya da daha az, kullanabileceğiniz 0 -100pxdiğer bir deyişle, url(...) no-repeat 0 -100px.
Alexis WILKE

11

Bazı olası sorunları önlemek için bir deneme / yakalama kullanmanızı öneririz:

OOP:

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

Standart:

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

Ayrıca, DOM'u sevdiğimde, eski aptal tarayıcıların DOM'u kullanmayla ilgili sorunları olabilir, bu yüzden freedev'in katkısının aksine IMHO'dan tamamen kaçının. Image () eski çöp tarayıcılarında daha iyi desteğe sahiptir.


7
Javascript Image yüklerken bu hata yakalamak nasıl olduğunu sanmıyorum - _img.onerror gibi kullanılabilir (gerekir?) Gibi bir şey var.
Greg0ry

3
"Olası sorunlar" nelerdir?
Kissaki

Komut desteği gibi sorunlar. Desteklemek istediğiniz bir tarayıcının javascript komutunu destekleyip desteklemediğini görmek için "kullanabilir miyim" sitesini ziyaret edin.
Dave

10

Bu yaklaşım biraz daha detaylıdır. Burada önceden yüklenmiş tüm görüntüleri bir kapta saklarsınız, bir div olabilir. Ve görüntüleri gösterdikten veya DOM içinde doğru konuma taşıdıktan sonra.

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

Burada deneyin , ayrıca birkaç yorum ekledim


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

Bu irade işi Evet, ancak tarayıcılar olacak sınırlamak gerçek aramaları (4-8 arasında) ve böylece istenen tüm görüntüleri önbelleğe almaz / önyüklemez.

Bunu yapmanın daha iyi bir yolu, görüntüyü aşağıdaki gibi kullanmadan önce onload çağırmaktır:

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

Lütfen bu tarayıcı sınırıyla ilgili bazı referansları bağlayabilir misiniz?
nulll

Bir referans bulmak zordur (en azından hiç bulamadım), ancak Chrome geliştirici araçlarındaki (f12) Chrome'un ağ sekmesini kullanarak önbellek ve sunucu çağrılarından yüklenenlere yakından bakarken bunu test etmek için kendi projemi kullandım. Aynı davranışı Firefox'ta ve mobil cihazlarda da buldum.
Robin

1
-1 çünkü buradaki ilk cümlede iddiayı dikkatle test ettim ve en azından modern tarayıcılarda yanlış olduğunu buldum. Bkz. Stackoverflow.com/a/56012084/1709587. Evet, tarayıcının göndermek istediği maksimum paralel HTTP isteği sayısında bir sınırlama vardır, ancak sonuçta gösterilen şekilde yapsanız bile, preloadImage'ı çağırdığınız her URL'yi istemeye döner.
Mark Amery

5

İşte yaklaşımım:

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

ECMAScript 2017 uyumlu tarayıcılar için çözüm

Not: Bu, Babel gibi bir transpiler kullanıyorsanız da işe yarayacaktır.

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

Ayrıca tüm görüntülerin yüklenmesini beklemiş olabilirsiniz.

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

Veya Promise.allparalel olarak yüklemek için kullanın .

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

Promises hakkında daha fazla bilgi .

"Async" işlevleri hakkında daha fazla bilgi .

Yıkım ödevi hakkında daha fazla bilgi .

ECMAScript 2015 hakkında daha fazla bilgi .

ECMAScript 2017 hakkında daha fazla bilgi .


2

Sorudaki yaklaşımın indirilecek ve önbelleğe alınacak görüntüleri tetiklemek için yeterli olduğunu onaylayabilirim (tarayıcının yanıt başlıklarınız aracılığıyla bunu yapmasını yasaklamadıysanız):

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

Bunu test etmek için, bir yavru kedi resmini sunmadan önce her biri 10 saniye uyuyan birkaç uç noktaya sahip küçük bir web uygulaması yaptım. Sonra iki web sayfasını ekledim, bunlardan biri <script>yavruların her birinin preloadImagesorudan gelen işlev kullanılarak önceden yüklendiği ve diğerinin<img> etiketleri kullanarak sayfadaki tüm yavru kedileri içeren bir etiketi içeriyordu .

Yukarıdaki tüm tarayıcılarda, önce ön yükleyici sayfasını ziyaret edersem, bir süre bekledim ve sonra <img>etiketlerle sayfaya gittim , yavru kedilerimin anında işlediğini buldum. Bu, önyükleyicinin, test edilen tüm tarayıcılarda yavruları önbelleğe başarıyla yüklediğini gösterir.

Bunu test etmek için kullandığım uygulamayı https://github.com/ExplodingCabbage/preloadImage-test adresinde görebilir veya deneyebilirsiniz .

Özellikle, bu tekniğin, döngülenmekte olan görüntü sayısı, Robin'in yanıtının aksine tarayıcının bir anda yapmak istediği paralel istek sayısını aşsa bile yukarıdaki tarayıcılarda çalıştığını unutmayın. . Resimlerinizin preload tabii kaç paralel tarafından sınırlı olacaktır hızdır tarayıcı göndermeye istekli olduğunu istekleri, ancak olacak sonunda aramak her resim URL'sini ister preloadImage()üzerinde.


Görüntülerin bellekte mi yoksa diskte mi önbelleklenmiş olması önemli midir? bellekte önbelleğe alınan @clintgh yönteminin aksine OP yönteminin diskte önbelleğe alındığı görülmektedir.
Chris L

1

Herhangi bir URL'den görüntüleri önceden yüklemek için bu kodu index.html dosyasına taşıyabilirsiniz

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

0

Tarayıcı, baştaki bağlantı etiketini kullanarak en iyi şekilde çalışır.

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);
    });
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.