实现方式一:
原理:
- 先设置图片的data-src属性(浏览器不会加载data-*属性)。
- 获取图片距离顶部的高度和整个屏幕的高度,若是小于关系,说明图片进入了可视窗口,则设置图片的src属性。
代码:
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<img src="" alt="" data-src="./images/iPhone.jpg">
<img src="" alt="" data-src="./images/iPhone.jpg">
<img src="" alt="" data-src="./images/iPhone.jpg">
<img src="" alt="" data-src="./images/iPhone.jpg">
<img src="" alt="" data-src="./images/iPhone.jpg">
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
<p>111111111111111111111111111111</p>
const images = document.querySelectorAll('img')
window.addEventListener('scroll',e => {
images.forEach(item => {
const itemTop = item.getBoundingClientRect().top
if(itemTop < window.innerHeight){
const data_src = item.getAttribute('data-src')
item.setAttribute('src',data_src)
}
console.log('滚动');
})
})
缺点:
- 打印滚动次数过多
- 需要增加滚动事件监听
优化:
使用节流(n秒内只运行一次,若在n 秒后重复执行,只有一次执行 )进行优化。
const images = document.querySelectorAll('img')
window.addEventListener('scroll',throttle(e => {
images.forEach(item => {
const itemTop = item.getBoundingClientRect().top
if(itemTop < window.innerHeight){
const data_src = item.getAttribute('data-src')
item.setAttribute('src',data_src)
}
})
console.log('滚动');
},2000))
function throttle(fn,t){
let last,timer
let interval = t | 500
return function(){
let args = arguments
let now = +new Date()
if(last && now - last < interval){
clearTimeout(timer)
timer = setTimeout(()=>{
last = now
fn.apply(this,args)
},interval)
}else{
last = now
fn.apply(this,args)
}
}
}
但是,尽管做了优化,图片展示了滚动事件还是会继续监听,所以得使用另一种实现方式:IntersectionObserver。
实现方式二:
IntersectionObserver是浏览器提供的函数,需要浏览器支持。
原理:
- 先拿到所有的img DOM节点
- 对这个构造方法进行实例化
- 使用forEach对每一个图片进行观察
- 这个构造方法接收一个回调函数,回调函数触发两次,回调函数有一个参数entries(是观察对象组成的一个数组),每个观察对象有一个isIntersecting属性,代表是否进入可视区域。
- 进入可视区域就把data-src属性赋值于src属性,因为图片已经显示了,没有必要继续观察,使用unobserve方法关闭观察。
代码:
let images = document.querySelectorAll('img')
console.log(images);
function lazyLoad(entries){
entries.forEach(item => {
if(item.isIntersecting){
const image = item.target
const data_src = image.getAttribute('data-src')
image.setAttribute('src',data_src)
o.unobserve(image)
console.log('触发');
}
})
}
let o = new IntersectionObserver(lazyLoad)
images.forEach(item => {
o.observe(item)
})
使用IntersectionObserver方法,可以对已显示的图片不再进行监听,大大提升了原始方法的性能。