移動端圖片滾動加載-lazyload實現的要點總結

最近在作移動端的營銷頁面時,遇到了頁面有大量圖片的狀況,因而很天然的想到了要使用圖片lazyload,PC端用着jQuery,也有現成的插件。
可是在移動端,基本不用jQuery,因而就試着本身去造一下輪子。
實現lazyload並不難,我很快就想到如下幾個步驟:css

  1. 首先HTML中不直接寫圖片真實URL,而是用一個空圖代替,如<img class="lazy" src="images/nopic.png" data-original="images/test.jpg">
  2. 監聽滾動scroll事件
  3. 判斷圖片元素是否出如今屏幕中,若是是則替換src爲data-original裏的真實URL。

在這簡單的幾個步驟中,也有幾個細節要點須要注意:html

  1. 監聽事件是scroll,須要考慮是否應用函數節流(畢竟移動端,性能問題不能忘)
  2. 在移動端,水平X方向的滾動是很方便的,判斷圖片是否出現時,須要考慮水平方向。
  3. 滾動必定距離再刷新頁面時,頁面會有一次滾動,空圖佔據的大小若比真實圖片大,就會出如今這一次判斷中原本不會出現的圖,替換真實圖片重排後,高度減少,頂上來了。空圖若是比真實圖片小,就會加載還不該該出現的圖,lazyload的效果就打折扣了。PC可直接寫定px值,移動端須要使用類似比例的空圖。

其中上面要點2比較坑。瀏覽器

垂直Y方向直接監聽window的scroll便可,但水平X方向的多半是頁面內某個元素設定了必定區域並overflow:auto; 恰恰頁面內元素的scroll事件是不會冒泡的,事件的bubbles爲false,即不可冒泡函數

默認的頁面滾動事件是可冒泡的,由document開始觸發,冒泡到window,事件的bubbles爲true,便可冒泡,最坑的是獲取和設置scrollTop等值時,倒是使用 body 或 html 元素,水平有限,不理解爲何會是割裂開的。性能

對於這個問題,暫時沒想到好的解決方案,暫時用寫2次的方法解決,如lazyload(window),再lazyload('#container')監聽各自的事件測試

 

而判斷圖片是否出現時,剛開始我是使用elem.offsetTop對比scrollTop,後來發現offsetTop是相對最近的定位元素的,很容易就坑了。spa

一番尋覓後才發現 elem.getBoundingClientRect(),一番查資料後總結了這個方法的一些要點prototype

elem.getBoundingClientRect() 獲取 元素相對於瀏覽器窗口的距離,會受到滾動的影響,實時獲取,translate,scale等transform屬性也會影響結果
getBoundingClientRect是DOM元素到瀏覽器可視範圍的距離(不包含文檔卷起的部分)。
該函數返回一個Object對象,該對象有6個屬性:top,lef,right,bottom,width,height;
這裏的top、left和css中的理解很類似,width、height是元素自身的寬高,
可是right,bottom和css中的理解有點不同。right是指元素右邊界距窗口最左邊的距離,bottom是指元素下邊界距窗口最上面的距離。插件

 

踩完一些坑後,總算仿照PC端的jQuery插件完成了輪子,代碼以下code

function lazyload(options){
    var settings = {
        selector         : 'img.lazy',
        container       : window,
        threshold       : 0,
        failurelimit    : 0,
        dataAttribute  : "data-original",
    };
    if(typeof options == 'object'){
        for(var key in options){
            settings[key] = options[key] || settings[key];
        }
    }
    var tId = null;
    var imgsArr = Array.prototype.slice.call(document.querySelectorAll(settings.selector));
    function inViewport(elem, threshold){
        var o = elem.getBoundingClientRect();
        var pageWidth = document.documentElement.clientWidth;
        var pageHeight = document.documentElement.clientHeight;
        threshold = threshold || pageHeight/5;
        return  o.left < pageWidth + threshold && o.top < pageHeight + threshold
    }
    function loadImg(){
        clearTimeout(tId);
        tId = setTimeout(function(){
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            var src = '';
            var item = null;
            var counter = 0;
            if(imgsArr.length > 0){
                for (var i = 0;     i < imgsArr.length; i++) {
                    item = imgsArr[i];
                    if( inViewport(item) ){
                        src = item.getAttribute(settings.dataAttribute);
                        item.setAttribute('src', src);
                        imgsArr.splice(i,1);
                        i--;
                        counter = 0;
                    }else if( ++counter > settings.failurelimit ){
                        break;
                    }
                };
            }else {
                settings.container.removeEventListener('scroll',loadImg);
            }
        }, 100);
    }
    loadImg();
    settings.container.addEventListener('scroll',loadImg);
}

使用時和jQuery的lazyload插件相似,只是沒有了$,直接使用函數lazyload();默認配置以下

lazyload({
        selector         : 'img.lazy', // 選擇器
        container       : window,  // 容器
        threshold       : 0,  // 預留間距,即圖片距離屏幕還有必定距離就預先加載
        failurelimit    : 0,  // failurelimit,值爲數字.lazyload默認在找到第一張不在可見區域裏的圖片時則再也不繼續加載,但當HTML容器混亂的時候可能出現可見區域內圖片並沒加載出來的狀況,failurelimit意在加載N張可見區域外的圖片,以免出現這個問題.
        dataAttribute  : "data-original",  // 存放真實URL的屬性
    });

以上實現尚未測試兼容,純屬交流,歡迎指正

相關文章
相關標籤/搜索