­

【JavaScript】使用純JS實現多張圖片的懶載入Lazy(附源碼)

  • 2019 年 10 月 14 日
  • 筆記

一、效果圖如下

 

上面的效果圖,效果需求如下

        1、還沒載入圖片的時候,默認顯示載入圖片背景圖

        2、剛開始進入頁面,自動載入第一螢幕的圖片

        3、下拉介面,當一張圖片容器完全顯露出螢幕,即刻載入圖片,替換背景圖

        4、載入圖片的時候,有漸進顯示圖片效果

 

 

二、難點

        1)如何Ajax請求數據

        2)如何動態將json數據綁定到html中。

        3)如何通過對圖片的定位計算,觸發圖片懶載入機制

        4)加分項,顯示圖片時有漸現的過渡動畫

 

 

三、前期知識點

        1)Ajax相關知識,XMLHttpRequest對象,所有現代的瀏覽器都支援此對象。

        2)innerHTML,數據綁定使用字元串拼接的方式

        3)HTML DOM getAttribute() 方法,返回自定屬性名的屬性值(主要是用於返回自定義屬性的屬性值)

        4)圖片的 onload事件,當圖片的src屬性的屬性值為正確(即能成功載入圖片),才能觸發圖片的onload事件

 

 

四、難點逐一攻破

        1)如何Ajax請求數據

        分四步走

// 1)首先創建一個Ajax對象  var xhr = new XMLHttpRequest;  // 2)打開我們需要請求的數據的那個文件地址  // URL地址後面加隨機數目的:清除每一次請求數據時候(get請求)產生的快取  // 因為每次訪問的地址不一樣,樣瀏覽器就不會嘗試快取來自伺服器的響應,讀取本地快取的數據。  xhr.open('get', 'json/newsList.txt?' + Math.random(), false); // false代表同步   // 3)監聽請求的狀態  xhr.onreadystatechange = function () {      if (xhr.readyState === 4 && /^2d{2}$/.test(xhr.status)) {          var val = xhr.responseText;          jsonData = utils.jsonParse(val);      }  }  // 4)發送請求  xhr.send(null);

 

        2)如何動態將json數據綁定到html中。

        字元串拼接的方式(數據綁定中最常用的方式),即通過使用innerHTML,對頁面元素進行字元串拼接,再重新渲染到頁面中

var str = "";  if (jsonData) {      for (var i = 0, len = jsonData.length; i < len; i++) {          var curData = jsonData[i];          str += '<li>';          str += '<div><img src="" trueImg="' + curData["img"] + '"></div>';          str += '<div><h2>' + curData["title"] + '</h2>';          str += '<p>' + curData["desc"] + '</p>';          str += '</div>';          str += '</li></div>';      }      news.innerHTML += str;  }    

        優勢:數據綁定最常用的方式,因為瀏覽器只需要渲染一次(所有模板引擎數據綁定的原理就是字元串拼接,vue、angular、jade、kTemplate.js等等)
                   事先把內容拼接好,最後統一添加到頁面中,只引發一次迴流

        弊端:我們把新憑藉的字元串添加到#ul1中,原有的三個li的滑鼠滑過效果都消失了(原來標籤綁定的事件都消失了)
                   原來,oUl.innerHTML的作用是把原來的標籤以字元串的方式取出,原來作為標籤的時候,對應事件綁定的東西已經沒有了,然後進行字元串拼接,
                   但是,拼接完成之後,還是字元串!最後再把字元串統一添加到頁面中,瀏覽器還需要把字元串渲染成為對應的標籤

 

        3)如何通過對圖片的定位計算,觸發圖片懶載入機制(關鍵點

        思路:

                A:代表圖片距離螢幕頂部的距離 

//這裡使用了utils工具類中的offset方法,具體實現看下面源碼  var A = utils.offset(curImgPar).offsetTop + curImgPar.offsetHeight; 

                B:代表一螢幕距離+滾動條滾動距離

//這裡使用了utils工具類中的win方法,具體實現看下面源碼  var B = utils.win("clientHeight") + utils.win("scrollTop");

                A < B的時候,此時懶載入的默認圖片才能完整顯示出來,這個時候就需要觸發圖片懶載入

 

        4)載入圖片的時候,有漸進顯示圖片效果

        思路,利用window.setInterval 方法,通過對當前圖片的透明度屬性(curImg.style.opacity) 從透明0開始到透明度1,變化總時間為500ms即可

// ->實現漸現效果  function fadeIn(curImg) {      var duration = 500, // 總時間      interval = 10, //10ms走一次      target = 1; //總距離是1      var step = (target / duration) * interval; //每一步的步長      var timer = window.setInterval(function () {          var curOp =  utils.getCss2SS(curImg, "opacity");          if (curOp >= 1) {              curImg.style.opacity = 1;              window.clearInterval(timer);              return          }          curOp += step;          curImg.style.opacity = curOp;      }, interval);  }  

 

 

五、完整程式碼

        1)main.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <!--做移動端響應式布局頁面,都需要加下面的meta-->      <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!--meta:vp+tap一鍵生成-->      <title>多張圖片的延遲載入</title>      <style type="text/css">          * {              margin: 0;              padding: 0;              font-family: "Microsoft Sans Serif";              font-size: 14px;          }          ul, li {              list-style: none;          }          img {              display: block;              border: none;          }          .news {              padding: 10px;          }          .news li {              position: relative;              height: 60px;              padding: 10px 0;              border-bottom: 1px solid #eee;          }          .news li > div:first-child {   /*意思是,li下面的子div,中的第一個*/              position: absolute;              top: 10px;              left: 0;              width: 75px;              height: 60px;              background: url("./img/loading.PNG") no-repeat center center #e1e1e1;              background-size: 100% 100%;          }          /*移動端布局,最外層容器是不設置寬高的*/            .news li > div:first-child img {              display: none;              width: 100%;              height: 100%;              opacity: 0;  /*這裡設置為0的目的是,實現漸進的效果,後面的fadeIn函數,作用就是讓圖片透明都從0變成1*/          }            .news li > div:nth-child(2) {              height: 60px;              margin-left: 80px;          }          .news li > div:nth-child(2) h2 {              height: 20px;              line-height: 20px;              /*實現文字超出一行自動裁切*/              overflow: hidden;              text-overflow: ellipsis; /*超出部分省略號顯示*/              white-space: nowrap; /*強制不換行*/          }          .news li > div:nth-child(2) p {              line-height: 20px;              font-size: 12px;              color: #616161;          }      </style>  </head>  <body>      <ul id="news" class="news">          <!--<li>-->              <!--<div>-->                  <!--<img src="./img/new1.PNG" alt="">-->              <!--</div>-->              <!--<div>-->                  <!--<h2>香港四大家族往事,香港四大家族往事,香港四大家族往事</h2>-->                  <!--<p>香港四大家族往事:李嘉誠為鄭裕彤扶靈香港四大家族往事:李嘉誠為鄭裕彤扶靈</p>-->              <!--</div>-->          <!--</li>-->      </ul>          <script type="text/javascript" src="./tool/utils.js"></script>  <script type="text/javascript">      var news = document.getElementById("news"),          imgList = news.getElementsByTagName("img");        // 1、獲取需要綁定的數據(通過Ajax)      var jsonData = null;      ~function () {          // 1)首先創建一個Ajax對象          var xhr = new XMLHttpRequest;          // 2)打開我們需要請求的數據的那個文件地址          // URL地址後面加隨機數目的:清除每一次請求數據時候(get請求)產生的快取          // 因為每次訪問的地址不一樣,樣瀏覽器就不會嘗試快取來自伺服器的響應,讀取本地快取的數據。          xhr.open('get', 'json/newsList.txt?' + Math.random(), false); // false代表同步          // 3)監聽請求的狀態          xhr.onreadystatechange = function () {              if (xhr.readyState === 4 && /^2d{2}$/.test(xhr.status)) {                  var val = xhr.responseText;                  jsonData = utils.jsonParse(val);              }          }          // 4)發送請求          xhr.send(null);      }();      console.log(jsonData);        // 2、數據綁定(使用字元串拼接的方式)      ~function () {          var str = "";          if (jsonData) {              for (var i = 0, len = jsonData.length; i < len; i++) {                  var curData = jsonData[i];                  str += '<li>';                  str += '<div><img src="" trueImg="' + curData["img"] + '"></div>';                  str += '<div><h2>' + curData["title"] + '</h2>';                  str += '<p>' + curData["desc"] + '</p>';                  str += '</div>';                  str += '</li></div>';              }              news.innerHTML += str;          }      }();        // 3、圖片延遲載入      // ->首先實現單張圖片的延時載入      function lazyImg(curImg) {          var oImg = new Image;          oImg.src = curImg.getAttribute("trueImg");          oImg.onload = function() {              curImg.src = this.src;              curImg.style.display = "block";              fadeIn(curImg);              oImg = null;          }          curImg.isLoad = true;      }        // -> 循環處理每一張圖片      function handleAllImg() {          for (var i = 0, len = imgList.length; i < len; i++) {              var curImg = imgList[i];              if (curImg.isLoad) { // 當前圖片處理過的話,就不需重新進行處理                  continue;              }                // ->只有當A小於B的時候再進行處理  //          var A = utils.offset(curImg).top + curImg.offsetHeight; // 這裡A不能這麼計算,因為此時圖片是隱藏的,沒有圖片,他的offsetHeight當讓也是為0                                                                        // 如果我要的到圖片的A值,我們可以通過拿到他父節點的容器就行了,哈哈              var curImgPar = curImg.parentNode,                  A = utils.offset(curImgPar).offsetTop + curImgPar.offsetHeight,                  B = utils.win("clientHeight") + utils.win("scrollTop");              if (A < B) {                  lazyImg(curImg);              }          }      }        // ->實現漸現效果      function fadeIn(curImg) {          var duration = 500, // 總時間              interval = 10, //10ms走一次              target = 1; //總距離是1          var step = (target / duration) * interval; //每一步的步長          var timer = window.setInterval(function () {              var curOp =  utils.getCss2SS(curImg, "opacity");              if (curOp >= 1) {                    curImg.style.opacity = 1;                    window.clearInterval(timer);                    return              }              curOp += step;              curImg.style.opacity = curOp;          }, interval);      }        // 4、開始的時候(過500ms)載入1螢幕的圖片,當滾動條滾動的時候,載入其他圖片      window.setTimeout(handleAllImg, 500);      window.onscroll = handleAllImg;    </script>  </body>  </html>  

  

        2)utils.js

// 為了與全局變數衝突,我們使用單例模式  var utils = {    // jsonParse: 把JSON格式的字元串轉化為JSON格式的對象    jsonParse: function (str) {        var val = null;         try {            val = JSON.parse(str);        } catch (e) {            val = eval('(' + str + ')');        }        return val;    },      getCss2SS : function(curEle, attr) {        var val = null, reg = null;        if ('getComputedStyle' in window) {            val = window.getComputedStyle(curEle, null)[attr];        } else {            if (attr === 'opacity') {                val = curEle.currentStyle[attr]; // ->返回 alpha(opacity=10)                reg = /^alpha(opacity=(d+(?:.d+)?))$/i;  //  獲取10這個數字                val = reg.test(val)?reg.exec(val)[1]/100:1  // 超厲害,test與exec一起使用!!!            }            val = curEle.currentStyle[attr];        }        reg = /^-?d+(.d+)?(px|pt|rem|em)?$/i; //匹配的情況:純數值或者帶單位的數值        return reg.test(val) ? parseFloat(val) : val;    },      offset : function(curEle) {        var totalLeft = null,            totalTop = null,            par = curEle.offsetParent;        // 首先把自己本身的進行累加        totalLeft += curEle.offsetLeft;        totalTop += curEle.offsetTop;          while (par) {            if (navigator.userAgent.indexOf("MSIE 8.0") === -1) {                // 累加父級參照物邊框                totalTop += par.clientTop;                totalLeft += par.clientLeft;            }            // 累加父級參照物本身的偏移            totalTop += par.offsetTop;            totalLeft += par.offsetLeft;            par = par.offsetParent;        }        console.log('offsetTop: ' + totalTop + ', offsetLeft: ' + totalLeft);        var result = {};        result.offsetTop = totalTop;        result.offsetLeft = totalLeft;        return result;    },      win : function(attr, value) {        if (value === undefined) {            return document.documentElement[attr] || document.body[attr];        }        document.documentElement[attr] = value;        document.body[attr] = value;    }  };  

 

        3、json文件

[{"img":"./img/new1.PNG", "title": "1網路強國戰略與“十三五”十四大戰略", "desc": "1互聯網是二十世紀人類最大的發明,互聯網是二十世紀人類最大的發明"},   {"img":"./img/new2.PNG", "title": "2網路強國戰略與“十三五”十四大戰略", "desc": "2互聯網是二十世紀人類最大的發明,互聯網是二十世紀人類最大的發明"},   {"img":"./img/new3.PNG", "title": "3網路強國戰略與“十三五”十四大戰略", "desc": "3互聯網是二十世紀人類最大的發明,互聯網是二十世紀人類最大的發明"}  ]