前言
圖片延遲加載技術其實應該被用得很多了,令人汗顏的是我居然之前一直沒有用過,今天還是一個后端的大哥給提出來的,於是我便趁着中午休息的時間做了一下研究,這里提出來和大家討論一下。
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'));
演示地址
http://sandbox.runjs.cn/show/7vpjps1r
結語
中午不睡下午崩潰,我這里感覺去睡一會,若是您覺得這篇文章不錯請點一下推薦,若是您發現有何問題請一定提出來!
哎,沒有功勞也有苦勞,沒有苦勞我有疲勞哦,各位看在中午沒睡的情況下,頂下吧。。。。:)
不足與交流
呵呵,根據這個功能居然會有朋友關注,並提出了寶貴的意見,真的十分感謝,以下是朋友的見解:
通過閱讀原作中的代碼,可以看到,在上述幾個步驟中存在如下幾個問題:
- 用戶指定的圖片容器其實沒有用;
- 自動掃描全部被延遲加載的圖片信息(按照作者的意圖其實應該是僅掃描指定容器內的圖片),另外,圖片掃描的代碼可以寫的更簡練;
- 加載當前屏幕圖片時,計算某圖片是否在當前窗口的算法不夠嚴謹,僅當容器的高度小於當前瀏覽器窗口高度時算法有效,其他情況下均無效;
- 另外,我認為這里判斷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"));
});