1. 程式人生 > >ImagesLazyLoad 圖片延遲載入效果

ImagesLazyLoad 圖片延遲載入效果

之前在做一個圖片瀏覽效果時,要看後面的小圖必須等到前面的載入完,而且大圖的位置是在大量的小圖後面,導致大圖要等到小圖都載入完才能顯示,為了解決這個問題,就想到了Lazyload效果。
現在很多網站都用了類似的效果,如淘寶、Bing等。
這個圖片延遲載入效果是在Lazyload的基礎上擴充套件的,主要擴充套件了獲取img元素,獲取src和圖片載入的部分。

相容:ie6/7/8, firefox 3.5.5, opera 10.10, safari 4.0.4, chrome 3.0
其中safari和chrome部分功能不支援。


效果預覽

圖片延遲載入:共有圖片  張,未載入 0張   
圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳
圖片上傳預覽
多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳 圖片上傳預覽 多級聯動選單 浮動定位提示 資料延遲載入 簡便檔案上傳

程式說明

【獲取圖片】

先定義filter函式作為篩選程式:

程式碼


然後用這個filter函式篩選出需要的圖片集合:

this._elems = $$A.filter(
        opt.images 
|| container.getElementsByTagName("img"), filter
    );

如果要自定義圖片集合可以在程式可選引數的images屬性來設定,否則自動從容器獲取img元素作為圖片集合。

這裡的filter其實是包裝了篩選樣式cls、獲取src的方法getSrc和佔位圖holder三個引數的_filter篩選程式。
在_filter程式中,會對圖片集合進行篩選和整理。
如果自定義了"class"篩選樣式,會自動排除樣式不對應的圖片:

if ( className && (""+ img.className +"").indexOf(""+ className +""==-1 ) returnfalse;


再用getSrc獲取原圖地址,即實際要顯示的圖片地址。
如果有自定義getSrc會優先使用。
沒有的話,再通過儲存原圖地址的_attribute自定義屬性從元素獲取。
最後才直接從元素的src屬性獲取。

接著排除src不存在的:

if ( !src ) returnfalse;


要注意處理原圖地址就是元素當前src的情況:

if ( src == img.src ) {
    
if ( img.complete || $$B.chrome || $$B.safari ) returnfalse;
    img.removeAttribute(
"src");
}

如果complete為true,說明圖片已經載入完成了,可以排除;
如果是chrome或safari,不能取消當前載入,所以也排除掉(具體看圖片的HTTP請求部分)。
否則,用removeAttribute移除src屬性來取消圖片當前的載入。

如果設定了holder佔位圖,就重新設定圖片src:

if ( holder ) { img.src = holder; }


最後把原圖地址記錄到元素的_attribute自定義屬性中:

img.setAttribute( this._attribute, src );


逐個圖片元素篩選整理後,就得到要載入的圖片集合了。


【圖片載入】

ImagesLazyLoad相比LazyLoad,已經實現了_onLoadData載入程式,不需要再自己定義載入。
在_onLoadData程式中,主要是用來顯示圖片。

先用_hasAttribute方法判斷是否有_attribute自定義屬性。
在_hasAttribute方法中是這樣判斷的:

this._hasAttribute = $$B.ie6 || $$B.ie7
    
?function(img){ return attribute in img; }
    : 
function(img){ return img.hasAttribute( attribute ); };

由於ie6/7跟其他瀏覽器對attribute和property的理解不同,所以要分開處理,詳細參考這裡的attribute/property
為了保證相容性,程式會優先使用attribute的方式來操作自定義屬性。

當img有_attribute自定義屬性時,就用getAttribute來獲取原圖地址,並設定img的src,在用removeAttribute來移除自定義屬性。
移除的意義在於,當有多個例項使用同一個元素時,能保證圖片載入一次後就不會重複載入,即防止例項間的衝突。


【圖片的HTTP請求】

這裡說說開發過程中發現的一些關於圖片載入的問題。

首先是載入空字串的問題,如果給img的src設為空字串的話,可能會得到意料之外的結果。
例如在 http://xxx/test.htm 裡面的 <img src=""> 會發生以下情況:
ie 會產生相對地址的請求,即:http://xxx/
Safari/Chrome 會產生當前頁面地址的請求,即:http://xxx/test.htm
Opera/Firefox 不會產生請求
詳細參考Nicholas C. Zakas的“Empty image src can destroy your site”。
如果不想載入圖片,不應該把src設為空值,因為還可能會發出請求,浪費資源。
可以像程式那樣,通過removeAttribute來移除就行了。

還有一個問題是在Safari和Chrome,由於webkit核心的bug,正在載入的圖片並不能取消載入。
所以程式在取消圖片載入的部分,如果是Safari或Chrome會繼續載入,不進行延遲。
這個問題最初從lifesinger的datalazyload的說明部分看到的,具體可以自己用Fiddler來測試。

更多相關資料可以參考lifesinger的“圖片的HTTP請求”。


【繼承結構】

在釋出的程式中,這是第一個用了繼承的,本人平時也沒怎麼用到,所以還不成熟,算是試試水吧。
程式用wrapper來做繼承,詳細參考工具庫的說明
先用wrapper給ImagesLazyLoad包裝(繼承)LazyLoad:

var ImagesLazyLoad = $$.wrapper(function(options) {
    ...
}, LazyLoad);


再用extend擴充套件prototype,新增子類的方法函式:

$$.extend( ImagesLazyLoad.prototype, {
  ...
});


其中_initialize方法用來設定子類屬性,由於覆蓋了父類的同名方法,所以要通過LazyLoad.prototype._initialize來呼叫,還要注意用call來修正this。

還有_setOptions方法用來設定子類的可選屬性:

return LazyLoad.prototype._setOptions.call(this, $$.extend({
    ...
}, $$.extend( options, {
    onLoadData:    
this._onLoadData
})));

子類的_setOptions方法也覆蓋了父類的方法,解決方法同_initialize。
其中第一個引數是子類的可選屬性,第二個引數是子類定義的屬性,即不再是可選而是由程式來定義的屬性。

總體來說,這是個簡陋的繼承,等以後積累了一定經驗再來擴充套件吧。


使用技巧

【設定src】

有幾個方法可以設定原圖地址:
1,正常設定src:漸進增強,不支援js時也能顯示,但chrome和safari有bug,不支援這種方式;
2,把原圖地址設定到自定義屬性中:所有瀏覽器都相容,但在不支援js時圖片不能顯示;
3,用自定義函式獲取:使用在比較複雜的情況,需要手動設定。
具體還是要根據實際情況來選擇。

【設定holder】

如果使用了holder佔位圖,程式會自動設定圖片元素顯示佔位圖。
推薦使用loading圖片來設定,但loading圖往往跟原圖的尺寸是不同的。
如果img設定了原圖寬高,又想保持loading圖的尺寸,把它設為背景就可以了。
但這樣在ie下,不設定src預設會有一個小圖示。
要去掉這個小圖示可以設定holder為一個透明圖片的連結,或者參考這裡的TRANSPARENT“做”一個透明圖片。
例項中也是這樣設定的,可以參考一下。

【執行程式】

千萬不能在window.onload中執行,因為那時圖片都已經載入完了。
而應該在容器後面(window的話是文件結尾)或DOMContentLoaded中執行。


程式原始碼 

程式碼