前端圖片懶加載的原理與3種實現(xiàn)方式舉例
一. 圖片懶加載的目的
大型網(wǎng)站如常用的淘寶,京東等頁面,需要展示大量的商品圖片信息,如果打開網(wǎng)頁時讓所有圖片一次性加載完成,需要處理很多次網(wǎng)絡(luò)請求,等待加載時間比較長,用戶體驗感很差。
有一種常用的解決方式是:隨著滾動動態(tài)加載,即圖片的惰性加載。視圖之外的圖片默認(rèn)不加載,隨著頁面的滾動,圖片進(jìn)入了顯示的范圍,則觸發(fā)圖片的加載顯示。
優(yōu)點:頁面加載速度快,用戶體驗感更好且節(jié)省流量
二. 圖片懶加載的原理方法
1.初始化時,圖片標(biāo)簽的src不能是真實的圖片地址,也不可以是空地址或者壞地址(會出現(xiàn)圖片加載失敗的圖標(biāo))。
2.初始化的時候,可以設(shè)置圖片的src是某一個小型圖片。例如一張1px*1px的透明圖片。由于所有圖片都使用這一張圖片,只會發(fā)送一次請求,不會增加性能負(fù)擔(dān)。將圖片的真實路徑綁定給一個自定義屬性,例如data-url。注意:頁面的img元素,如果沒有src屬性,瀏覽器就不會發(fā)出請求去下載圖片
<img data-url="xxx" src="1px.gif" width="100" height="100"/>
3.定義滾動事件,判斷元素進(jìn)入視口,則將src替換為真正的url地址。利用js提取data-url的真實圖片地址賦值給src屬性
三. 圖片懶加載的實現(xiàn)方法
圖片懶加載的關(guān)鍵在于獲取元素的位置,并判斷其是否出現(xiàn)在視口。故有以下三種方式
- 滾動監(jiān)聽+scrollTop+offsetTop+innerHeight
- 滾動監(jiān)聽+getBoundingClientRect()
- intersectionObserve()
3.1 滾動監(jiān)聽+scrollTop+offsetTop+innerHeight
- scrollTop:指網(wǎng)頁元素被滾動條卷去的部分。
- offsetTop:元素相對父元素的位置
- innerHeight:當(dāng)前瀏覽器窗口的大小。需要注意兼容性問題。
- IE8及更早版本以前沒有提供取得瀏覽器窗口大小的屬性,不過提供了API:document.documentElement.clientHeight/clientWidth:返回元素內(nèi)容及其內(nèi)邊距所占據(jù)的空間大小。
- IE6中,上述屬性必須在標(biāo)準(zhǔn)模式才有效,如果是混雜模式,需要通過document.body.clientWidth 和 document.body. clientHeight 取得相同信息。
var pageWidth = window.innerWidth
var pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
//pageWidth的值不是數(shù)值,說明沒有innerwidth屬性
if (document.compatMode == "CSS1Compat"){ //標(biāo)準(zhǔn)模式
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else { //混雜模式
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
- 三個屬性之間的關(guān)系如圖所示,故當(dāng)scrollTop+innerHeight > offsetTop,即圖片在視口內(nèi),否則圖片在可視區(qū)域外。
代碼實現(xiàn)
滾動監(jiān)聽完成圖片懶加載的簡易版本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
margin-top:400px;
width: 250px;
display: block;
}
</style>
</head>
<body>
<img src="img/1pxImg.png" data-url="img/1.jpg">
<img src="img/1pxImg.png" data-url="img/2.jpg">
<img src="img/1pxImg.png" data-url="img/3.jpg">
<img src="img/1pxImg.png" data-url="img/4.jpg">
<img src="img/1pxImg.png" data-url="img/5.jpg">
<script>
var imgs = document.getElementsByTagName('img')
scrollFn()
// 監(jiān)聽滾動事件
window.onscroll = scrollFn
function scrollFn() {
var clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
console.log(clietH, scrollTop);
Array.from(imgs).forEach((item) =>{
let eleTop = item.offsetTop
// console.log(eleTop)
let count = scrollTop + clietH - eleTop
console.log(count)
// 可設(shè)置為>100 查看懶加載效果
if (count > 0) {
//從data-url中取出真實的圖片地址賦值給scr
item.setAttribute('src', item.getAttribute('data-url'))
}
})
}
</script>
</body>
</html>
3.2 滾動監(jiān)聽+getBoundingClientRect() getBoundingClientRect()
Element.getBoundingClientRect() 方法返回元素的大小及其相對于視口的位置。返回一個對象,對象屬性包括top,right
rectObject = object.getBoundingClientRect();
API返回一個對象,即rectObject為一個對象,其包含以下屬性
- rectObject.top:元素上邊到視窗上邊的距離;
- rectObject.right:元素右邊到視窗左邊的距離;
- rectObject.bottom:元素下邊到視窗上邊的距離;
- rectObject.left:元素左邊到視窗左邊的距離;
- rectObject.width:元素自身的寬度
- rectObject.height:元素自身的高度

故當(dāng)rectObject.top的值處于0-視口高度,則元素處于可視區(qū)。即
getBoundingClientRect(ele).top >= 0 && getBoundingClientRect(ele).top <= offsetHeight
代碼實現(xiàn)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
margin-top:400px;
width: 250px;
display: block;
}
</style>
</head>
<body>
<img src="img/1pxImg.png" data-url="img/1.jpg">
<img src="img/1pxImg.png" data-url="img/2.jpg">
<img src="img/1pxImg.png" data-url="img/3.jpg">
<img src="img/1pxImg.png" data-url="img/4.jpg">
<img src="img/1pxImg.png" data-url="img/5.jpg">
<script>
var imgs = document.getElementsByTagName('img')
scrollFn()
// 監(jiān)聽滾動事件
window.onscroll = scrollFn
function scrollFn() {
var clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
Array.from(imgs).forEach((item) =>{
let ele = item.getBoundingClientRect()
console.log(clietH,ele.top)
// 可以設(shè)置為ele.top+200 查看懶加載效果
if (ele.top > 0 && ele.top < clietH) {
//從data-url中取出真實的圖片地址賦值給scr
item.setAttribute('src', item.getAttribute('data-url'))
}
})
}
</script>
</body>
</html>
3.3 intersectionObserve() intersectionObserve()
新的API,針對元素的可見時間進(jìn)行監(jiān)聽。由于可見(visible)的本質(zhì)是,目標(biāo)元素與視口產(chǎn)生一個交叉區(qū),所以這個 API 叫做"交叉觀察器"。
var io = new IntersectionObserver(callback, option);
IntersectionObserver是瀏覽器原生提供的構(gòu)造函數(shù),接受兩個參數(shù):callback是可見性變化時的回調(diào)函數(shù),option是配置對象(該參數(shù)可選)。
構(gòu)造函數(shù)的返回值是一個觀察器實例。實例的observe方法可以指定觀察哪個 DOM 節(jié)點。
// 開始觀察
io.observe(document.getElementById('example'));
// 停止觀察
io.unobserve(element);
// 關(guān)閉觀察器
io.disconnect();
上面代碼中,observe的參數(shù)是一個 DOM 節(jié)點對象。如果要觀察多個節(jié)點,就要多次調(diào)用這個方法。
io.observe(elementA); io.observe(elementB);
callack參數(shù)
目標(biāo)元素的可見性變化時,就會調(diào)用觀察器的回調(diào)函數(shù)callback。
一般會觸發(fā)兩次:1.目標(biāo)元素剛剛進(jìn)入視口(開始可見),2.完全離開視口(開始不可見)。
callback函數(shù)的參數(shù)是一個數(shù)組,每個成員都是一個IntersectionObserverEntry對象。
IntersectionObserverEntry 對象
提供目標(biāo)元素的信息,一共有六個屬性。
- time:可見性發(fā)生變化的時間,是一個高精度時間戳,單位為毫秒
- target:被觀察的目標(biāo)元素,是一個 DOM 節(jié)點對象
- rootBounds:根元素的矩形區(qū)域的信息,getBoundingClientRect()方法的返回值,如果沒有根元素(即直接相對于視口滾動),則返回null
- boundingClientRect:目標(biāo)元素的矩形區(qū)域的信息
- intersectionRect:目標(biāo)元素與視口(或根元素)的交叉區(qū)域的信息
- intersectionRatio:目標(biāo)元素的可見比例,即intersectionRect占boundingClientRect的比例,完全可見時為1,完全不可見時小于等于0
所以可以通過判斷intersectionRatio屬性是否處于(0,1)來判斷元素的可見性
代碼實現(xiàn)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
margin-top:400px;
width: 250px;
display: block;
}
</style>
</head>
<body>
<img src="img/1pxImg.png" data-url="img/1.jpg">
<img src="img/1pxImg.png" data-url="img/2.jpg">
<img src="img/1pxImg.png" data-url="img/3.jpg">
<img src="img/1pxImg.png" data-url="img/4.jpg">
<img src="img/1pxImg.png" data-url="img/5.jpg">
<script>
var imgs = document.getElementsByTagName('img')
// 觀察器實例
let io = new IntersectionObserver((entires) =>{
entires.forEach(item => {
// 原圖片元素
let oImg = item.target
if (item.intersectionRatio > 0 && item.intersectionRatio <= 1) {
oImg.setAttribute('src', oImg.getAttribute('data-url'))
}
})
})
// 給每一個圖片設(shè)置觀察器
Array.from(imgs).forEach(element => {
io.observe(element)
});
</script>
</body>
</html>
總結(jié)
到此這篇關(guān)于前端圖片懶加載的原理與3種實現(xiàn)方式的文章就介紹到這了,更多相關(guān)前端圖片懶加載實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript Title、alt提示(Tips)實現(xiàn)源碼解讀
我們知道給某些HTML標(biāo)簽加上title屬性后,這個標(biāo)簽對象在瀏覽的時候,鼠標(biāo)移上去就會有一個小提示框出來,并顯示title定義的內(nèi)容。2010-12-12
解決包含在label標(biāo)簽下的checkbox在ie8及以下版本點擊事件無效果兼容的問題
這篇文章主要介紹了解決包含在label標(biāo)簽下的checkbox在ie8及以下版本點擊事件無效果兼容的問題,本文給大家總結(jié)的非常詳細(xì),需要的朋友可以參考下2019-10-10

