【web前端優化之圖片延遲加載初探】中午不睡,下午崩潰


前言

圖片延遲加載技術其實應該被用得很多了,令人汗顏的是我居然之前一直沒有用過,今天還是一個后端的大哥給提出來的,於是我便趁着中午休息的時間做了一下研究,這里提出來和大家討論一下。

PS:小生初學,各位有問題可以提出來討論

延遲加載原理

延遲加載有多種實現,我選擇了其中一種:

為img標簽src設置統一的圖片鏈接,而將真實鏈接地址裝在自定義屬性中。
所以開始時候圖片是不會加載的,我們將滿足條件的圖片的src重置為自定義屬性便可實現延遲加載功能

當然還有其它的方案,里面我還比較青睞的就是將dom結構裝入textare,滿足條件時候將之載入,這里我便不討論了。

來看看我們用到的共用圖片:

問題

其實這種方案不能說沒有問題,因為我會遍歷所有的img標簽,相當於把整個頁面走了一次,萬一我有1000張圖片,我想效率會是很大的問題!

PS:頁面上出現1000張圖片這種事情我是不會讓其發生的,就算真的出現,也會有對應的解決之法,這里不扯遠了,於是我們開始今天的准備工作。

給我100張圖片

既然要做延遲加載,當然需要圖片了,所以給我100張圖片吧!!!

咳咳,其實我是一個程序員,所以我不會一張一張的搞的,下面是我的處理步驟:

① 在google圖片里面搜索圖片
② 將主要節點給拷貝出來
③ 在后面改寫代碼,將所有的img搞出來並裝入我們的節點
④ 批量操作節點

反正最后就成了這個樣子啦:

我們輕松加愉快的獲得了100張圖片(具體有沒有100張我沒有數。。。)

功能實現

代碼很簡單,我這里就直接給貼出來了,我們一起來看看吧:

function imgLazyLoad(container) {
    container = container || $(window); //需要時jquery對象
    var imgArr = {};

    initImg();
    lazyLoad();
    container.scroll(function () {
        lazyLoad();
    });
    $(window).resize(function () {
        initImg();
    });

    function initImg() {
        $('img').each(function () {
            var el = $(this);
            if (el.attr('lazy-src') && el.attr('lazy-src') != '') {
                var offset = el.offset();
                if (!imgArr[offset.top]) {
                    imgArr[offset.top] = [];
                }
                imgArr[offset.top].push(el);
            }
        });
    }

    function lazyLoad() {
        var height = container.height();
        var srollHeight = container.scrollTop();
        for (var k in imgArr) {
            if (parseInt(k) < srollHeight + height) {
                var _imgs = imgArr[k];
                for (var i = 0, len = _imgs.length; i < len; i++) {
                    var tmpImg = _imgs[i];
                    if (tmpImg.attr('lazy-src') && tmpImg.attr('lazy-src') != '') {
                        tmpImg.attr('src', tmpImg.attr('lazy-src'));
                        tmpImg.removeAttr('lazy-src');
                    }
                }
                delete imgArr[k];
            }
        }
    } //lazyLoad
}
imgLazyLoad($('#con'));

① 我們首先會給函數傳入一個容器,不傳的話默認就是window

② 然后我這里會初始化整個img,事實上就是遍歷了,我會把同時處於某一個的高度的圖片給他搞出來放到一起:

③ 當我們初始化結束后,我這里便定義了一個延遲加載的函數,他會取得當前的視圖高度與滾動條高度,然后遍歷我們的對象,將高度在他之上的圖片給顯示出來(這一步很關鍵哦

當元素圖片一旦加載的話便移除對象並且移除元素的lazy-src屬性,因為當我們窗口大小發生變化后,我們會重新計算圖片位置,計算中加載的元素不具備lazy-src屬性便忽略了。

以上我們的功能便結束了,我以為好像就這樣結束了,於是就結束了。。。。。。真的結束了嗎?

狗屁延遲加載

請思考以下場景,葉小釵現在正在看蒼老師的漫畫呢,葉小釵有好幾期沒看了呢,於是葉小釵猥瑣的打開了多個窗口慢慢等待圖片加載!自己就高高興興玩起游戲來。

半個小時后,葉小釵打開網頁一看,恩不錯所有網頁圖片都加載出來了呢!!

但是往下一番,尼瑪怎么下面的圖片沒有出來!!!我的蒼老師呢?換了幾個網頁都是這樣,所以葉小釵是否憎恨延遲加載!!!!

PS:以上場景只是笑話,不可能發生的,但是卻是我們需要考慮到的問題。

當頁面比較空閑的情況下,我們為什么要延遲加載呢?所以我們需要將以上代碼做一點改變,我是這樣想的:

若是頁面長時間沒有鼠標移動的話,我這里就繼續加載剩下圖片了,一旦鼠標運動,我這里就暫停一下

直接上代碼吧:

 1 function imgLazyLoad(container) {
 2 
 3     var imgLazyLoadTimer = null;
 4     var resetImglazy = null;
 5 
 6     container = container || $(window); //需要時jquery對象
 7     var imgArr = {};
 8     initImg();
 9     lazyLoad();
10     autoLoad();
11     container.scroll(function () {
12         lazyLoad();
13     });
14     $(window).resize(function () {
15         initImg();
16     });
17     $(document).mousemove(function () {
18         clearTimeout(imgLazyLoadTimer);
19         if (resetImglazy) clearTimeout(resetImglazy);
20         resetImglazy = setTimeout(function () {
21             autoLoad();
22         }, 5000);
23     });
24     function initImg() {
25         $('img').each(function () {
26             var el = $(this);
27             if (el.attr('lazy-src') && el.attr('lazy-src') != '') {
28                 var offset = el.offset();
29                 if (!imgArr[offset.top]) {
30                     imgArr[offset.top] = [];
31                 }
32                 imgArr[offset.top].push(el);
33             }
34         });
35     }
36 
37     function lazyLoad() {
38         var height = container.height();
39         var srollHeight = container.scrollTop();
40         for (var k in imgArr) {
41             if (parseInt(k) < srollHeight + height) {
42                 var _imgs = imgArr[k];
43                 for (var i = 0, len = _imgs.length; i < len; i++) {
44                     var tmpImg = _imgs[i];
45                     if (tmpImg.attr('lazy-src') && tmpImg.attr('lazy-src') != '') {
46                         tmpImg.attr('src', tmpImg.attr('lazy-src'));
47                         tmpImg.removeAttr('lazy-src');
48                     }
49                 }
50                 delete imgArr[k];
51             }
52         }
53     } //lazyLoad
54 
55     function autoLoad() {
56         var _key = null;
57         for (var k in imgArr) {
58             if (!_key) {
59                 _key = k;
60                 break;
61             }
62         }
           if(!_key) return false;
63
var _imgs = imgArr[_key]; 64 for (var i = 0, len = _imgs.length; i < len; i++) { 65 var tmpImg = _imgs[i]; 66 if (tmpImg.attr('lazy-src') && tmpImg.attr('lazy-src') != '') { 67 tmpImg.attr('src', tmpImg.attr('lazy-src')); 68 tmpImg.removeAttr('lazy-src'); 69 } 70 } 71 delete imgArr[_key]; 72 if (imgLazyLoadTimer) { 73 clearTimeout(imgLazyLoadTimer); 74 } 75 imgLazyLoadTimer = setTimeout(autoLoad, 1000); 76 } 77 } //imgLazyLoad 78 imgLazyLoad($('#con'));

具體我還沒怎么測試呢,各位看看吧,有問題請提出來哦

昨天有朋友提出有問題,我解決了下下面上新代碼:

function imgLazyLoad(container) {

    var imgLazyLoadTimer = null;
    var resetImglazy = null;

    container = container || $(window); //需要時jquery對象
    var imgArr = {};
    initImg();
    lazyLoad();
    imgLazyLoadTimer = setTimeout(autoLoad, 5000);

    container.scroll(function () {
        lazyLoad();
    });
    $(window).resize(function () {
        initImg();
    });
    $(document).mousemove(function () {
        if (imgLazyLoadTimer) clearTimeout(imgLazyLoadTimer);
        if (resetImglazy) clearTimeout(resetImglazy);
        resetImglazy = setTimeout(function () {
            autoLoad();
        }, 5000);
    });
    function initImg() {
        $('img').each(function () {
            var el = $(this);
            if (el.attr('lazy-src') && el.attr('lazy-src') != '') {
                var offset = el.offset();
                if (!imgArr[offset.top]) {
                    imgArr[offset.top] = [];
                }
                imgArr[offset.top].push(el);
            }
        });
    }

    function lazyLoad() {
        var height = container.height();
        var srollHeight = container.scrollTop();
        for (var k in imgArr) {
            if (parseInt(k) < srollHeight + height) {
                var _imgs = imgArr[k];
                for (var i = 0, len = _imgs.length; i < len; i++) {
                    var tmpImg = _imgs[i];
                    if (tmpImg.attr('lazy-src') && tmpImg.attr('lazy-src') != '') {
                        tmpImg.attr('src', tmpImg.attr('lazy-src'));
                        tmpImg.removeAttr('lazy-src');
                    }
                }
                delete imgArr[k];
            }
        }
    } //lazyLoad

    function autoLoad() {
        var _key = null;
        for (var k in imgArr) {
            if (!_key) {
                _key = k;
                break;
            }
        }
        if (!_key) return false;
        var _imgs = imgArr[_key];
        for (var i = 0, len = _imgs.length; i < len; i++) {
            var tmpImg = _imgs[i];
            if (tmpImg.attr('lazy-src') && tmpImg.attr('lazy-src') != '') {
                tmpImg.attr('src', tmpImg.attr('lazy-src'));
                tmpImg.removeAttr('lazy-src');
            }
        }
        delete imgArr[_key];
        if (imgLazyLoadTimer) {
            clearTimeout(imgLazyLoadTimer);
        }
        imgLazyLoadTimer = setTimeout(autoLoad, 1000);
    }
} //imgLazyLoad
imgLazyLoad($('#con'));
View Code

演示地址

http://sandbox.runjs.cn/show/7vpjps1r

 

結語

中午不睡下午崩潰,我這里感覺去睡一會,若是您覺得這篇文章不錯請點一下推薦,若是您發現有何問題請一定提出來!

哎,沒有功勞也有苦勞,沒有苦勞我有疲勞哦,各位看在中午沒睡的情況下,頂下吧。。。。:)

不足與交流

呵呵,根據這個功能居然會有朋友關注,並提出了寶貴的意見,真的十分感謝,以下是朋友的見解:

通過閱讀原作中的代碼,可以看到,在上述幾個步驟中存在如下幾個問題:

  1. 用戶指定的圖片容器其實沒有用;
  2. 自動掃描全部被延遲加載的圖片信息(按照作者的意圖其實應該是僅掃描指定容器內的圖片),另外,圖片掃描的代碼可以寫的更簡練;
  3. 加載當前屏幕圖片時,計算某圖片是否在當前窗口的算法不夠嚴謹,僅當容器的高度小於當前瀏覽器窗口高度時算法有效,其他情況下均無效;
  4. 另外,我認為這里判斷lazy-src是否為空幾乎沒有必要,因為實際應用中幾乎不可能出現,更可能出現的是地址錯誤,但是這種錯誤根本沒法去檢測出現;

針對上述四點問題,我只重點說一下第三點。

我們不妨做一個假設,假設所有的圖片不是在做着假設的id為con的div中,而是直接在body中,那么根據原來的算法,在計算某圖片是否屬於當前屏幕時,結果總是為true,此時延遲加載算法幾乎完全失效了
為什么呢?看看原作的代碼,如果圖片的容器是body,即整個document,那么代碼var height = container.height();得到的高度值就是整個頁面的高度,這個高度超過所有圖片的offset().top的高度,因此導致的結果就是,所有的圖片都被認為是在當前屏幕而被一次性加載,這跟不使用延遲加載有什么不同呢?

修改后的代碼:

//延遲加載圖片
var imageloadCurrentWindower =function(container){
//在空閑時循環加載剩余圖片工作的定時器
var autoLoaderTimer =null;
//在空閑時啟動加載剩余圖片工作的定時器
var restartAutoLoaderTimer =null;
//圖片容器
this.container = container || $(document);//容器默認為整個文檔
//所有圖片
this.allImages;
//初始化延遲加載的圖片的信息
this.scanLazyImageInfo =function(){
allImages
={};
this.container.find('img[lazy-src]').each(function(){
var el = $(this);
var offset = el.offset();
if(!allImages[offset.top]){
allImages
[offset.top]=[];
}
allImages
[offset.top].push(el);
});
}
//加載當前屏幕中被延遲加載的圖片
this.loadCurrentWindow =function(){
var h1,h2;
h1
=this.container.height();
h2
= $(window).height();
var height =this.container.scrollTop()+(h1 > h2 ? h2 : h1);
for(var k in allImages){
if(parseInt(k)< height){
loadImages
(allImages, k);
continue;
}
break;
}
}
//頁面空閑時自動加載未加載的圖片
this.autoLoad =function(){
// 取一行圖片加載
for(var k in allImages){
loadImages
(allImages, k);
break;
}
//取消上一個定時器
if(autoLoaderTimer){
clearTimeout
(autoLoaderTimer);
}
//重新生成定時器,1秒后如果頁面仍然空閑則加載下一行圖片
autoLoaderTimer
= setTimeout(autoLoad,1000);
}
//加載圖片
loadImages
=function(allImages, _key){
var _imgs = allImages[_key];
for(var i =0, len = _imgs.length; i < len; i++){
var tmpImg = _imgs[i];
tmpImg
.attr('src', tmpImg.attr('lazy-src'));
tmpImg
.removeAttr('lazy-src');
}
delete allImages[_key];
}
//掃描所有被延遲加載的圖片信息
this.scanLazyImageInfo();
//加載當前窗口中的圖片
this.loadCurrentWindow();
//監視當前容器的滾動條滾動事件
this.container.scroll(function(){
loadCurrentWindow
();
});
//監視窗口的尺寸改變事件
$
(window).resize(function(){
scanLazyImageInfo
();
loadCurrentWindow
();
});
//監視頁面是否空閑,以鼠標是否移動為標志
$
(document).mousemove(function(){
//清除空閑時自動加載圖片的定時器
clearTimeout
(autoLoaderTimer);
//如果存在清除重啟空閑時自動加載圖片的定時器
if(restartAutoLoaderTimer)
clearTimeout
(restartAutoLoaderTimer);
//重新生成重啟空閑時自動加載圖片的定時器
restartAutoLoaderTimer
= setTimeout(function(){
autoLoad
();
},5000);
});
//啟動自動加載當前屏幕外圖片
this.autoLoad();
}
//頁面下載完成后即刻開始延遲加載圖片工作
$
(document).ready(function(){
//啟動延遲加載,支持$(document)
imageloadCurrentWindower
($("#con"));
});

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM