Tải trước hình ảnh bằng JavaScript


263

Là chức năng tôi đã viết dưới đây có đủ để tải trước hình ảnh trong hầu hết, nếu không phải tất cả, các trình duyệt thường được sử dụng ngày nay?

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

Tôi có một loạt các URL hình ảnh mà tôi lặp lại và gọi preloadImagehàm cho mỗi URL.


19
Lưu ý rằng một số trình duyệt (tất cả?) Sẽ giải phóng hình ảnh sau vài giây nếu bạn chưa sử dụng nó. Để tránh điều này, hãy giữ một tham chiếu đến imgđối tượng, ví dụ như trong một mảng trong phạm vi cha.
Tamlyn

3
Bạn có ý nghĩa gì khi "phát hành hình ảnh"? Nếu nó được lưu trữ bởi trình duyệt, nó sẽ ở lại đó, phải không?
Francisc

56
(new Image()).src = url;
Ngắn

20
lưu ý điều này sẽ không hoạt động khi chrome devtool mở và 'vô hiệu hóa chache' được bật trong bảng điều khiển mạng
Wayou

6
Thậm chí ngắn hơn:new Image().src = url;
Daniel X Moore

Câu trả lời:


163

Đúng. Điều này sẽ làm việc trên tất cả các trình duyệt chính.


48
Người điều hành Lưu ý: Vui lòng dừng gắn cờ này là "không phải là câu trả lời". Trên thực tế, đó là một câu trả lời trực tiếp cho câu hỏi đã được hỏi. Nếu bạn nghĩ rằng câu trả lời là sai, hoặc không được hỗ trợ đầy đủ bằng chứng, thì hãy downvote nó.
Cody Grey

43

Hãy thử điều này tôi nghĩ rằng điều này là tốt hơn.

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

Nguồn: http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/


3
không có onloadxử lý cho bất kỳ hình ảnh nào
Benny

12
Bạn có thể giải thích tại sao điều này là tốt hơn?
JJJ

2
@BeNice Tôi nghĩ bạn đang hiểu nhầm, tải hình ảnh là không đồng bộ, do đó bạn phải xử lý onloadtrạng thái hoặc bạn chỉ đang tạo hình ảnh trong bộ nhớ.
Benny

3
Nếu bạn sử dụng giải pháp này, đừng quên câu lệnh var cho biến i. Nếu không, nó sẽ được đặt trên toàn cầu có thể gây ra lỗi rất khó giải quyết (trừ khi bạn biết rằng bạn đã quên câu lệnh var.for (var i = 0.....
Mohammer

2
@Mohammer cảm ơn vì điều này, tôi chỉ sao chép mã từ liên kết tôi cung cấp. Tôi sẽ chỉnh sửa mã ngay bây giờ để thêmvar
clintgh

26

Trong trường hợp của tôi, thật hữu ích khi thêm một cuộc gọi lại vào chức năng của bạn cho onloadsự kiện:

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

Và sau đó bọc nó cho trường hợp một mảng URL thành hình ảnh được tải sẵn với gọi lại trên tất cả đã được thực hiệ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

Thay thế 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; 
}

Thay thế CSS3: 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);
}

LƯU Ý: Hình ảnh trong một thùng chứa có display:nonethể không tải trước. Có lẽ khả năng hiển thị: ẩn sẽ hoạt động tốt hơn nhưng tôi chưa thử nghiệm điều này. Cảm ơn Marco Del Valle đã chỉ ra điều này


Cảm ơn mplungjan. Mặc dù nó không giúp tôi với trường hợp cụ thể này, nhưng thật tốt khi biết điều đó.
Francisc

5
Tôi có đúng không khi nói điều này sẽ làm chậm quá trình tải trang vì những hình ảnh này cần được tải xuống trước khi trang có thể khởi chạy sự kiện tải?
jakubiszon

3
Tôi không tin rằng những thứ này sẽ hoạt động trên tất cả các trình duyệt. Tôi biết rằng hình ảnh trên Chrome không được tải cho đến khi chúng hiển thị. Sẽ cần phải loại bỏ màn hình: không có; và thay vào đó hãy thử và định vị chúng để chúng không thể được nhìn thấy. Sau đó có thể ẩn với JS sau khi mọi thứ đã được tải nếu cần.
Bullyen

3
Hình nền trong một phần tử display: nonesẽ không tải trước.
Marquizzo 17/03/2016

1
Phương pháp này chỉ hiệu quả với tôi (1) tôi không sử dụng display: none, (2) không sử dụng width/height: 0, (3) đặt no-repeatvà một vị trí sao cho hình ảnh ở bên ngoài ( -<width>px -<height>pxsẽ đưa bạn đến đó, vì vậy nếu hình ảnh của bạn có 100px 0 -100pxNói cách khác, chiều cao hoặc ít hơn, bạn có thể sử dụng , nói cách khác url(...) no-repeat 0 -100px.
Alexis Wilke

11

Tôi khuyên bạn nên sử dụng thử / bắt để ngăn chặn một số vấn đề có thể xảy ra:

OOP:

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

Tiêu chuẩn:

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

Ngoài ra, trong khi tôi yêu DOM, các trình duyệt ngu ngốc cũ có thể có vấn đề với bạn khi sử dụng DOM, do đó, hãy tránh hoàn toàn IMHO trái với đóng góp của freedev. Image () có hỗ trợ tốt hơn trong các trình duyệt rác cũ.


7
Tôi không nghĩ rằng đây là cách bạn bắt lỗi khi tải Hình ảnh trong javascript - có một cái gì đó giống như _img.onerror có thể (nên?) Được sử dụng.
Greg0ry

3
"Các vấn đề có thể" là gì?
Kissaki

Các vấn đề như hỗ trợ cho lệnh. Kiểm tra trang web "tôi có thể sử dụng" để xem trình duyệt mà bạn được yêu cầu hỗ trợ có hỗ trợ cho lệnh javascript không.
Dave

10

Cách tiếp cận này là một chút công phu hơn. Ở đây bạn lưu trữ tất cả các hình ảnh được tải sẵn trong một container, có thể là một div. Và sau khi bạn có thể hiển thị hình ảnh hoặc di chuyển nó trong DOM đến đúng vị trí.

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

Hãy thử nó ở đây , tôi cũng đã thêm một vài bình luận


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

Có, điều này sẽ hoạt động, tuy nhiên trình duyệt sẽ giới hạn (trong khoảng 4-8) các cuộc gọi thực tế và do đó không lưu trữ / tải trước tất cả các hình ảnh mong muốn.

Cách tốt hơn để làm điều này là gọi onload trước khi sử dụng hình ảnh như vậy:

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

Bạn có thể vui lòng liên kết một số tài liệu tham khảo về giới hạn trình duyệt này?
nulll

Thật khó để tìm một tài liệu tham khảo (ít nhất là tôi chưa bao giờ tìm thấy một tài liệu tham khảo), nhưng tôi đã sử dụng dự án của riêng mình để kiểm tra điều này trong khi xem xét kỹ những gì được tải từ bộ nhớ cache và các cuộc gọi máy chủ bằng cách sử dụng tab mạng của Chrome trong các công cụ dành cho nhà phát triển Chrome (f12). Tôi đã tìm thấy hành vi tương tự trong Firefox và sử dụng điện thoại di động.
Robin

1
-1 bởi vì tôi đã kiểm tra cẩn thận yêu cầu trong câu đầu tiên ở đây và thấy nó là sai, ít nhất là trong các trình duyệt hiện đại. Xem stackoverflow.com/a/56012084/1709587. Có, có giới hạn về số lượng yêu cầu HTTP song song tối đa mà trình duyệt sẵn sàng gửi, nhưng cuối cùng nó cũng có thể yêu cầu mọi URL bạn gọi preloadImage, ngay cả khi bạn thực hiện theo cách được nêu trong câu hỏi.
Đánh dấu Amery

5

Đây là cách tiếp cận của tôi:

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

Giải pháp cho các trình duyệt tuân thủ ECMAScript 2017

Lưu ý: điều này cũng sẽ hoạt động nếu bạn đang sử dụng một bộ chuyển mã như 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();

Bạn cũng có thể đợi tất cả các hình ảnh được tải.

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

Hoặc sử dụng Promise.allđể tải chúng song song.

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

Thông tin thêm về Lời hứa .

Tìm hiểu thêm về các chức năng "Async" .

Thêm về nhiệm vụ phá hủy .

Thông tin thêm về ECMAScript 2015 .

Thông tin thêm về ECMAScript 2017 .


2

Tôi có thể xác nhận rằng cách tiếp cận trong câu hỏi là đủ để kích hoạt hình ảnh được tải xuống và lưu vào bộ nhớ cache (trừ khi bạn cấm trình duyệt thực hiện thông qua tiêu đề phản hồi của bạn), ít nhất là:

  • Chrome 74
  • Safari 12
  • Firefox 66
  • Cạnh 17

Để kiểm tra điều này, tôi đã tạo một ứng dụng web nhỏ với một số điểm cuối mà mỗi lần ngủ trong 10 giây trước khi phục vụ hình ảnh của một chú mèo con. Sau đó, tôi đã thêm hai trang web, một trong số đó chứa một <script>thẻ trong đó mỗi chú mèo con được tải sẵn bằng cách sử dụng preloadImagechức năng từ câu hỏi và khác trong đó bao gồm tất cả các chú mèo con trên trang sử dụng <img>thẻ.

Trong tất cả các trình duyệt ở trên, tôi thấy rằng nếu tôi truy cập trang tải trước trước, hãy đợi một lúc và sau đó đi đến trang có <img> thẻ, mèo con của tôi sẽ hiển thị ngay lập tức. Điều này chứng tỏ rằng trình tải trước đã tải thành công mèo con vào bộ đệm trong tất cả các trình duyệt được thử nghiệm.

Bạn có thể xem hoặc dùng thử ứng dụng tôi đã sử dụng để kiểm tra điều này tại https://github.com/ExplodingCabbage/preloadImage-test .

Đặc biệt lưu ý rằng kỹ thuật này hoạt động trong các trình duyệt ở trên ngay cả khi số lượng hình ảnh được lặp lại vượt quá số lượng yêu cầu song song mà trình duyệt sẵn sàng thực hiện tại một thời điểm, trái với câu trả lời của Robin . Tất nhiên tốc độ tải trước hình ảnh của bạn sẽ bị giới hạn bởi số lượng yêu cầu song song mà trình duyệt sẵn sàng gửi, nhưng cuối cùng nó sẽ yêu cầu mỗi URL hình ảnh bạn gọi preloadImage().


Có vấn đề cho dù hình ảnh được lưu trong bộ nhớ hoặc đĩa? có vẻ như phương thức OP được lưu trữ trên đĩa trái ngược với phương thức @clintgh được lưu trong bộ nhớ cache.
Chris L

1

Bạn có thể di chuyển mã này sang index.html để tải trước hình ảnh từ bất kỳ url nào

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

0

Trình duyệt sẽ hoạt động tốt nhất bằng cách sử dụng thẻ liên kết trong đầu.

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);
    });
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.