Image lazy loading principle
The custom attribute data-src of the rendered img is the real image path, and it is judged whether the image is in the unavailable area. If it is in the unavailable area, the value of the real image path data-src is assigned to the image src.
Application Scenario
The application scenario of lazy loading is biased towards network resource requests, which solves the problem that the website response time is too long caused by too many network resource requests.
How to judge whether the picture is in the viewable area?
1.offsetTop: the height from the top of the current element to the top of the parent element
2.window.innerHeight: The height of the visible window, excluding the toolbar at the top of the browser
3.scrollTop: the distance between the top of the current element and the top of the window, that is, the height beyond the visible area
When offsetTop <= window.innerHeight + scrollTop, the element is in the available area
method one:
function App() {
const [imgList, setImgList] = useState([])
const [start, setStart] = useState(0)
const [end, setEnd] = useState(10)
useEffect(() => {
let list:any = []
for (let index = 0; index < 10000; index++) {
list.push('/vite.svg')
}
setImgList(list)
},[])
const handleScroll = () => {
let img = document.getElementsByTagName('img')
const viewHeight = window.innerHeight
const scrollTop = document.querySelector('.App')?.scrollTop
const maxViewHeight = viewHeight + scrollTop
let n = 11
for (let i = n; i < img.length; i++) {
if(img[i].offsetTop <= maxViewHeight){
const src = img[i].getAttribute('data-src')
img[i].src = src || ''
}
n = i+1
}
}
return (
<div className="App" onScroll = {handleScroll}>
<div className='box'>
{imgList.map((item,index) => {
if(index >= start && index <= end){
return <img src={item} data-src="" className="logo" alt="Vite logo" key={index}/>
} else {
return <img src="" data-src={item} className="logo" alt="Vite logo" key={index}/>
}
})}
</div>
</div>
)
}
Method Two
const handleScroll = () => {
let img = document.getElementsByTagName('img')
const viewHeight = document.querySelector('.App')?.clientHeight
let n = 11
for (let i = n; i < img.length; i++) {
if(img[i].getBoundingClientRect().top <= viewHeight){
const src = img[i].getAttribute('data-src')
img[i].src = src || ''
}
n = i+1
}
}
getBoundingClientRect method: Element.getBoundingClientRect() - Web API interface reference | MDN
Method Three
import { useState, useEffect, } from 'react'
import reactLogo from './assets/react.svg'
import './App.css'
function App() {
const [imgList, setImgList] = useState( [])
useEffect(() => {
let list:any = []
for (let index = 0; index < 10000; index++) {
list.push('/vite.svg')
}
setImgList(list)
},[])
useEffect(() => {
if(IntersectionObserver){
imgList.length && lazyLoad()
}
},[imgList])
const lazyLoad = () => {
let img = document.getElementsByTagName('img')
let io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
// 获取当前元素
let curImg = entry.target
console.log(entry);
// 判断是否已经处于可视区域
if (entry.intersectionRatio > 0 && entry.intersectionRatio <= 1) {
curImg.setAttribute('src', curImg.getAttribute('data-src'))
}
})
})
Array.from(img).forEach((element) => {
io.observe(element)
})
}
return (
<div className="App" onScroll = {handleScroll}>
<div className='box'>
{imgList.map((item,index) => {
return <img src="" data-src={item} className="logo" alt="Vite logo" key={index}/>
})}
</div>
</div>
)
}
export default App