DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JavaScript前端圖片加載管理器imagepool使用詳解
JavaScript前端圖片加載管理器imagepool使用詳解
編輯:關於JavaScript     

前言

      imagepool是一款管理圖片加載的JS工具,通過imagepool可以控制圖片並發加載個數。

      對於圖片加載,最原始的方式就是直接寫個img標簽,比如:<img src="圖片url" />。

      經過不斷優化,出現了圖片延遲加載方案,這回圖片的URL不直接寫在src屬性中,而是寫在某個屬性中,比如:<img src="" data-src="圖片url" />。這樣浏覽器就不會自動加載圖片,等到一個恰當的時機需要加載了,則用js把data-src屬性中的url放到img標簽的src屬性中,或者讀出url後,用js去加載圖片,加載完成後再設置src屬性,顯示出圖片。

      這看起來已經控制的很好了,但依然會有問題。

      雖然能做到只加載一部分圖片,但這一部分圖片,仍然可能是一個比較大的數量級。

      這對於PC端來說,沒什麼大不了,但對於移動端,圖片並發加載數量過多,極有可能引起應用崩潰。

      因此我們迫切需要一種圖片緩沖機制,來控制圖片加載並發。類似於後端的數據庫連接池,既不會創建過多連接,又能充分復用每一個連接。

      至此,imagepool誕生了。

 拙劣的原理圖

 

使用說明

     首先要初始化連接池:

 var imagepool = initImagePool(5);
     initImagePool 是全局方法,任何地方都可以直接使用。作用是創建一個連接池,並且可以指定連接池的最大連接數,可選,默認為5。

     在同一個頁面中,多次調用initImagePool均返回同一個核心實例,永遠是第一個,有點單例的感覺。比如:

代碼如下:
 var imagepool1 = initImagePool(3);
 var imagepool2 = initImagePool(7);

     此時imagepool1和imagepool2的最大連接數均為3,內部使用的是同一個核心實例。注意,是內部的核心相同,並不是說imagepool1 === imagepool2。

     初始化之後,就可以放心大膽的加載圖片了。

     最簡單的調用方法如下:

代碼如下:
 var imagepool = initImagePool(10);
 imagepool.load("圖片url",{
     success: function(src){
         console.log("success:::::"+src);
     },
     error: function(src){
         console.log("error:::::"+src);
     }
 });

     直接在實例上調用load方法即可。

     load方法有兩個參數。第一個參數是需要加載的圖片url,第二個參數是各種選項,包含了成功、失敗的回調,回調時會傳入圖片url。

     這樣寫只能傳入一張圖片,因此,也可以寫成如下形式:

代碼如下:
 var imagepool = initImagePool(10);
 imagepool.load(["圖片1url","圖片2url"],{
     success: function(src){
         console.log("success:::::"+src);
     },
     error: function(src){
         console.log("error:::::"+src);
     }
 });

     通過傳入一個圖片url數組,就可以傳入多個圖片了。

     每一個圖片加載成功(或失敗),都會調用success(或error)方法,並且傳入對應的圖片url。

     但有時候我們並不需要這樣頻繁的回調,傳入一個圖片url數組,當這個數組中所有的圖片都處理完成後,再回調就可以了。

     只需加一個選項即可:

代碼如下:
 var imagepool = initImagePool(10);
 imagepool.load(["圖片1url ","圖片2url "],{
     success: function(sArray, eArray, count){
         console.log("sArray:::::"+sArray);
         console.log("eArray:::::"+eArray);
         console.log("count:::::"+count);
     },
     error: function(src){
         console.log("error:::::"+src);
     },
     once: true
 });

     通過在選項中加一個once屬性,並設置為true,即可實現只回調一次。

     這一次回調,必然回調success方法,此時error方法是被忽略的。

     此時回調success方法,不再是傳入一個圖片url參數,而是傳入三個參數,分別為:成功的url數組、失敗的url數組、總共處理的圖片個數。

     此外,還有一個方法可以獲取連接池內部狀態:

代碼如下:
 var imagepool = initImagePool(10);
 console.log(imagepool.info());

     通過調用info方法,可以得到當前時刻連接池內部狀態,數據結構如下:

     Object.task.count 連接池中等待處理的任務數量
     Object.thread.count 連接池最大連接數
     Object.thread.free 連接池空閒連接數
 
     建議不要頻繁調用此方法。

     最後需要說明的是,如果圖片加載失敗,最多會嘗試3次,如果最後還是加載失敗,才回調error方法。嘗試次數可在源碼中修改。

     最最後再強調一下,讀者可以盡情的往連接池中push圖片,完全不必擔心並發過多的問題,imagepool會有條不絮的幫你加載這些圖片。

     最最最後,必須說明的是,imagepool理論上不會降低圖片加載速度,只不過是平緩的加載。

源碼

代碼如下:
(function(exports){
    //單例
    var instance = null;
    var emptyFn = function(){};
    //初始默認配置
    var config_default = {
        //線程池"線程"數量
        thread: 5,
        //圖片加載失敗重試次數
        //重試2次,加上原有的一次,總共是3次
        "try": 2
    };
    //工具
    var _helpers = {
        //設置dom屬性
        setAttr: (function(){
            var img = new Image();
            //判斷浏覽器是否支持HTML5 dataset
            if(img.dataset){
                return function(dom, name, value){
                    dom.dataset[name] = value;
                    return value;
                };
            }else{
                return function(dom, name, value){
                    dom.setAttribute("data-"+name, value);
                    return value;
                };
            }
        }()),
        //獲取dom屬性
        getAttr: (function(){
            var img = new Image();
            //判斷浏覽器是否支持HTML5 dataset
            if(img.dataset){
                return function(dom, name){
                    return dom.dataset[name];
                };
            }else{
                return function(dom, name){
                    return dom.getAttribute("data-"+name);
                };
            }
        }())
    };
    /**
     * 構造方法
     * @param max 最大連接數。數值。
     */
    function ImagePool(max){
        //最大並發數量
        this.max = max || config_default.thread;
        this.linkHead = null;
        this.linkNode = null;
        //加載池
        //[{img: dom,free: true, node: node}]
        //node
        //{src: "", options: {success: "fn",error: "fn", once: true}, try: 0}
        this.pool = [];
    }
    /**
     * 初始化
     */
    ImagePool.prototype.initPool = function(){
        var i,img,obj,_s;
        _s = this;
        for(i = 0;i < this.max; i++){
            obj = {};
            img = new Image();
            _helpers.setAttr(img, "id", i);
            img.onload = function(){
                var id,src;
                //回調
                //_s.getNode(this).options.success.call(null, this.src);
                _s.notice(_s.getNode(this), "success", this.src);
                //處理任務
                _s.executeLink(this);
            };
            img.onerror = function(e){
                var node = _s.getNode(this);
                //判斷嘗試次數
                if(node.try < config_default.try){
                    node.try = node.try + 1;
                    //再次追加到任務鏈表末尾
                    _s.appendNode(_s.createNode(node.src, node.options, node.notice, node.group, node.try));
                }else{
                    //error回調
                    //node.options.error.call(null, this.src);
                    _s.notice(node, "error", this.src);
                }
                //處理任務
                _s.executeLink(this);
            };
            obj.img = img;
            obj.free = true;
            this.pool.push(obj);
        }
    };
    /**
     * 回調封裝
     * @param node 節點。對象。
     * @param status 狀態。字符串。可選值:success(成功)|error(失敗)
     * @param src 圖片路徑。字符串。
     */
    ImagePool.prototype.notice = function(node, status, src){
        node.notice(status, src);
    };
    /**
     * 處理鏈表任務
     * @param dom 圖像dom對象。對象。
     */
    ImagePool.prototype.executeLink = function(dom){
        //判斷鏈表是否存在節點
        if(this.linkHead){
            //加載下一個圖片
            this.setSrc(dom, this.linkHead);
            //去除鏈表頭
            this.shiftNode();
        }else{
            //設置自身狀態為空閒
            this.status(dom, true);
        }
    };
    /**
     * 獲取空閒"線程"
     */
    ImagePool.prototype.getFree = function(){
        var length,i;
        for(i = 0, length = this.pool.length; i < length; i++){
            if(this.pool[i].free){
                return this.pool[i];
            }
        }
        return null;
    };
    /**
     * 封裝src屬性設置
     * 因為改變src屬性相當於加載圖片,所以把操作封裝起來
     * @param dom 圖像dom對象。對象。
     * @param node 節點。對象。
     */
    ImagePool.prototype.setSrc = function(dom, node){
        //設置池中的"線程"為非空閒狀態
        this.status(dom, false);
        //關聯節點
        this.setNode(dom, node);
        //加載圖片
        dom.src = node.src;
    };
    /**
     * 更新池中的"線程"狀態
     * @param dom 圖像dom對象。對象。
     * @param status 狀態。布爾。可選值:true(空閒)|false(非空閒)
     */
    ImagePool.prototype.status = function(dom, status){
        var id = _helpers.getAttr(dom, "id");
        this.pool[id].free = status;
        //空閒狀態,清除關聯的節點
        if(status){
            this.pool[id].node = null;
        }
    };
    /**
     * 更新池中的"線程"的關聯節點
     * @param dom 圖像dom對象。對象。
     * @param node 節點。對象。
     */
    ImagePool.prototype.setNode = function(dom, node){
        var id = _helpers.getAttr(dom, "id");
        this.pool[id].node = node;
        return this.pool[id].node === node;
    };
    /**
     * 獲取池中的"線程"的關聯節點
     * @param dom 圖像dom對象。對象。
     */
    ImagePool.prototype.getNode = function(dom){
        var id = _helpers.getAttr(dom, "id");
        return this.pool[id].node;
    };
    /**
     * 對外接口,加載圖片
     * @param src 可以是src字符串,也可以是src字符串數組。
     * @param options 用戶自定義參數。包含:success回調、error回調、once標識。
     */
    ImagePool.prototype.load = function(src, options){
        var srcs = [],
            free = null,
            length = 0,
            i = 0,
            //只初始化一次回調策略
            notice = (function(){
                if(options.once){
                    return function(status, src){
                        var g = this.group,
                            o = this.options;
                        //記錄
                        g[status].push(src);
                        //判斷改組是否全部處理完成
                        if(g.success.length + g.error.length === g.count){
                            //異步
                            //實際上是作為另一個任務單獨執行,防止回調函數執行時間過長影響圖片加載速度
                            setTimeout(function(){
                                o.success.call(null, g.success, g.error, g.count);
                            },1);
                        }
                    };
                }else{
                    return function(status, src){
                        var o = this.options;
                        //直接回調
                        setTimeout(function(){
                            o[status].call(null, src);
                        },1);
                    };
                }
            }()),
            group = {
                count: 0,
                success: [],
                error: []
            },
            node = null;
        options = options || {};
        options.success = options.success || emptyFn;
        options.error = options.error || emptyFn;
        srcs = srcs.concat(src);
        //設置組元素個數
        group.count = srcs.length;
        //遍歷需要加載的圖片
        for(i = 0, length = srcs.length; i < length; i++){
            //創建節點
            node = this.createNode(srcs[i], options, notice, group);
            //判斷線程池是否有空閒
            free = this.getFree();
            if(free){
                //有空閒,則立即加載圖片
                this.setSrc(free.img, node);
            }else{
                //沒有空閒,將任務添加到鏈表
                this.appendNode(node);
            }
        }
    };
    /**
     * 獲取內部狀態信息
     * @returns {{}}
     */
    ImagePool.prototype.info = function(){
        var info = {},
            length = 0,
            i = 0,
            node = null;
        //線程
        info.thread = {};
        //線程總數量
        info.thread.count = this.pool.length;
        //空閒線程數量
        info.thread.free = 0;
        //任務
        info.task = {};
        //待處理任務數量
        info.task.count = 0;
        //獲取空閒"線程"數量
        for(i = 0, length = this.pool.length; i < length; i++){
            if(this.pool[i].free){
                info.thread.free = info.thread.free + 1;
            }
        }
        //獲取任務數量(任務鏈長度)
        node = this.linkHead;
        if(node){
            info.task.count = info.task.count + 1;
            while(node.next){
                info.task.count = info.task.count + 1;
                node = node.next;
            }
        }
        return info;
    };
    /**
     * 創建節點
     * @param src 圖片路徑。字符串。
     * @param options 用戶自定義參數。包含:success回調、error回調、once標識。
     * @param notice 回調策略。 函數。
     * @param group 組信息。對象。{count: 0, success: [], error: []}
     * @param tr 出錯重試次數。數值。默認為0。
     * @returns {{}}
     */
    ImagePool.prototype.createNode = function(src, options, notice, group, tr){
        var node = {};
        node.src = src;
        node.options = options;
        node.notice = notice;
        node.group = group;
        node.try = tr || 0;
        return node;
    };
    /**
     * 向任務鏈表末尾追加節點
     * @param node 節點。對象。
     */
    ImagePool.prototype.appendNode = function(node){
        //判斷鏈表是否為空
        if(!this.linkHead){
            this.linkHead = node;
            this.linkNode = node;
        }else{
            this.linkNode.next = node;
            this.linkNode = node;
        }
    };
    /**
     * 刪除鏈表頭
     */
    ImagePool.prototype.shiftNode = function(){
        //判斷鏈表是否存在節點
        if(this.linkHead){
            //修改鏈表頭
            this.linkHead = this.linkHead.next || null;
        }
    };
    /**
     * 導出對外接口
     * @param max 最大連接數。數值。
     * @returns {{load: Function, info: Function}}
     */
    exports.initImagePool = function(max){
        if(!instance){
            instance = new ImagePool(max);
            instance.initPool();
        }
        return {
            /**
             * 加載圖片
             */
            load: function(){
                instance.load.apply(instance, arguments);
            },
            /**
             * 內部信息
             * @returns {*|any|void}
             */
            info: function(){
                return instance.info.call(instance);
            }
        };
    };
}(this));

以上就是這款特別棒的javascript前端圖片加載管理器的使用方法示例,小伙伴們學會使用了嗎?

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved