注:文章最末尾有我的公衆號二維碼,會分享更多技術文章等,敬請關注javascript
不論是在移動端開發仍是在PC端開發,基本都會用到圖片的懶加載,這其中最大的緣由就是爲了保證頁面打開的速度(正常狀況下3秒以內若是首頁打不開,就已經算是死亡時間了)。css
延遲加載的的基本作法通常是這樣的:html
首屏內容中的圖片:首先給對應的區域一張默認的圖片佔着位置(默認圖須要很是的小,通常能夠維持在5kb之內),當首屏內容都加載完成後(或者也能夠給一個延遲的時間),再開始加載真實的圖片。前端
其餘屏中的圖片:也是給一張默認的圖片佔位,當滾動條滾動到對應區域的時候,咱們再開始加載真實的圖片。java
擴展:對於數據的異步加載,也是這樣的,並不會一次性把頁面中全部的數據都請求出來,正常狀況是先請求前兩屏的數據,後面的數據先不請求,當頁面滾動到對應區域的時候,再從新請求數據,接着綁定渲染數據。json
html瀏覽器
<div id="banner">
<!--trueImg是當前IMG標籤的自定義屬性,存儲的是真實圖片的地址-->
<img src="" trueImg="img/jd.jpg">
</div>複製代碼
css緩存
* {margin: 0; padding: 0; font-family: "\5FAE\8F6F\96C5\9ED1", Helvetica, sans-serif; font-size: 14px;}
img {display: block;border: none;}
#banner {margin: 10px auto; width: 350px; height: 200px; border: 1px solid green; background: url("img/default.gif") no-repeat center center #e1e1e1; }
#banner img {display: none; width: 100%;height: 100%;}複製代碼
在設置樣式的時候,把默認的圖片設置給img外層的div,做爲div的背景,由於要延遲加載,因此一開始給src的屬性值是空,這樣的話在IE瀏覽器當中會顯示一張碎圖,不美觀,因此咱們讓其默認是隱藏的,當真實的圖片加載完成後,再顯示。dom
JavaScript
在沒有想其餘狀況的時候,咱們可能會很簡單的就把JavaScript代碼寫成如下這樣:異步
var banner = document.getElementById('banner') , imgFir = banner.getElementsByTagName('img')[0];
imgFir.src = imgFir.getAttribute('trueImg');
imgFir.style.display = 'block';複製代碼
可是以上處理是不完整的:若是咱們獲取的真實圖片地址是錯誤的,當賦值給IMG的src屬性的時候,不只控制檯會報錯,並且頁面中會出現碎圖/叉子圖,影響視覺效果。因此咱們獲取圖片的地址後,應該先驗證地址的有效性,是有效的才賦值,不是有效的是不進行賦值處理的。
var oImg = new Image; // 建立一個臨時的img標籤
oImg.src = imgFir.getAttribute('trueImg');
oImg.onload = function () { // 當圖片可以正常加載
imgFir.src = this.src;
imgFir.style.display = 'block';
oImg = null;
}複製代碼
A:圖片容器距離body的上偏移 + 容器的offsetHeight
B:瀏覽器一屏幕的高度 + 滾動條捲去的高度
因此判斷的條件就是:A < B 容器就已經徹底的進入了視野,加載真實的圖片。
window.onscroll = function () {
if (banner.isLoad) { // 已經加載過了
return;
}
var A = banner.offsetHeight + utils.offset(banner).top;
var B = utils.win('clientHeight') + utils.win('scrollTop');
if (A < B) {
console.log('ok');
// 當條件成立,加載真實的圖片,第一次加載完成後,再讓頁面繼續滾動的過程當中,A<B一直成立,又從新的執行了下面的操做,致使了重複給一個容器中的圖片進行加載,因此有下面的isLoad自定義屬性
var oImg = new Image;
oImg.src = imgFir.getAttribute('trueImg');
oImg.onload = function () {
imgFir.src = this.src;
imgFir.style.display = 'block';
oImg = null;
};
// 設置一個自定義屬性,告訴瀏覽器我已經把圖片加載完了(不論是否正常的加載,只要處理過一次之後都再也不處理了)
banner.isLoad = true;
}
}複製代碼
本例中用到的utils工具類中的offset方法和win方法:
function offset(curEle) {
var disLeft = curEle.offsetLeft, disTop = curEle.offsetTop, par = curEle.offsetParent;
while (par) {
if (navigator.userAgent.indexOf("MSIE 8") === -1) {
disLeft += par.clientLeft;
disTop += par.clientTop;
}
disLeft += par.offsetLeft;
disTop += par.offsetTop;
par = par.offsetParent;
}
return {left: disLeft, top: disTop};
}
function win(attr, value) {
if (typeof value === "undefined") {
return document.documentElement[attr] || document.body[attr];
}
document.documentElement[attr] = value;
document.body[attr] = value;
}複製代碼
本例爲移動端的狀況,有一個列表,超過屏幕好幾屏
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>多張圖片的延遲加載</title>
<!--作移動端響應式佈局頁面,咱們必需要加META:viewport-->
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<style type="text/css">
* {margin: 0; padding: 0; font-family: "\5FAE\8F6F\96C5\9ED1", Helvetica, 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 dashed #ccc;
}
.news li > div:nth-child(1) {
position: absolute;
top: 10px;
left: 0;
width: 75px;
height: 60px;
background: url("img/default.jpg") no-repeat center center;
background-size: 100% 100%;
}
.news li > div:nth-child(1) img {
width: 100%;
height: 100%;
display: none;
}
.news li > div:nth-child(2) {
margin-left: 80px;
height: 60px;
}
.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 {
height: 20px;
line-height: 20px;
font-size: 12px;
color: darkgrey;
}
</style>
</head>
<body>
<ul class="news" id="news">
<!--<li>-->
<!--<div>-->
<!--<img src="" trueImg="img/1.jpg" alt="">-->
<!--</div>-->
<!--<div>-->
<!--<h2>AAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h2>-->
<!--<p>BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB</p>-->
<!--</div>-->
<!--</li>-->
</ul>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript">
var news = document.getElementById("news"), imgList = news.getElementsByTagName("img");
// 一、獲取須要綁定的數據(Ajax)->json/newsList.txt中的JSON格式的字符串
var jsonData = null;
~ function () {
var xhr = new XMLHttpRequest();
//URL地址後面加的隨機數是在清除每一次請求數據時候(GET請求)產生的緩存
xhr.open("get", "json/newsList.txt?_=" + Math.random(), false);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
var val = xhr.responseText;
jsonData = utils.formatJSON(val);
}
};
xhr.send(null);
}();
console.log(jsonData);
//二、數據綁定->把jsonData中存儲的數據綁定在頁面中(字符串拼接)
~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>';
str += '<h2>' + curData["title"] + '</h2>';
str += '<p>' + curData["desc"] + '</p>';
str += '</div>';
str += '</li>';
}
}
news.innerHTML = str;
}();
// 三、圖片延遲加載
//->我先編寫一個方法,實現單張圖片的延遲加載
function lazyImg(curImg) {
console.log('ok')
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 fadeIn(curImg) {
var duration = 500, interval = 10, target = 1;
var step = (target / duration) * interval;
var timer = window.setInterval(function () {
var curOp = utils.getCss(curImg, "opacity");
if (curOp >= 1) {
curImg.style.opacity = 1;
window.clearInterval(timer);
return;
}
curOp += step;
curImg.style.opacity = curOp;
}, interval);
}
//->循環處理每一張圖片
function handleAllImg() {
for (var i = 0, len = imgList.length; i < len; i++) {
var curImg = imgList[i];
//->當前的圖片處理過的話,就不須要在從新的進行處理了
if (curImg.isLoad) {
continue;
}
//->只有A<B的時候在進行處理:當前圖片是隱藏的,咱們計算的A的值實際上是計算它父親(容器)的值
var curImgPar = curImg.parentNode;
var A = utils.offset(curImgPar).top + curImgPar.offsetHeight, B = utils.win("clientHeight") + utils.win("scrollTop");
if (A < B) {
lazyImg(curImg);
}
}
}
//四、開始的時候(過500ms加載第一屏幕的圖片)、滾動條滾動的時候加載其它圖片
window.setTimeout(handleAllImg, 500);
window.onscroll = handleAllImg;
</script>
</body>
</html>複製代碼
我的公衆號(icemanFE):分享更多的前端技術和生活感悟