前言
当我们在做需求时,可能经常会遇到很多跟页面的滚动有关的需求。例如
-
图片的懒加载:我们希望只加载用户当前视图窗口的图片,而未进入到视图窗口的图片,只有在进入到视图窗口时才进行加载,以提高页面响应速度,从而改善用户体验。
-
无限滚动:我们希望在一个页面在不断下拉的过程中,不断的加载新的内容,而无需进行页面跳转,类似于Vue、React等框架中的虚拟DOM的思想,以提升用户体验。
-
根据元素位置需求渲染元素:根据元素在视图窗口中的不同位置去进行一些或动画效果或者样式的改变。
当我们拿到这些需求的时候,可能大家最先想到的实现方法都是使用scroll监听,通常过不断轮询目标元素的位置信息,然后计算是否符合特定条件,来进行操作DON。但是,这种不断轮询方式会在JavaScript的主线程中不断执行,导致性能的极大消耗,很容易就会造成页面的掉帧,从而严重影响用户体验,。
今天我们就来讲讲另一个更好的解决方案Intersection Observer API
。它能够在帮助我们实现需求的同时,减少性能的消耗,从而提升用户体验。
通俗点说就是,Intersection Observer可以帮助我们监测一个元素是否进入或离开另一个元素或浏览器窗口的视口,并且可以精确到一个元素的可见比例
优点
-
性能强:
- 异步处理:相比于传统
scroll
事件监听的,Intersection Observer使用异步回调,避免了阻塞主线程,从而提高了性能。 - 减少不必要的计算:相比于传统的频繁地检查元素的可见性,Intersection Observer只在元素的符合条件下才发生变化时的回调,
- 异步处理:相比于传统
-
使用简单:
- 无需手动检测:使用Intersection Observer,不再需要我们手动编写大量代码来检测元素是否在视口中,在开发更加简单和可维护。
-
实时监测:
- 实时反馈:它能够即时响应元素的可见性变化,适用于需要实时反馈的交互效果。
-
精确度:
- 自定义阈值:我们可以手动的设置触发回调的阈值,可以更精确度的根据需求来触发回调。
-
跨浏览器兼容性:
- 兼容性好:Intersection Observer是标准的Web API,被主流浏览器支持,具有较好的跨浏览器兼容性。
用法
Intersection Observer API包括4部分:构造函数、观察选项、回调函数和一些方法。
-
构造函数:IntersectionObserver
构造函数用于创建Intersection Observer我们的实例,它接受两个参数:回调函数和观察选项。当被观察的元素和我们的窗口视图,或者别的元素的交叉状态满足我们设定的观察选项时,我们的回调函数就会被触发。
const observer = new IntersectionObserver(callback, options);
-
观察选项(Options):
观察选项是一个配置对象,用于指定我们的观察规则。它包括3个属性:
root
:根元素,用于指定一个容器元素,设为null
时会默认使用视口作为根元素。rootMargin
:根元素的边距范围,用像素或百分比表示,该属性值是用作 root 元素和 target 发生交集时候的计算交集的区域范围,用于扩大或缩小可视区域。threshold
:一个触发回调的阈值数组,表示目标元素的可见比例。
代码示例:
const options = {
root: document.querySelector('#container'), // 视口的根元素
rootMargin: '0px', // 例如 "10px 20px 30px 40px" (top, right, bottom, left)。
threshold: [0, 0.25, 0.5, 0.75, 1] //这里当目标元素的可见比例分别达到0%、25%、50%、75%和100%时,都会触发回调函数
};
-
回调函数(Callback Function):
回调函数会在被观察的元素的交叉状态发生变化时被调用。它接收两个参数:
entries
和observer
。entries
:一个Intersection Observer Entry对象的数组,每个Entry对象表示一个被观察元素与视口或根元素的交叉状态的信息。observer
:对观察器本身的引用,通常不需要使用。
每个Entry对象包含以下信息:
`target`:被观察的目标元素,即触发了交叉事件的元素。 `time`:发生相交到相应的时间,毫秒。 `rootBounds`:根元素矩形区域的信息,如果没有设置根元素则返回 null,图中蓝色部分区域。 `boundingClientRect`:一个DOMRect对象,描述了目标元素的边界框,包括位置、大小等信息。,图中黑色边框的区域。 `intersectionRect`:一个DOMRect对象,表示目标元素与视口或根元素的交叉区域的边界框,图中蓝色方块和粉红色方块相交的区域。 `intersectionRatio`:一个介于0和1之间的值,表示目标元素的可见比例,0表示完全不可见,1表示完全可见。 - `isIntersecting`:一个布尔值,表示目标元素是否与视口或根元素发生交叉。
代码示例:
// 创建一个 Intersection Observer 实例
const observer = new IntersectionObserver(callback, options);
// 回调函数,处理相交事件
function callback(entries, observer) {
entries.forEach(entry => {
const target = entry.target; // 被观察的目标元素
const time = entry.time; // 发生相交的时间(毫秒)
const rootBounds = entry.rootBounds; // 根元素的矩形区域信息
const boundingClientRect = entry.boundingClientRect; // 目标元素的边界框信息
const intersectionRect = entry.intersectionRect; // 目标元素与视口的交叉区域信息
const intersectionRatio = entry.intersectionRatio; // 目标元素的可见比例
const isIntersecting = entry.isIntersecting; // 目标元素是否与视口发生交叉
// 打印这些信息
console.log('目标元素:', target);
console.log('发生相交的时间:', time);
console.log('根元素矩形区域信息:', rootBounds);
console.log('目标元素边界框信息:', boundingClientRect);
console.log('交叉区域信息:', intersectionRect);
console.log('可见比例:', intersectionRatio);
console.log('是否相交:', isIntersecting);
// 根据需要执行操作,例如加载图片或触发动画
if (isIntersecting) {
// 目标元素进入视口,执行相关操作
} else {
// 目标元素离开视口,执行其他操作
}
});
}
// 启动观察
const targetElement = document.querySelector('#your-target-element'); // 选择要观察的目标元素
observer.observe(targetElement); // 开始观察目标元素
jcode
注意:
-
我们注册的回调函数将在主线程中执行,因此我们应该尽量保持函数的执行速度。如果需要执行一些耗时或者会引起阻塞的操作,建议使用
Window.requestIdleCallback()
方法。 -
在 Intersection Observer API 中,所有区域都被视为矩形。即使元素的形状不规则,它也会被看作包含该元素所有区域的最小矩形。同样,如果元素与视口的交集部分不是矩形,它也将被看作包含所有交集区域的最小矩形。
-
方法:
Intersection Observer实例还提供了一些方法,可以用于操作观察器的行为:
observe(target)
:将目标元素添加到观察器中,开始监测其交叉状态。unobserve(target)
:停止监测特定目标元素的交叉状态。disconnect()
:停止监测所有目标元素的交叉状态,可以在不需要观察器时使用。
示例:
const observer = new IntersectionObserver(callback, options); observer.observe(document.querySelector('#element')); observer.unobserve(document.querySelector('#element')); observer.disconnect();
代码示例
以下是一个简单的Intersection Observer示例,它会监测目标元素何时进入视口并改变其背景颜色:
<div id="target" style="height: 200px; background-color: lightblue;"></div>
<script>
const target = document.querySelector('#target');
const options = {
root: null,// 根元素,通常使用视口,可以设为null
rootMargin: '0px', // 根元素的边距,用于扩展或缩小可视区域
threshold: 0.5 // 触发回调的阈值,0表示完全不可见,1表示完全可见
};
const callback = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
target.style.backgroundColor = 'lightgreen';
} else {
target.style.backgroundColor = 'lightblue';
}
});
};
const observer = new IntersectionObserver(callback, options);
//将观察器绑定到目标元素上,以开始监测可见性变化。
observer.observe(target);
</script>
jcode