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


最近在做移動端的營銷頁面時,遇到了頁面有大量圖片的情況,於是很自然的想到了要使用圖片lazyload,PC端用着jQuery,也有現成的插件。
但是在移動端,基本不用jQuery,於是就試着自己去造一下輪子。
實現lazyload並不難,我很快就想到以下幾個步驟:

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

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

  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是相對最近的定位元素的,很容易就坑了。

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

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插件完成了輪子,代碼如下

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的屬性
    });

以上實現還沒有測試兼容,純屬交流,歡迎指正

 


免責聲明!

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



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