JavaScript實現(xiàn)圖片懶加載的三種常用方法總結(jié)
1.前言
1.1 什么是使用圖片懶加載
懶加載是一種對網(wǎng)頁性能優(yōu)化的方式,比如當訪問一個網(wǎng)頁的時候,優(yōu)先顯示可視區(qū)域的圖片而不是一次加載全部的圖片,當需要顯示時,再發(fā)送請求加載圖片。
1.2 為什么使用圖片懶加載
- 避免首次加載時消耗大量時間,降低頁面渲染速度,造成卡頓現(xiàn)象。
- 按需加載,避免無效圖片的加載,減輕服務器壓力,節(jié)約網(wǎng)絡資源。
若不使用圖片懶加載,頁面啟動時,會加載全部的圖片資源:
1.3 圖片懶加載的實現(xiàn)原理
1 基本原理: 監(jiān)聽圖片是否位于頁面的可視區(qū)域內(nèi),若在則加載圖片,不在則不加載圖片
2 實現(xiàn)方案: 自定義屬性-將圖片真實地址 url 存儲在自定義屬性中,當監(jiān)聽到圖片進入可視區(qū)域時,將自定義屬性值賦值給 img 的 src 屬性
2.實現(xiàn)方法
2.1 利用元素的 getBoundingClientRect 方法實現(xiàn)
(1)屬性介紹:
利用.getBoundingClientRect
實時獲取物體的動態(tài)位置
(2)實現(xiàn)方法:
步驟 1:監(jiān)聽頁面滾動事件,lazyLoad
為頁面滾動時的處理函數(shù),在本節(jié)為處理圖片懶加載。
window.addEventListener('scroll', lazyLoad)
步驟 2:判斷圖片是否處于可視區(qū)域內(nèi)
(1)若距離頂部top
小于頁面的整體高度window.innerHeight
(2)若距離左側(cè)left
小于頁面的整體寬度window.innerWidth
(3)同時圖片的底部bottom
與圖片的右側(cè)right
距頁面頂部、左側(cè)的距離均大于0
則說明該圖在屏幕的可視區(qū)域內(nèi)。
為了提高復用性,我們可以將它封裝成一個自定義函數(shù)isVisible
,將每張圖片作為參數(shù)傳入該函數(shù),并返回true
或false
// 可視區(qū)域判斷函數(shù) function isVisible(img) { // 判斷是否在可視區(qū)域,并返回true或false const imgRect = img.getBoundingClientRect() // getBoundingClientRect 獲取圖片的動態(tài)信息 return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth }
步驟 3:定義圖片懶加載時的處理事件,監(jiān)聽所有的img
,判斷該img
是否處于可視范圍內(nèi)
querySelectorAll
獲取的元素為偽數(shù)組 需要轉(zhuǎn)為真數(shù)組,否則無法使用數(shù)組的某些方法
// 獲取所有的img元素,并利用擴展運算符轉(zhuǎn)為真數(shù)組 const images = [...document.querySelectorAll('img')]
步驟 4:對每張圖片進行監(jiān)聽,利用自定義函數(shù)isVisible
判斷是否在可視區(qū)域內(nèi)
(1)若處于可視區(qū)域:將自定義的data-src
值,賦值給真正的src
屬性值,其中 data-src
存儲圖片的URL
地址,并刪除該元素防止重復加載
(2)若不處于可視區(qū)域:return 不做處理
// 利用循環(huán)判斷每張圖片是否屬于可視區(qū)域 function lazyLoad(){ for (let i = 0; i < images.length; i++) { // isVisible是否該圖片位于可視區(qū)域 返回true 或false if (isVisible(images[i])) { // 將元素的自定義屬性 data-src 賦值給元素的 src 屬性 // 等價于:img.setAttribute('src', img.getAttribute('data-src')) images[i].src = images[i].dataset.src // 防止重復被遍歷 加載完之后 刪除元素不再加載 images.splice(i, 1) i-- } } } lazyLoad()
(3)整體代碼:
// html 標簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認.jpg" alt="">
// 1 獲取全部圖片的DOM節(jié)點 // 注意:querySelectorAll 值為偽數(shù)組利用擴展運算符轉(zhuǎn)為真數(shù)組 const images = [...document.querySelectorAll('img')] // 2 監(jiān)聽頁面滾動事件 window.addEventListener('scroll', lazyLoad) // 3 定義頁面滾動的處理函數(shù) function lazyLoad(){ for (let i = 0; i < images.length; i++) { // isVisible是否該圖片位于可視區(qū)域 返回true 或false if (isVisible(images[i])) { // 將元素的自定義屬性 data-src 賦值給元素的 src 屬性 // dataset.src 此為元素的自定義屬性 data-src images[i].src = images[i].dataset.src // 等價于:img.setAttribute('src', img.getAttribute('data-src')) // 防止重復被遍歷 加載完之后 刪除元素不再加載 images.splice(i, 1) i-- } } } lazyLoad() // 4 可視區(qū)域判斷函數(shù) function isVisible(img) { // 判斷是否在可視區(qū)域 const imgRect = img.getBoundingClientRect() // getBoundingClientRect 獲取圖片的動態(tài)信息 return imgRect.bottom > 0 && imgRect.top < window.innerHeight && imgRect.right > 0 && imgRect.left < window.innerWidth }
2.2 利用整體距離實現(xiàn)
(1)屬性介紹:
clientHeight : 網(wǎng)頁可見區(qū)域高
- A 表示可見區(qū)域的高度,包含
padding
不包含border
和margin
- B 語法:
element.clientHeight
- C 備注:
body.clientHeight
=window.innerHeight
innertHeight : window 整體高度
- A 表示
window
的內(nèi)部高度,包括縱向滾動條 - B 語法:
window.innertHeight
offsetTop : 距離父級元素頂部的高度
- A 表示當前元素相對于其
offsetParent
元素的頂部內(nèi)邊距的距離 - B 語法:
element.offsetTop
scrollTop : 網(wǎng)頁被卷去的距離
- A 表示在有滾動條時,滾動條向下滾動的距離也就是元素頂部被遮住部分的高度
- B 語法:
element.scrollTop
(2)實現(xiàn)方法:
可以用image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop
判斷圖片是否可以在可視區(qū)域內(nèi)。
- 圖片元素位置的頂部距離:
offsetTop
- 滾動距離的最下端:
scrollTop+clientHeight
// html 標簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認.jpg" alt="">
// 1 獲取全部圖片的DOM節(jié)點 // 注意:querySelectorAll 值為偽數(shù)組利用擴展運算符轉(zhuǎn)為真數(shù)組 const images = [...document.querySelectorAll('img')] // 2 監(jiān)聽頁面滾動事件 window.addEventListener('scroll', lazyLoad) // 3 定義頁面滾動的處理函數(shù) function lazyload(e){ // 3.1 獲取屏幕的可視高度 const clientHeight = document.documentElement.clientHeight // 3.2 獲取屏幕的滾動距離 const scrollTop = document.documentElement.scrollTop for (let i = 0; i < images.length; i++) { if (images[i].offsetTop < clientHeight + scrollTop) { images[i].setAttribute('src', images[i].getAttribute('data-src')) } } }
2.3 利用Intersection Observer實現(xiàn)
Intersection Observer
是一個比較新的api,他允許你追蹤目標元素與其祖先元素或視窗的交叉狀態(tài),用他來檢測圖片是否進入視口非常方便,不用再像之前綁定事件、計算距離等。
(1)屬性介紹:
- 利用
Intersection Observer
實例上的observe
和unobserve
方法,注冊或取消監(jiān)聽事件。 - 利用
isIntersecting
方法,判斷該圖片是否處于圖片與屏幕可視區(qū)域的交叉范圍內(nèi)。 - 注意:
Intersection Observer
實例會監(jiān)聽交叉狀態(tài),即出現(xiàn)和消失(觸發(fā)兩次),出現(xiàn)交叉狀態(tài)后會去調(diào)用new
的時候傳入的callback
回調(diào)函數(shù)
(2)實現(xiàn)方法:
步驟 1: 監(jiān)聽頁面滾動事件,lazyLoad
為頁面滾動時的處理函數(shù),在本節(jié)為處理圖片懶加載。
window.addEventListener('scroll', lazyLoad)
步驟 2: 創(chuàng)建圖片與可視區(qū)域交叉實例
callback
:
- 此為傳入的回調(diào)函數(shù),用于當處于交叉狀態(tài)改變時進行的處理函數(shù)
- 該函數(shù)會被觸發(fā)2次:圖片進入視野時+圖片離開視野時
const observer = new IntersectionObserver(callback)
步驟 3: 利用observer
實例上的.observe(img)
方法,給每張圖片綁定觀察事件
// 給每一個圖片綁定觀察方法 imagess.forEach(img => { // 圖片進入視野+離開視野時會觸發(fā)callback回調(diào)函數(shù) observer.observe(img) })
步驟 4: 定義圖片的懶加載事件
imgArr
:
- 可以獲得包含所有圖片的
isIntersecting
屬性的集合,該屬性可判斷是否在交叉區(qū)域內(nèi) target
為該圖片的標簽元素
// callback 接收的參數(shù)為帶有監(jiān)聽所有圖片交叉屬性的集合 const callback = (imgArr) => { console.log('視圖交叉時觸發(fā),離開交叉時也觸發(fā)', imgArr) imgArr.forEach(e => { // 判斷是否在視野區(qū)域 if (e.isIntersecting) { e.target.src = e.target.dataset.src // 取消監(jiān)聽,避免重復加載同一張圖片 observer.unobserve(e.target) } }) }
(3)整體代碼:
// html 標簽結(jié)構(gòu) <img data-src="./public/image/VCG211430870249.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211430987515.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211431054751.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211435102490.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438229829.jpg" src="./public/image/默認.jpg" alt=""> <img data-src="./public/image/VCG211438109615.jpg" src="./public/image/默認.jpg" alt="">
// intersectionObserver 交叉觀察 : 目標元素和可視窗口會產(chǎn)生交叉區(qū)域 const imagess = [...document.querySelectorAll('img')] // 2.1 創(chuàng)建視覺交叉的觀察實例 const observer = new IntersectionObserver(callback) // 2.2 給每一個圖片綁定觀察方法 imagess.forEach(img => { // 2.3 圖片進入視野+離開視野時觸發(fā) - 回調(diào) observer.observe(img) }) // callback 接收的參數(shù)為帶有監(jiān)聽所有圖片交叉屬性的集合 const callback = (imgArr) => { console.log('視圖交叉時觸發(fā),離開交叉時也觸發(fā)', imgArr) // imgArr為 imgArr.forEach(e => { // 判斷是否在視野區(qū)域 if (e.isIntersecting) { e.target.src = e.target.dataset.src // 取消觀察追蹤,避免重復加載同一張圖片 observer.unobserve(e.target) } }) }
以上就是JavaScript實現(xiàn)圖片懶加載的三種常用方法總結(jié)的詳細內(nèi)容,更多關于JavaScript圖片懶加載的資料請關注腳本之家其它相關文章!
相關文章
詳解JavaScript創(chuàng)建數(shù)組的三種方式
這篇文章主要介紹了JavaScript創(chuàng)建數(shù)組的三種方式:直接聲明,?以對象方式創(chuàng)建數(shù)組和使用?Array.from()?方法創(chuàng)建,并通過代碼示例講解的非常詳細,具有一定的參考價值,需要的朋友可以參考下2024-06-06Webpack打包時將文件內(nèi)聯(lián)方法實現(xiàn)
本文主要介紹了Webpack打包時將文件內(nèi)聯(lián)方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01Javascript ES6中數(shù)據(jù)類型Symbol的使用詳解
Symbol類型是es6新增的一個數(shù)據(jù)類型,Symbol值通過Symbol函數(shù)生成Symbol類型是保證每個屬性的名字都是獨一無二的,對于一個對象由對個模塊構(gòu)成的情況非常有用,本文主要介紹了Javascript ES6中數(shù)據(jù)類型Symbol使用的相關資料,需要的朋友可以參考下。2017-05-05