背景:加载多张图片需要处理很多次网络请求,等待加载时间较长,用户体验感差。
解决方式:随着滚动动态加载,即图片的惰性加载。
优点:页面加载速度快,用户体验感好。
一、图片懒加载的原理
1.对于所有图片初始化为一个小尺寸的图片(非实际需要的图片),因为所有图片都用同一张图片初始化,只会发送一次请求。
2.将图片的真实路径存储在一个自定义属性中,如在HTML5中支持自定义属性命名data-xx。
3.当图片进入可视窗口,将img来源路径替换成真实的路径。
二、图片懒加载具体实现
2.1 初始化
1.设置lazy_image类用于设置默认加载图片,给所有img添加该类。
.lazy_image {
background: url('./imgs/loading.gif') no-repeat center;
}
2.初始化所有的img标签,将真实图片路径存于自定义属性data-image中。
<img data-src="./imgs/1.webp" class="lazy_image">
2.2 判断元素是否出现在可视窗口(目前只考虑垂直方向)
1.利用offsetTop、scrollTop等计算判别
用window.onscroll定义函数监听滚动
a.元素进入可视区
可见的条件:content.offsetTop + content.offsetHeight > scrollTop
b.元素即将离开可视区
可见的条件:content.offsetTop < scrollTop + content.offsetHeight
综上可见函数定义如下:
const isShow = function(ele){
return (ele.offsetTop + ele.offsetHeight > document.documentElement.scrollTop) && (ele.offsetTop < document.documentElement.scrollTop + ele.offsetHeight);
}
当元素部分或全部出现在视口范围内时,isShow()返回true。
2.利用getBoundingClientRect()
用window.onscroll定义函数监听滚动
使用element.getBoundingClientRect()返回一个对象,对象的属性包括元素的大小及其相对视口的位置。
其对象有属性left、right、top、bottom、height、width。
onst {top, buttom, left, right, height, width} = d1.getBoundingClientRect(); // 获取属性
综上可见函数定义如下:
const isShow = function (ele) {
const {top, buttom, left, right, height, width} = ele.getBoundingClientRect();
const showHeight = window.innerHeight || document.documentElement.clientHeight;
return (top >= -height) && (top <= showHeight);
}
当元素部分或全部出现在视口范围内时,isShow()返回true。
3.利用new IntersectionObserver(callback, options)
IntersectionObserver():重叠观察者,用于判定两个元素是否重叠,无需进行监听事件,可以自动“观察”元素是否可见,性能方面较getBoundingRect()会好很多。
IntersectionObserver API是异步触发,只有线程空闲才会执行观察期,优先级非常低。可以同时观察多个对象。
使用判定分为两个步骤:
1> 创建观察者 const observer = new IntersectionObserver(callback, options)
callback: 回调函数。
const callback = (entries, observer) => {
entries.forEach(entry => {
entry.time; // 触发的时间
entry.rootBounds; // 根元素的位置矩阵,一般视为窗口位置
entry.boundingClientRect; // 被观察者的位置矩阵
entry.intersectionRect; // 重叠区域的位置矩阵
entry.intersectionRatio; // 重叠区域占被观察者面积的比例 (被观察者不是矩阵时也按矩阵计算)
entry.target; // 被观察者
})
};
options: 配置触发相关属性。threshold: [,,,]决定什么时候触发回调函数;root:可选 定义监听对象的父元素;rootMargin:用于扩展或缩小rootBounds这个矩阵的大小,从而影响intersectionRect交叉区域的大小。使用CSS的定义方法,上右下左的顺序。这样设置后,不管是窗口滚动或者容器内滚动,只要被观察元素可见性发生变化,都会触发观察者。
2> 传入被观察者。通过observer.observe(target)来注册被观察者。
关于IntersectionObserver()其他方法:
1> 停止观察:observer.unobserve(element)
2> 关闭观察器:observer.disconnect()
综上可见性判定代码如下:
var target = document.querySelector('#target');
const options = {
// 设置重叠面积占被观察者面积的比例 0~1
threshold: [0, 0.25, 0.5, 0.75, 1], // 1表全部覆盖
};
// callback函数在重叠比例超过options中设置的threshold时会被执行
const callback = (entries, observer) => {
entries.forEach(entry => {
if (entry.intersectionRatio === 0) {
// 不可见时操作代码
}else{
// 可见时操作代码
}
})
};
const observer = new IntersectionObserver(callback, options);
observer.observe(target);
还可以通过entry.isIntersecting判定在视口中是否可见。entry.isIntersecting为true表示可见。
注意:针对需要srcoll事件监听协助的前两种方式,为防止事件重复触发,可以借助节流的方式减少触发频率。
2.3 获取img元素集合对于可见元素进行操作
判定可见时
1.获取特定元素data-src的值,用dataset.src获取。
2.将取到的真实图片路径赋值给img的src属性,去请求对应资源。
3.移除lazy_image类
具体实现可参考:图片懒加载 - 掘金