郑州今年7月份的洪水,8月份的疫情,12月份公司搬家离职,过了元旦的又是疫情!过的着实不易!这不现在公司内网,在家不让连公司电脑,所以不能在家写公司的代码!让在家学习是否会有工资都是未知!顺便总结些知识点,
1.图片延迟加载的意义
图片延时加载,是真实项目中的一个非常重要的性能优化手段。如果不做图片的延时加载,那也页面渲染的时候,同时也要把图片资源请求回来,进行渲染,这样会阻碍页面的渲染进度,导致首次加载页面的速度很慢,延迟加载一方面可以提要页面的加载速度,另一方面可以减少没必要的网络消耗
**实现:**在没有加载真实的图片之前,图片区域用默认的背景图(背景色)占位即可
**首屏:**先加载首屏其他数据,等待其他数据加载完成,再去加载真实图片
其他屏:滚动到对应的区域(一露面,出来一半,完全出现在屏幕中)再去加载真实图片
2.图片延迟加载方案一
在浏览器滚动条滚动(页面滚动)的过程中,等待图片完全出现在可视窗口内的时候,我们加载真实的图片,“这是非首屏图片
的处理步骤”-> 计算出盒子底边距离body顶部的距离A,和浏览器底边距离body 的距离B 做比较,如果A<=B的时候我们去加载图片即可
WechatIMG174
<!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>
</head>
<style>
* {
margin: 0;
}
.imageLazyBox {
width: 236px;
height: 420px;
background: url(./images/loading.gif) no-repeat center center #eee;
background-size: 100px 100px;
margin: 1000px 0px;
}
.imageLazyBox img {
width: 100%;
height: 100%;
/* 开始图片隐藏:因为在ie 浏览器中,如果图片src 是空的,或者加载图片是错误的,图片不隐藏 ,会显示一个X
效果,很难看,所以图片没有加载之前还是让他隐藏比较好
办法一: display:none;这种办法加载完成真实的图片后,还需让他display:blok;这样触发dom的回流重汇,性能消耗比较大
办法二 :opacity:0 ;transition:opacity: .3s; ->推荐方案,一方面加载真实图片后,我们只需要设置opacity:1;
这样一方面不会引发dom的回流重汇,一方面可以css3实现出渐现的效果
*/
opacity: 0;
transition: opacity 1s;
/* display: none; */
}
</style>
<body>
<div class="imageLazyBox">
<img src="" alt="" lazy-image="./images/img1.jpeg">
</div>
</body>
</html>
<script>
// 图片延迟加载方法的处理
function imageLazyFun(imageLazyBox) {
let imageItem = imageLazyBox.querySelector('img'),
lazy_image = imageItem.getAttribute('lazy-image')
imageItem.src = lazy_image
imageItem.onload = function () {
imageItem.style.opacity = 1
}
imageItem.removeAttribute('lazy-image');
imageItem.isLoad = true;
}
let imageLazyBox = document.querySelector(".imageLazyBox"),
imageItem = imageLazyBox.querySelector('img'),
HTML = document.documentElement;
// 获取一个元素距离body顶部的距离
function offset(element) {
let l = element.offsetLeft,
t = element.offsetTop,
p = element.offsetParent;
while (p && p.tagName !== 'body') {
if (!/MSIE 8/.test(navigator.userAgent)) {
l += p.clientLeft;
t += p.clientTop;
}
l += p.offsetLeft;
t += p.offsetTop;
p = p.offsetParent;
}
return {
top: t,
left: l
}
}
// 截流函数
let throttle = function throttle(fn, wait) {
let timeout = null, // 计时器变量
result = null, // 上次执行的结果
previous = 0; //上次执行的时间
return function anymouse(...args) {
let now = new Date,
context = this;
let remaining = wait - (now - previous);
// 看下次执行的时间到没有时间到了清除上次的内容执行
// console.log("Shiji",remaining);
if (remaining <= 0) {
clearTimeout(timeout);
previous = now;
result = fn.apply(context, args);
// 首次执行
} else if (!timeout) {
timeout = setTimeout(() => {
previous = new Date;
result = fn.apply(context, args)
}, wait)
}
// console.log(result)
return result
}
}
// 执行频率过高 做函数的截流
window.onscroll = throttle(function () {
console.log("我做了截流处理")
if (imageItem.isLoad) return
let A = offset(imageLazyBox).top + imageLazyBox.offsetHeight,
B = HTML.clientHeight + HTML.scrollTop;
if (A <= B) {
console.log("ok")
imageLazyFun(imageLazyBox);
}
},500)
</script>
3.图片延迟加载方案二 利用 getBoundingClientRect()
getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。
上面代码不东只需要把判断条件换了就行啦
window.onscroll = throttle(function () {
console.log("我做了截流处理")
if (imageItem.isLoad) return
// let A = offset(imageLazyBox).top + imageLazyBox.offsetHeight,
// B = HTML.clientHeight + HTML.scrollTop;
let A = imageLazyBox.getBoundingClientRect().bottom;
B = HTML.clientHeight
if (A <= B) {
console.log("ok")
imageLazyFun(imageLazyBox);
}
},500)
4.图片延迟加载方案三基于 IntersectionObserver来实现延迟加载
概念
IntersectionObserver接口(从属于Intersection Observer API)为开发者提供了一种可以异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段。祖先元素与视窗(viewport)被称为根(root)。
这个是MDN上官方概念,贴出来显得专业
重点:这里监听目标元素与其祖先或视窗交叉状态的手段**,其实就是观察一个元素是否在视窗可见。**
API
var io = new IntersectionObserver(callback, options)
其实就是一个简单的构造函数。
以上代码会返回一个IntersectionObserver实例,callback是当元素的可见性变化时候的回调函数,options是一些配置项(可选)。
options
root
用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素
threshold
用来指定交叉比例,决定什么时候触发回调函数,是一个数组,默认是[0]。
const options = {
root: null,
threshold: [0, 0.5, 1]
}
var io = new IntersectionObserver(callback, options)
io.observe(document.querySelector('img'))
rootMargin
用来扩大或者缩小视窗的的大小,使用css的定义方法,10px 10px 30px 20px表示top、right、bottom 和 left的值
const options = {
root: document.querySelector('.box'),
threshold: [0, 0.5, 1],
rootMargin: '30px 100px 20px'
}
为了方便了解我们看图
首先我们来看下图上的问题,蓝线是什么呢?他就是咱们定义的root元素,我们添加了rootMargin属性,将视窗的增大了,虚线就是现在的视窗,所以元素现在也就在视窗里面了。
由此可见,root元素只有在rootMargin为空的时候才是绝对的视窗。
说了简单的options,接下来我们看下callback。
callback
callback函数会触发两次,元素进入视窗(开始可见时)和元素离开视窗(开始不可见时)都会触发
var io = new IntersectionObserver((entries)=>{
console.log(entries)
})
io.observe($0) // 观察的dom
运行结果如下
我们可以看到callback函数有个entries参数,它是个IntersectionObserverEntry对象数组,接下来我们重点说下IntersectionObserverEntry对象
IntersectionObserverEntry
IntersectionObserverEntry提供观察元素的信息,有七个属性。
boundingClientRect 目标元素的矩形信息获取的结果基于getBoundingClientRect()获取的结果 intersectionRatio 相交区域和目标元素的比例值 intersectionRect/boundingClientRect 不可见时小于等于0 intersectionRect 目标元素和视窗(根)相交的矩形信息 可以称为相交区域 isIntersecting 目标元素当前是否可见 Boolean值 可见为true rootBounds 根元素的矩形信息,没有指定根元素就是当前视窗的矩形信息 target 观察的目标元素 time 返回一个记录从
IntersectionObserver的时间到交叉被触发的时间的时间戳
重点:intersectionRatio和isIntersecting是用来判断元素是否可见的,押题咯.
IntersectionObserver触发时机
默认:第一次监听完dom触发一次,元素刚开始出现在页面中触发一次,元素完全消失触发一次
<!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>
</head>
<style>
* {
margin: 0;
}
.imageLazyBox {
width: 236px;
height: 420px;
background: url(./images/loading.gif) no-repeat center center #eee;
background-size: 100px 100px;
margin: 1000px 0px;
}
.imageLazyBox img {
width: 100%;
height: 100%;
/* 开始图片隐藏:因为在ie 浏览器中,如果图片src 是空的,或者加载图片是错误的,图片不隐藏 ,会显示一个X
效果,很难看,所以图片没有加载之前还是让他隐藏比较好
办法一: display:none;这种办法加载完成真实的图片后,还需让他display:blok;这样触发dom的回流重汇,性能消耗比较大
办法二 :opacity:0 ;transition:opacity: .3s; ->推荐方案,一方面加载真实图片后,我们只需要设置opacity:1;
这样一方面不会引发dom的回流重汇,一方面可以css3实现出渐现的效果
*/
opacity: 0;
transition: opacity 1s;
/* display: none; */
}
</style>
<body>
<div class="imageLazyBox">
<img src="" alt="" lazy-image="./images/img1.jpeg">
</div>
</body>
</html>
<script>
function imageLazyFun(imageLazyBox) {
let imageItem = imageLazyBox.querySelector('img'),
lazy_image = imageItem.getAttribute('lazy-image')
imageItem.src = lazy_image
imageItem.onload = function () {
imageItem.style.opacity = 1
}
imageItem.removeAttribute('lazy-image');
imageItem.isLoad = true;
}
let imageLazyBox = document.querySelector(".imageLazyBox");
let ob = new IntersectionObserver(changes => {
// 我们监听的dom元素与可视窗口的交叉信息
let item = changes[0];
target = item.target;
// 符合条件:盒子已经完全出现在视口中
if (item.isIntersecting) {
// 符合条件:盒子已经完全出现在视口中
imageLazyFun(target);
// 处理过一次延迟加载,以后这个元素出现无需要再次监听处理
ob.unobserve(target)
}
});
// 监听dom元素
ob.observe(imageLazyBox)
// 解除监听
// ob.unobserve(imageLazyBox)
</script>
5.图片延时加载方案四个,未来方法不兼容
loading="lazy" 浏览器兼容的比较少
<div class="imageLazyBox">
<img src="./images/img1.jpeg" loading="lazy" >
</div>
6. 几种图片延迟加载方法优缺点的对比
getBoundingClientRect 相对比第一种方法计算较少,但是getBoundingClientRect 兼容性不及第一种方法移动端适用,这两种当发都用了 window.onscroll 方法,需要自己写截流函数优化性能
IntersectionObserver相对于使用window.onscroll方法,更方便性能更优化,不用自己节流处理