【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互聯網是二十世紀人類最大的發明,互聯網是二十世紀人類最大的發明"} ]