Javascript實現圖片的預加載功能

本文同步自我得博客:http://www.joeray61.comjavascript

最近要用javascript作一個動畫功能,爲了確保動畫在播放的時候可以順利和平滑,我須要對所用到的圖片素材進行預加載,下面跟你們分享一下我實現這個功能的過程java

單圖片預加載

目前最多見的一種實現方式以下數組

function preloadImg(url) {
    var img = new Image();
    img.src = url;
    if(img.complete) {
        //接下來可使用圖片了
        //do something here
    }
    else {
        img.onload = function() {
            //接下來可使用圖片了
            //do something here
        };
    }
}

首先實例化一個Image對象賦值給img,而後設置img.src爲參數url指定的圖片地址,接着判斷imgcomplete屬性,若是本地有這張圖片的緩存,則該值爲true,此時咱們能夠直接操做這張圖片,若是本地沒有緩存,則該值爲false,此時咱們須要監聽imgonload事件,把對img的操做放在onload的回調函數裏面,通過測試,這種方案基本可以兼容目前全部瀏覽器promise

多圖片預加載

不少場景下,單圖片預加載並不能知足咱們的需求,由於像動畫這種功能一般都會有不少的圖片素材,接下來咱們就在原來單圖片預加載的基礎上來改進咱們的函數瀏覽器

function preloadImg(list) {
    var imgs = arguments[1] || [],    //用於存儲預加載好的圖片資源
        fn = arguments.cal    lee;
    if(list.length == 0) {
        return imgs;
    }
    var img = new Image();
    img.src = list[0];
    if(img.complete) {
        imgs.push(img);
        list.shift();
        fn(list, imgs);
    }
    else {
        img.onload = function() {
            imgs.push(img);
            list.shift();
            fn(list, imgs);
        };
    }
}
var list = [......],    //此處省略一萬個字符
    imgs = preloadImg();

由於幀動畫可能須要保證每一幀動畫所用的圖片的順序,因此我在這段代碼中使用遞歸的方式,在上一張加載完成以後再去加載下一張圖片,每加載一張圖片,就把這張圖片資源存儲到imgs數組中,而且把這張圖片的地址從地址數組list中去掉,當list中已經沒有地址的時候跳出遞歸,並返回imgs數組
設想很美好,現實很殘酷,這段代碼有2個不能忍受的問題緩存

  1. 首先,我頗有可能拿不到最後返回的imgs數組,由於只要有圖片在本地沒有緩存,imgs的存儲操做都會放到onload的回調事件中,而事件監聽也屬於javascript中異步操做的一種,在綁定完onload事件的回調函數後,preloadImg函數就執行結束了,沒有任何返回值,外部imgs變量接收到的值爲undefined,只有在全部圖片都有本地緩存的狀況下,外部imgs變量才能順利拿到存儲了所有預加載圖片資源的數組閉包

  2. 在加載完一張圖片以後纔去加載下一張,整個預加載圖片的過程所須要的時間相對會比較長,用戶體驗會下降,並且原本異步操做具體速度快的特性,這樣的實現方式等於徹底棄置了onload異步的這個特性異步

多圖片預加載(改進版)

此次咱們直接把一個空數組做爲參數傳進函數,圖片所有存儲到這個數組裏面,下面是改進後的函數代碼(假設咱們可使用jQuery函數

function preloadImg(list,imgs) {
    var def = $.Deferred(),
        len = list.length;
    $(list).each(function(i,e) {
        var img = new Image();
        img.src = e;
        if(img.complete) {
            imgs[i] = img;
            len--;
            if(len == 0) {
                def.resolve();
            }
        }
        else {
            img.onload = (function(j) {
                return function() {
                    imgs[j] = img
                    len--;
                    if(len == 0) {
                        def.resolve();
                    }
                };
            })(i);
            img.onerror = function() {
                len--;
                console.log('fail to load image');
            };
        }
    });
    return def.promise();
}
var list = [......],    //此處省略一萬個字符
    imgs = [];
$.when(preloadImg(list, imgs)).done(
    function() {
        //預加載結束
        //do something here
    }
);

在分別給每個img綁定onload的回調函數時採用了閉包的方式,目的是爲了保存住當前的遞增變量i,要是不這麼作,結果將會是list地址中沒有本地緩存的圖片都存儲到imgs的最後一個元素上
此次每載入一張圖片,咱們並無把這張圖片的地址從list數組中去掉,這樣後續須要使用list數組的數據時就可以順利獲取到
在此次的代碼中,咱們引入了jQueryDeferred對象,這樣更方便我把握整個預加載圖片的過程,Deferred對象或者Promise對象的實現原理能夠參看個人這篇文章測試

thx for reading, hope u enjoy

相關文章
相關標籤/搜索