Memulai gambar dengan JavaScript

263

Apakah fungsi yang saya tulis di bawah ini cukup untuk memuat gambar di sebagian besar, jika tidak semua, peramban yang umum digunakan saat ini?

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

Saya memiliki array URL gambar yang saya putar dan memanggil preloadImagefungsi untuk setiap URL.

Francisc
sumber
19
Perhatikan bahwa beberapa browser (semua?) Akan merilis gambar setelah beberapa detik jika Anda belum menggunakannya. Untuk menghindari hal ini, simpan referensi ke imgobjek, misalnya dalam array dalam lingkup induk.
Tamlyn
3
Apa yang Anda maksud dengan "melepaskan gambar"? Jika di-cache oleh browser, itu akan tetap di sana, bukan?
Francisc
56
Sedikit lebih pendek:(new Image()).src = url;
mrzmyr
20
perhatikan ini tidak akan berfungsi ketika chrome devtool terbuka dan 'disable chache' diaktifkan di dalam panel jaringan
Wayou
6
Bahkan lebih pendek:new Image().src = url;
Daniel X Moore

Jawaban:

163

Iya. Ini harus bekerja pada semua browser utama.

Huzi --- Javiator
sumber
48
Moderator Catatan: Tolong hentikan menandai ini sebagai "bukan jawaban". Ini sebenarnya adalah jawaban langsung untuk pertanyaan yang diajukan. Jika Anda berpikir jawabannya salah, atau tidak didukung dengan bukti, maka unduhkanlah .
Cody Gray
43

Coba ini saya pikir ini lebih baik.

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

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

Clintgh
sumber
3
tidak ada onloadpenangan untuk salah satu gambar
Benny
12
Bisakah Anda menjelaskan mengapa ini lebih baik?
JJJ
2
@BeNice Saya pikir Anda salah paham, memuat gambar adalah async, karena itu Anda harus menangani onloadkeadaan atau Anda hanya membuat gambar di memori.
Benny
3
Jika Anda menggunakan solusi ini, jangan lupa pernyataan var untuk variabel-i. Kalau tidak, itu akan diatur secara global yang dapat menyebabkan kesalahan yang sangat sulit untuk diselesaikan (kecuali Anda tahu bahwa Anda lupa pernyataan var.for (var i = 0.....
Mohammer
2
@Mohammer terima kasih untuk ini, saya baru saja menyalin kode dari tautan yang saya berikan. Saya akan mengedit kode sekarang untuk menambahkanvar
clintgh
26

Dalam kasus saya, berguna untuk menambahkan callback ke fungsi Anda untuk onloadacara:

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

Dan kemudian membungkusnya untuk kasus array URL ke gambar yang akan dimuat dengan callback pada semua dilakukan: 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
sumber
19

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

Alternatif CSS3: https://perishablepress.com/preload-images-css3/ (Bendungan H / T Linh)

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

CATATAN: Gambar dalam wadah dengan display:nonemungkin tidak dimuat sebelumnya. Mungkin visibilitas: tersembunyi akan bekerja lebih baik tetapi saya belum menguji ini. Terima kasih Marco Del Valle karena menunjukkan ini

mplungjan
sumber
Terima kasih mplungjan. Meskipun tidak membantu saya dengan kasus khusus ini, itu baik untuk diketahui.
Francisc
5
Apakah saya benar mengatakan ini akan memperlambat pemuatan halaman karena gambar-gambar ini perlu diunduh sebelum halaman dapat meluncurkan acara pemuatan?
jakubiszon
3
Saya tidak percaya ini akan bekerja pada semua browser. Saya tahu bahwa pada gambar Chrome tidak dimuat sampai terlihat. Akan perlu menghapus tampilan: tidak ada; dan sebagai gantinya cobalah memposisikan mereka sehingga mereka tidak dapat dilihat. Kemudian dapat bersembunyi dengan JS setelah semuanya dimuat jika diperlukan.
Bullyen
3
Gambar latar dalam suatu elemen dengan display: nonetidak akan dimuat sebelumnya.
Marquizzo
1
Metode ini bekerja untuk saya hanya saya (1) tidak menggunakan display: none, (2) tidak menggunakan width/height: 0, (3) menempatkan no-repeatdan posisi sedemikian rupa sehingga gambar akan berada di luar ( -<width>px -<height>pxakan membawa Anda ke sana, jadi jika gambar Anda semua 100px dalam tinggi atau kurang, Anda dapat menggunakan 0 -100px, dengan kata lain url(...) no-repeat 0 -100px.
Alexis Wilke
11

Saya sarankan Anda menggunakan try / catch untuk mencegah beberapa masalah yang mungkin terjadi:

OOP:

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

Standar:

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

Juga, sementara saya suka DOM, browser bodoh lama mungkin memiliki masalah dengan Anda menggunakan DOM, jadi hindari sama sekali IMHO bertentangan dengan kontribusi freedev. Gambar () memiliki dukungan yang lebih baik di peramban sampah lama.

Dave
sumber
7
Saya tidak berpikir ini adalah bagaimana Anda menangkap kesalahan saat memuat Gambar di javascript - ada sesuatu seperti _img.onerror yang dapat (harus?) Digunakan.
Greg0ry
3
Apa itu "kemungkinan masalah"?
Kissaki
Masalah seperti dukungan untuk perintah. Lihatlah situs "dapatkah saya menggunakan" untuk melihat apakah browser yang Anda perlukan untuk mendukung memiliki dukungan untuk perintah javascript.
Dave
10

Pendekatan ini sedikit lebih rumit. Di sini Anda menyimpan semua gambar yang dimuat sebelumnya dalam wadah, mungkin div. Dan setelah Anda bisa menunjukkan gambar atau memindahkannya di dalam DOM ke posisi yang benar.

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

Coba di sini , saya juga menambahkan beberapa komentar

freedev
sumber
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)))
david_adler
sumber
5

Ya ini akan berfungsi, namun browser akan membatasi (antara 4-8) panggilan yang sebenarnya dan dengan demikian tidak cache / pramuat semua gambar yang diinginkan.

Cara yang lebih baik untuk melakukan ini adalah memanggil onload sebelum menggunakan gambar seperti:

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
sumber
Bisakah Anda menautkan beberapa referensi tentang batas browser ini?
nulll
Sulit untuk menemukan referensi (setidaknya saya tidak pernah menemukan satu), tetapi saya menggunakan proyek saya sendiri untuk menguji ini sambil melihat dengan cermat pada apa yang diambil dari cache dan panggilan server menggunakan tab jaringan Chrome di alat pengembang Chrome (f12). Saya menemukan perilaku yang sama di Firefox dan menggunakan ponsel.
Robin
1
-1 karena saya hati-hati menguji klaim dalam kalimat pertama di sini dan menemukan itu salah, setidaknya di browser modern. Lihat stackoverflow.com/a/56012084/1709587. Ya, ada batasan jumlah maksimum permintaan HTTP paralel yang bersedia dikirim oleh browser, tetapi akhirnya tidak bisa meminta setiap URL yang Anda panggil preloadImage aktif, bahkan jika Anda melakukannya dengan cara yang ditunjukkan dalam pertanyaan.
Mark Amery
5

Inilah pendekatan saya:

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

Solusi untuk browser yang sesuai dengan ECMAScript 2017

Catatan: ini juga akan berfungsi jika Anda menggunakan transpiler seperti 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();

Anda juga bisa menunggu semua gambar dimuat.

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

Atau gunakan Promise.alluntuk memuatnya secara paralel.

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

Lebih lanjut tentang Janji .

Lebih lanjut tentang fungsi "Async" .

Lebih lanjut tentang penugasan penghancuran .

Lebih lanjut tentang ECMAScript 2015 .

Lebih lanjut tentang ECMAScript 2017 .

Amin NAIRI
sumber
2

Saya dapat mengonfirmasi bahwa pendekatan dalam pertanyaan ini cukup untuk memicu gambar untuk diunduh dan di-cache (kecuali Anda melarang browser melakukannya melalui header respons Anda) di, setidaknya:

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

Untuk menguji ini, saya membuat webapp kecil dengan beberapa titik akhir yang masing-masing tidur selama 10 detik sebelum menyajikan gambar anak kucing. Lalu saya menambahkan dua halaman web, yang satu berisi <script>tag di mana masing-masing anak kucing dimuat menggunakan preloadImagefungsi dari pertanyaan, dan yang lainnya mencakup semua anak kucing di halaman menggunakan <img>tag.

Di semua browser di atas, saya menemukan bahwa jika saya mengunjungi halaman preloader terlebih dahulu, menunggu sebentar, dan kemudian pergi ke halaman dengan <img>tag, anak-anak kucing saya merender langsung. Ini menunjukkan bahwa preloader berhasil memuat anak kucing ke dalam cache di semua browser yang diuji.

Anda dapat melihat atau mencoba aplikasi yang saya gunakan untuk menguji ini di https://github.com/ExplodingCabbage/preloadImage-test .

Perhatikan secara khusus bahwa teknik ini bekerja di browser di atas bahkan jika jumlah gambar yang dilewati melebihi jumlah permintaan paralel yang bersedia dilakukan oleh browser pada suatu waktu, bertentangan dengan apa yang disarankan jawaban Robin . Tingkat di mana preload gambar Anda tentu saja akan dibatasi oleh berapa banyak permintaan paralel yang bersedia dikirim oleh browser, tetapi pada akhirnya akan meminta setiap URL gambar yang Anda panggil preloadImage().

Mark Amery
sumber
Apakah masalah apakah gambar di-cache dalam memori atau disk? tampaknya metode OP di-cache pada disk sebagai lawan dari metode @ clintgh yang di-cache dalam memori.
Chris L
1

Anda dapat memindahkan kode ini ke index.html untuk memuat ulang gambar dari url apa pun

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

Browser akan bekerja paling baik menggunakan tag tautan di kepala.

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
sumber