1 IntersectionObserver
IntersectionObserver API接口 俗称 交叉口观察员,它提供了一种异步观察目标元素与祖先元素或顶级文档viewport的交集中的变化的方法,而祖先元素与窗口( viewport ) 被称为根(root)
该 API 方法的调用:
var observer = new IntersectionObserver(callback[, options]);
// 调用
observer.observer(DOM)
支持两个产生:
- callback:是当被监听元素的可见性变化时,触发的回调函数
- options:是一个配置参数,可选,有默认的属性值 详情参考
1.1 callback参数
当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数
entries:一个IntersectionObserverEntry对象的数组,每个被触发的阈值,都或多或少与指定阈值有偏差,callback是添加监听后,当监听目标发生滚动变化时触发的回调函数。接收一个参数entries,即IntersectionObserverEntry实例。描述了目标元素与root的交叉状态
observer:被调用的IntersectionObserver实例
它包含该元素的一些基本信息和变化的信息如下代码:
var observer = new IntersectionObserver(changes => {
for (const change of changes) {
console.log(change.boundingClientRect);
console.log(change.intersectionRect);
console.log(change.intersectionRatio);
console.log(change.isIntersecting);
console.log(change.rootBounds);
console.log(change.target);
console.log(change.time);
}
}, {})
其中三个参数是用来获取位置信息的,rootBounds(root元素的位置信息),boundingClientRect(target元素的位置信息),intersectionRect(进入交叉显示区域的位置信息,与intersectionRatio属性有关)
1.2 options的参数
可以用来配置 observer 实例的对象。如果options未指定,observer 实例默认使用文档视口作为 root,并且没有 margin,阈值为 0%(意味着即使一像素的改变都会触发回调函数)。你可以指定以下配置:
那么接下来看看有哪些默认的属性和方法
root: 监听元素的祖先元素Element对象,其边界盒将被视作视口。目标在根的可见区域的的任何不可见部分都会被视为不可见
rootMargin: 一个在计算交叉值时添加至根的边界盒 (bounding_box (en-US)) 中的一组偏移量,类型为字符串 (string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和 CSS 中的margin 属性等同; 可以参考 The root element and root margin in Intersection Observer API来深入了解 margin 的工作原理及其语法。默认值是"0px 0px 0px 0px"
threshold: 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。若指定值为 0.0,则意味着监听元素即使与根有 1 像素交叉,此元素也会被视为可见。若指定值为 1.0,则意味着整个元素都在可见范围内时才算可见。阈值的默认值为 0.0
1.3 IntersectionObserver 方法
2 IntersectionObserver 应用
- 图片懒加载
- 无限滚动加载
- 埋点曝光
2.1 结合 vue3.x 的无限滚动加载组件
首先创建一个观察器,当他与根元素(root)相交的时候,触发回调
<template>
<div class="target" ref="target">
<div class="loading" v-if="loading">
<span class="text">加载中...</span>
</div>
<div class="none" v-if="finished">
<span class="text">亲,没有完了</span>
</div>
</div>
</template>
import { ref, onMounted, onBeforeUnmount } from 'vue'
export default {
name: 'InfiniteLoading',
props: {
loading: {
type: Boolean,
default: false
},
finished: {
type: Boolean,
default: false
}
},
setup(props, { emit }) {
const target = ref(null)
onMounted(() => {
// 构建观察器
const observer = new IntersectionObserver(
([{ isIntersecting }]) => {
// 目标元素与根元素相交
if (isIntersecting) {
// console.log('进入可视区了')
// 触发加载条件:请求加载完成,数据加载完毕
if (!props.loading && !props.finished) {
emit('infinite')
}
}
},
{
threshold: 0.01 // 相交距离
}
)
// 观察目标元素
observer.observe(target.value)
})
// 组件销毁前停止监听
onBeforeUnmount(() => {
observer.disconnect()
})
return { target }
}
}
首先,在 setup 中使用 ref 创建一个响应式变量:const target = ref(null) ; 并且 setup 返回这个变量:return { target } 然后,直接通过 ref="target" 绑定元素为 ref target 最后在 setup 内部,通过 target.value 访问ref
组件销毁前,需要停止监听,所以在生命周期 onBeforeUnmount 里调用观察器的 disconnect 方法
使用:
将该观察器组件引入放在需要加载列表的底部部。将事件处理参数( loading 与 finished ),通过根元素选择器传给子组件,且子组件返回一个 infinite 事件
<InfiniteLoading :finished="finished" :loading="loading" @infinite="传值"/>
setup() {
const loading = ref(false) // 加载中
const finished = ref(false) // 是否加载结束
// 自定义数据逻辑处理
// ...
return { loading, finished }
}