引言
不知道大家在网站上浏览的时候有没有发现过这样一个问题:图片是逐行加载出来的,而不像我们写平常的html界面,图片是全部一起加载完成的。这就涉及到图片懒加载的知识了,图片懒加载可以显著减少性能的损耗,是我们开发过程中必备的技能。今天我们就来了解如何实现这个功能。
图片懒加载说白了就是图片延迟加载,由于浏览器的可视范围是有限的,而现在网页的内容越来越丰富,浏览内容需要进行滚动才能完成。当一个网页上有很多图片,而我们知道图片又是非常耗流量的,如果用户还没看到网页下的内容,那我们就没必要一下子加载这些看不见的图片。
监听事件
既然要滚动到网页下面才能预览到看不见的图片,那我们可以采用事件监听scroll这个事件进行解决。监听scroll事件,鼠标滚动就会触发。
这里我们需要知道两个高度
- 窗口显示区的高度(window.innerHeight)
- 图片到视窗上边的距离(可用getBoundingClientRect().top获取)
如果还不能看到图片,即图片距离视窗顶部的距离大于等于窗口显示区的高度
如果可以看到图片,即图片距离视窗顶部的距离小于窗口显示区的高度
<body>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<img data-src="1.gif">
<img data-src="2.gif">
<img data-src="3.gif">
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<script src="index.js">
</script>
</body>
</html>
使用自定义属性 data-,浏览器不会像默认属性那样处理这个自定义的属性。
const images = document.querySelectorAll('img')//获取所有img标签
window.addEventListener('scroll',(e)=>{//判断每张图片的位置是否出现在可视区域
images.forEach(image=>{
const imageTop = image.getBoundingClientRect().top//每次遍历的时候,获取每张图片到顶部的距离
//判断
if(imageTop<window.innerHeight){//图片距视图顶部距离小于窗口显示区高度
//获取刚刚取得的自定义属性
const data_src = image.getAttribute('data-src')
//将自定义属性赋值给原本的src属性
image.setAttribute('src',data_src)
}
console.log('scroll触发');
})
})
我们看一下这个结果:
虽然确实实现了懒加载,但是我们看到控制台中srcoll事件被触发了很多次,页面需要加载很多内容就会导致任务堆积。而且即使图片加载了还是会不断触发事件,非常消耗资源,所以我们更推荐IntersectionObserver这种方法。
IntersectionObserver
IntersectionObserver是浏览器提供的构造函数(但是部分浏览器版本不兼容)
IntersectionObserver(交叉观察),目标元素和可视窗口会产生交叉区域
使用方法
因为是构造函数,首先我们需要new一个用于观察的实例
const observer = new IntersectionObserver
进行观察
observer.observe(DOM节点)
当一些图片加载出来之后就没有必要观察了,所以还需要一个方法结束观察
observer.unobserve(DOM节点)
我们在观察到目标DOM节点的时候需要进行相应的动作,需要函数来进行封装这些动作,所以IntersectionObserver 的构造函数需要接受两个参数:
- 回调函数:当被观察的元素的可见度发生变化时,这个回调函数会被调用。
- 选项对象:一个可选的参数,用于指定如何观察元素。
const observer = new IntersectionObserver(callback, options);
参数详解
-
callback:
-
类型:
Function -
作用:当被观察元素的可见性发生变化时,这个回调函数会被调用,一般触发两次。
-
回调函数接收两个参数:
entries:一个数组,包含所有发生可见度变化的元素。observer:IntersectionObserver实例本身。
-
-
options:
-
类型:
Object -
作用:用于配置观察行为。
-
可配置的属性包括:
root:一个元素节点,作为观察的相对容器。默认为null,表示相对于视口。rootMargin:一个字符串,定义了相对于root的边界框边缘的偏移量。默认为'0px'。threshold:一个数字或数字数组,定义了在什么比例的可见度时触发回调。默认为[0],表示只要元素可见就触发回调。
-
虽然我们知道了回调函数的触发条件,但是我们并不知道触发回调函数时图片处于什么位置,所以这里的回调函数需要接受一个参数,这个参数是一个数组,表示为 entries
在控制台输出的话可以看到这些:
使用foreEach遍历这个数组,并输出每一次触发的细节
const callback =(entries)=>{
entries.forEach(entry=>{
console.log(entry);
})
};
运行我们的html页面,打开控制台,着重看isIntersecting属性,目前还没加载到图片,都为false:
然后我们滚动页面至图片到达可视区域,isIntersecting属性更新为true:
isIntersecting属性可以帮助我们判断该次触发回调函数时是否已经观察到了图片,但是控制台中显示触发了3次回调函数,所以我们还要通过target属性确定对应触发isIntersecting更改为true的元素。
target属性指的就是目标元素。
const callback =(entries)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){//如果观察到了图片
const image = entry.target;
const data_src = image.getAttribute('data-src')//获取相应图片节点
image.setAttribute('src',data_src)//修改为常规的src属性
console.log('触发');
}
})
};
我们查看控制台,当我们上下来回滚动的时候,回调函数也会重复执行:
所以我们要在图片被加载后取消回调函数的触发
取消回填函数的触发并不是取消滚动,而是直接取消observer实例这个观察动作
const callback =(entries)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){//如果观察到了图片
const image = entry.target;
const data_src = image.getAttribute('data-src')//获取相应图片节点
image.setAttribute('src',data_src)//修改为常规的src属性
observer.unobserve(image)//取消观察
console.log('触发');
}
})
};
这样就算来回滚动窗口也不会重复执行啦。
结语
以上就是关于图片懒加载的介绍,本篇文章是参考了技术蛋老师的视频原地址:www.bilibili.com/video/BV1FU…
蛋老师讲的非常详细,感兴趣的小伙伴强烈推荐观看原视频。 最后,希望本文对你有所帮助,感谢你的阅读!