最近在給公司寫微商城,但是寫完後單頁的商品太多,導致圖片加載很慢,而且我發現公司給服務器域名限制了單個IP器訪問速度最高只有500K/s,面對這種情況,只好對圖片進行懶加載處理了。
接下來要給大家到來的是前端圖片懶加載的實現和思路,主要分兩種:縱向的和橫向的。
- 不管是縱向的還是橫向圖片懶加載實現的思路都是一樣
- 首先在第一次渲染列表圖片時,不要把圖片路徑直接賦值給src(不然的話整個頁面的圖片都會一起加載)
- 然後在img標籤中自定義一個屬性值,我這裏用
src-url
表示,然後將圖片路徑先賦值給src-url
(此時頁面並不會真正顯示圖片) - 然後監聽頁面滾動,判斷圖片外層div或者內層src標籤都可以,當其出現在頁面時將
src-url
的圖片路徑賦值給img標籤的src屬性(這時圖片會加載渲染) - 然後把多餘的自定義屬性值
src-url
給刪掉 - 這樣我們就控制了只有在設備窗口中出現的圖片才加載顯示的這麼一個效果
▼縱向圖片懶加載
GIF效果圖:
整個HTML
縱向的還是比較好實現的,直接監聽window層的onscroll就好了,獲取每一個圖片距離窗口頂部的距離也會很容易,也不用考慮多個滾動條的情況。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>縱向懶加載圖片</title>
</head>
<style>
body {
margin: 0;
}
.block .item {
width: 100vw;
height: 100vw;
}
.item img {
width: 100vw;
height: 100vw;
}
</style>
<body>
<main>
<div class="block" id="blick-id">
<!-- <div class="item">
<img src="" alt="">
</div> -->
</div>
</main>
</body>
<!-- <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> -->
<script>
var imgArr = ['http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3X8RoF22*mTXfJ3*RNuBlDKADJ1uOLA7MGzFp8BJJXnqeNhEXGt9UcyseKRjTRL3g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAxNbk8AOn2*a26dMBu9JTc5ZMjUoOP8CXSWklFFu3wNX1doPdvuRbT1XP*LngYIcYQ!!/b&bo=qAKoAqgCqAIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAycZuvsrYM89XrUpvXk.BK7uk48c7uH5PB.O54*yiHLP2DcqIniegFyShf51OvTodw!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA7XyxBYj1ChXgmpm2TPBJ9Qwg7Pg.b85HrbKUQMCkvQPZe8.CKMUzq4gadEk6ZLK7g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2FzxFGTwkwju8grAh*GGjv1BSvZN3Ob9ZKplkXdZtK0dp7dwORVtLeFlZYYpPVWLA!!/b&bo=0ALQAtAC0AIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3PQtUyBpTU*8PxS83qy6uNvksCAXfA*BjDhPE*k8S48m6I7.tvBznnNyMVpMUe2lA!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA1E320PXRp5qlcLil8TkwsUJIBXnUyjiBKUyIvQnmY87Gx3eBsgtJlhJ5m4P8G12ww!!/b&bo=NgE2ATYBNgERCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2mIh303WaCbhJsTAaw4ebe6WoKyO4SX*lp0lb3pwbXgfdMqCXj5VFP3D.dRrq9UZg!!/b&bo=9AH0AfQB9AERCT4!&rf=viewer_4',
]
//自定義字符串,拼接列表數據
var itemStr = '';
//自定義屬性值src-url,用於暫存圖片路徑
imgArr.forEach((e) => {
itemStr += `<div class="item">
<img src-url="${e}" alt="">
</div>`
})
//將拼接後的字符串渲染到列表中
document.getElementById('blick-id').innerHTML = itemStr;
window.onload = () => {
// 獲取圖片列表
var imgList = document.getElementsByClassName('block')[0].children;
// console.log(imgList);
// 初次渲染圖片
lazyLoad(imgList)
// 二次監聽滾動渲染圖片
window.onscroll = function () {
lazyLoad(imgList);
};
}
//封裝懶加載圖片方法
function lazyLoad(imgList) {
//獲取屏幕高度
var viewHeight = window.innerHeight;
// console.log(viewHeight)
//獲取縱向滾動的值
var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
// console.log(scrollHeight)
for (var i = 0; i < imgList.length; i++) {
// console.log(imgList[i].offsetTop)
//判斷,當圖片出現在頁面時,
//imgList[i].offsetTop表示圖片外層div距離設備窗口頂部的距離
if ((viewHeight + scrollHeight) >= imgList[i].offsetTop) {
//設置立即調用函數
(function (i) {
// 設置延時模擬圖片加載緩慢的情況,實際項目中去求就好
setTimeout(function () {
// console.log(imgList[0].children[0]);
// console.log(imgList[i].children[0].getAttribute('src'));
//當前圖片的節點
var imgNode = imgList[i].children[0];
//判斷,如果圖片的路徑是空的,就對其進行賦值,且刪除多餘的自定義屬性
if (imgNode.getAttribute('src') == null) {
imgNode.setAttribute('src', imgNode.getAttribute('src-url'));
imgNode.removeAttribute('src-url');
}
}, 1000)
})(i)
}
}
}
</script>
</html>
▼橫向圖片懶加載
GIF效果圖:
整個HTML
橫向的考慮因數會比較多,因爲一般橫向展示物品不單只是一橫排的,所以面對多人運動,呸,多排展示時,我們要分別對每一橫排的滾動進行處理。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>橫向懶加載圖片</title>
</head>
<style>
body {
margin: 0;
}
.module .title {
text-align: center;
background-color: orange;
color: white;
}
.block {
display: flex;
overflow-x: auto;
overflow-y: hidden;
}
.block .item {
display: flex;
width: 50vw;
height: 50vw;
}
.item img {
width: 50vw;
height: 50vw;
}
</style>
<body>
<main>
<div class="module" id="module-id">
<!-- <div class="row">
<div class="title"></div>
<div class="block">
<div class="item">
<img src="" alt="">
</div>
</div>
</div> -->
</div>
</main>
</body>
<!-- <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> -->
<script>
var imgObj = [
{
title: '板塊一',
imgArr: ['http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3X8RoF22*mTXfJ3*RNuBlDKADJ1uOLA7MGzFp8BJJXnqeNhEXGt9UcyseKRjTRL3g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAxNbk8AOn2*a26dMBu9JTc5ZMjUoOP8CXSWklFFu3wNX1doPdvuRbT1XP*LngYIcYQ!!/b&bo=qAKoAqgCqAIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAycZuvsrYM89XrUpvXk.BK7uk48c7uH5PB.O54*yiHLP2DcqIniegFyShf51OvTodw!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA7XyxBYj1ChXgmpm2TPBJ9Qwg7Pg.b85HrbKUQMCkvQPZe8.CKMUzq4gadEk6ZLK7g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2FzxFGTwkwju8grAh*GGjv1BSvZN3Ob9ZKplkXdZtK0dp7dwORVtLeFlZYYpPVWLA!!/b&bo=0ALQAtAC0AIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3PQtUyBpTU*8PxS83qy6uNvksCAXfA*BjDhPE*k8S48m6I7.tvBznnNyMVpMUe2lA!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA1E320PXRp5qlcLil8TkwsUJIBXnUyjiBKUyIvQnmY87Gx3eBsgtJlhJ5m4P8G12ww!!/b&bo=NgE2ATYBNgERCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2mIh303WaCbhJsTAaw4ebe6WoKyO4SX*lp0lb3pwbXgfdMqCXj5VFP3D.dRrq9UZg!!/b&bo=9AH0AfQB9AERCT4!&rf=viewer_4',
]
},
{
title: '板塊二',
imgArr: ['http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2mIh303WaCbhJsTAaw4ebe6WoKyO4SX*lp0lb3pwbXgfdMqCXj5VFP3D.dRrq9UZg!!/b&bo=9AH0AfQB9AERCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA1E320PXRp5qlcLil8TkwsUJIBXnUyjiBKUyIvQnmY87Gx3eBsgtJlhJ5m4P8G12ww!!/b&bo=NgE2ATYBNgERCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3PQtUyBpTU*8PxS83qy6uNvksCAXfA*BjDhPE*k8S48m6I7.tvBznnNyMVpMUe2lA!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2FzxFGTwkwju8grAh*GGjv1BSvZN3Ob9ZKplkXdZtK0dp7dwORVtLeFlZYYpPVWLA!!/b&bo=0ALQAtAC0AIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA7XyxBYj1ChXgmpm2TPBJ9Qwg7Pg.b85HrbKUQMCkvQPZe8.CKMUzq4gadEk6ZLK7g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAycZuvsrYM89XrUpvXk.BK7uk48c7uH5PB.O54*yiHLP2DcqIniegFyShf51OvTodw!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAxNbk8AOn2*a26dMBu9JTc5ZMjUoOP8CXSWklFFu3wNX1doPdvuRbT1XP*LngYIcYQ!!/b&bo=qAKoAqgCqAIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3X8RoF22*mTXfJ3*RNuBlDKADJ1uOLA7MGzFp8BJJXnqeNhEXGt9UcyseKRjTRL3g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
]
},
]
//自定義字符串,拼接列表數據
var moduleStr = '';
//自定義屬性值src-url,用於暫存圖片路徑
imgObj.forEach((element, index) => {
moduleStr +=
`<div class="row">
<div class="title">${element.title}</div>
<div class="block" id="block${index}">
${element.imgArr.map((e) => {
return `<div class="item">
<img src-url="${e}" alt="">
</div>`}).join('')}
</div>
</div>`
})
//將拼接後的字符串渲染到列表中
document.getElementById('module-id').innerHTML = moduleStr;
window.onload = () => {
//全部板塊的節點
var rowLists = document.getElementsByClassName(`module`)[0].children;
// console.log(rowLists)
lazyLoad(rowLists);
}
//封裝懶加載圖片方法
function lazyLoad(rowLists) {
//獲取可視高度(屏幕高度)
var viewWidth = window.innerWidth;
/*注意,如果這裏用var定義i,需要再包裹一層立即調用函數*/
for (let i = 0; i < imgObj.length; i++) {
//設置立即調用函數
// (function (i) {
// console.log(imgList)
//封裝加載每一個商品的方法
function lazyLoadItem(e) {
//滾動條距離窗口左側的距離
var scrollLeft = null;
//判斷,如果e等於空,說明是第一次渲染頁面
if (e == '') {
scrollLeft = 0;
} else {
scrollLeft = e.target.scrollLeft;
}
for (let n = 0; n < imgObj[i].imgArr.length; n++) {
// console.log(viewWidth + scrollLeft)
// console.log(viewWidth / 2 * n)
//每一個商品圖片外層div距離設備窗口左邊的距離
var offsetLeft = rowLists[i].children[1].children[n].offsetLeft;
// console.log(offsetLeft)
if (viewWidth + scrollLeft > offsetLeft) {
//設置立即調用函數
(function (n) {
// 設置延時模擬圖片加載緩慢的情況,實際項目中去求就好
setTimeout(function () {
// 當前商品圖片的路徑
var itemImg = rowLists[i].children[1].children[n].children[0];
// console.log(itemImg)
//判斷,如果圖片的路徑是空的,就對其進行賦值,且刪除多餘的自定義屬性
if (itemImg.src == '') {
//獲取真的圖片路徑
var goodsSrc = itemImg.getAttribute('src-url');
// 替換設置圖片
itemImg.setAttribute('src', goodsSrc);
//清理掉原來的屬性
itemImg.removeAttribute('src-url');
}
}, 1000)
})(n)
}
}
}
// 初次渲染圖片
lazyLoadItem('');
//監聽不同滑塊的滾動
document.getElementById(`block${i}`).addEventListener("scroll", function (e) {
// console.log(e)
// 二次渲染圖片
lazyLoadItem(e);
})
// })(i)
}
}
</script>
</html>
關於性能問題
細心的同學應該會發現一個問題,那就是每次監聽到滾動變化時都會執行lazyLoad懶加載方法,然而你滑動一次屏幕可能觸發了幾十次這個方法,所以非常消耗性能,可能會出現滑動時有卡頓的現象。我在網上找了一些滾動停止觸發的事例都不怎麼理想(貌似都是用setTimeout執行方法,每次監聽時都清理clearTimeout計時器,然後用scrollTop來判斷執行前後是否相等,相等說明停止了),最後我在菜鳥教程jQuery Mobile 滾屏事件中找到了不錯的解決方案,可以參考一下面這個例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>jquery.mobile優化版縱向懶加載圖片</title>
<!-- 引入jquery全家桶 -->
<link rel="stylesheet" href="https://apps.bdimg.com/libs/jquerymobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="https://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://apps.bdimg.com/libs/jquerymobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<style>
body {
margin: 0;
}
.block .item {
width: 100vw;
height: 100vw;
}
.item img {
width: 100vw;
height: 100vw;
}
</style>
<body>
<main>
<div class="block" id="blick-id"></div>
</main>
</body>
<script>
var imgArr = ['http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3X8RoF22*mTXfJ3*RNuBlDKADJ1uOLA7MGzFp8BJJXnqeNhEXGt9UcyseKRjTRL3g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAxNbk8AOn2*a26dMBu9JTc5ZMjUoOP8CXSWklFFu3wNX1doPdvuRbT1XP*LngYIcYQ!!/b&bo=qAKoAqgCqAIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBAycZuvsrYM89XrUpvXk.BK7uk48c7uH5PB.O54*yiHLP2DcqIniegFyShf51OvTodw!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA7XyxBYj1ChXgmpm2TPBJ9Qwg7Pg.b85HrbKUQMCkvQPZe8.CKMUzq4gadEk6ZLK7g!!/b&bo=LAEsASwBLAEDCSw!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2FzxFGTwkwju8grAh*GGjv1BSvZN3Ob9ZKplkXdZtK0dp7dwORVtLeFlZYYpPVWLA!!/b&bo=0ALQAtAC0AIRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA3PQtUyBpTU*8PxS83qy6uNvksCAXfA*BjDhPE*k8S48m6I7.tvBznnNyMVpMUe2lA!!/b&bo=IAMgAyADIAMRCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA1E320PXRp5qlcLil8TkwsUJIBXnUyjiBKUyIvQnmY87Gx3eBsgtJlhJ5m4P8G12ww!!/b&bo=NgE2ATYBNgERCT4!&rf=viewer_4',
'http://photogz.photo.store.qq.com/psc?/V14ZaBeY27gVgy/BbSbWrmEw8nJ9LYcvxlBA2mIh303WaCbhJsTAaw4ebe6WoKyO4SX*lp0lb3pwbXgfdMqCXj5VFP3D.dRrq9UZg!!/b&bo=9AH0AfQB9AERCT4!&rf=viewer_4',
]
var itemStr = '';
imgArr.forEach((e) => {
itemStr += `<div class="item">
<img src-url="${e}" alt="">
</div>`
})
document.getElementById('blick-id').innerHTML = itemStr;
window.onload = () => {
var imgList = document.getElementsByClassName('block')[0].children;
lazyLoad(imgList)
//在這裏用jquery.mobile的scrollstop處理監聽結束
$(document).on("scrollstop", function () {
// alert("停止滾動!");
console.log("停止滾動!")
lazyLoad(imgList);
});
}
function lazyLoad(imgList) {
var viewHeight = window.innerHeight;
var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
for (var i = 0; i < imgList.length; i++) {
if ((viewHeight + scrollHeight) >= imgList[i].offsetTop) {
(function (i) {
setTimeout(function () {
var imgNode = imgList[i].children[0];
if (imgNode.getAttribute('src') == null) {
imgNode.setAttribute('src', imgNode.getAttribute('src-url'));
imgNode.removeAttribute('src-url');
}
}, 1000)
})(i)
}
}
}
</script>
</html>
最後呢,其實在for循環輸出列表時可以先賦值一張僞圖(也就是加載圖片中gif類型的輕量級圖片)給img標籤的src,然後
在setAttribute圖片替換這一步中用onload監聽真的圖片加載完了再把url賦值給src,這樣就不會產生圖片一段一段加載的感覺,這個看個人的項目需求吧。加油同志們,奧利給!
奧利給釋義:
作爲感嘆詞,包含了讚美、加油打氣等多種感情色彩。
白嫖罰多人運動,請自重
。
。
。
。
。
。
。
。
。
。
不要誤會