关于IntersectionObserver 官方上说明是提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
具体的内容可以参考官网解释:https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver
这里我们主要来用IntersectionObserve来实现图片的懒加载。
图片的懒加载原理可以分为三步 :
- 我们先定义好img元素,我们也可以通过DOM动态添加,可以预先定义好img元素,并给他们一个基础的加载的图片作为公共图片。
- 给每个img元素加上data-src来存放真正图片的资源。
- 我们通过图片在整个界面的高度位置来和目前可视区域的高度进行比较,如果在可是区域内就进行图片资源的加载,不然就先不加载,从而提高性能。
在这里我们通过IntersectionObserve来实现图片懒加载。IntersectionObserver是浏览器原生提供的构造函数,他支持一个回调函数和,一个可选参数。
const intersectionObserve = new IntersectionObserver(callback, option)
callback的回调函数用来 处理浏览器可视区域的变化,option是一个可选的配置对象。
callback():
当目标元素可视性变化时就会执行的函数。
option:
配置选项,其中包括root, rootMargin, thresholds
- root: 所监听对象的具体祖先元素,若未指定则默认顶级元素
- rootMargin: 计算交叉时添加到根边界盒的矩形偏移量,可以有效的放大和缩小根的判定范围。
- thresholds: 一个阈值数组每个阈值都是交叉区域和边界区域的比例,当监听对象的可视部分穿过阈值时就会指向指定的回调函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Intersection</title>
</head>
<body>
<h2>图片列表</h1>
<ul>
<li><img src="imgs/timg.gif" data-src="imgs/OIP.jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (1).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (2).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (3).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (4).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP.jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (1).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (2).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (3).jpg" alt="" /></li>
<li><img src="imgs/timg.gif" data-src="imgs/OIP (4).jpg" alt="" /></li>
</ul>
</body>
<script>
const root = document.getElementsByTagName('ul')[0]
const options = {
root: root,
// 这里是一个数组可以指定多个比例类似[0.25, 0.5, 0.75, 1]
threshold: [0],//交会处
rootMargin:"0px"//对视口进行收缩和扩张
}
const lazyIntersection = new IntersectionObserver(entires => {
// entires为监听的节点数组对象
entires.forEach((item,index) => {
console.log(item)
// console.log(item.target, item.isIntersecting? '可见': '不可见')
// isIntersecting是当前监听元素交叉区域是否在可视区域指定的阈值内返回的是一个布尔值
if(item.isIntersecting) {
console.log('可见')
item.target.src = item.target.getAttribute('data-src')
// 这里资源加载后就停止进行观察
lazyIntersection.unobserve(item.target)
}
// console.log(item)
})
}, options)
// doucument.getElementsByTagName()获取的是一个元素DOM节点的伪数组,无法进行数组的遍历
// 我们可以使用Array.from()将伪数组转化为真数组进行数组操作
let data = Array.from(document.getElementsByTagName('img'))
data.forEach(item => {
// observe用来观察指定的DOM节点
lazyIntersection.observe(item)
})
</script>
<style>
* {
margin: 0;
padding: 0;
}
ul {
height: 1000px;
overflow-y: scroll;
}
ul li {
list-style: none;
margin-bottom: 10px;
text-align: center;
}
img {
min-height: 900px;
}
</style>
</html>
在这里我们通过li元素定义了几个img这里的图片就用想相同的。
由于要实现图片懒加载所以在这里我们需要给img元素定义一个data-src来存放加载图片作为公共图片来增强用户体验。我们先定义需要懒加载的图片的父元素来作为根节点,在这里我们拿ul作为根节点。
我们通过遍历img数组对象来给每一个元素进行监听,在判断isIntersecting,如果为true则就进行加载指定图片资源,当加载完成后就停止监听该节点。
需要注意的是,这里我们需要给一开始的img元素来指定一个宽高。如果我们是纵向展示的话就需要一个高度。因为当我们img元素渲染后如果没有指定高度它会默认为0此时还没有任何资源进行加载,那么所有的img元素都将会在可视区域内,那么他的isIntersecting都将会为true从而会加载所有资源,这样就无法实现懒加载的效果了,所以如果我们的图片都是一样大的我们可以指定好宽高,或者我们在给根元素指定一个特定的高度,然后按照我们需要在可视范围内想要展示图片数量,给每个img指定需要的高度,或者min-height让它把img元素撑开,这也就可以实现我们要的效果了。
这里我们因为定义的ul元素为1000px但是我们给img设置的min-height是900px,所以我们的img元素会有两个显示在当前可视区域内,由于我们设置的阈值为0所以在交叉边界为0的时候就调用回调函数加载资源,所以这里我们会加载两个图片资源。
当我们进行往下滑动的时候会进行图片的懒加载效果。
当然对于IntersectionObserver 还是一个较新的概念,所以可能对于相关的浏览器还是会有不兼容的。
我们可以参考下面的图片。
如果大家有想了解更多关于IntersectionObserver API的可以参考以下资源
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html