1. 程式人生 > >【JavaScript】使用純JS實現多張圖片的懶載入(附原始碼)

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

一、效果圖如下

 

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

        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 && /^2\d{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;
}

 

        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 && /^2\d{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網際網路是二十世紀人類最大的發明,網際網路是二十世紀人類最大的發明"}
]

&n