WebKit 中的 IntersectionObserver
网页开发者经常需要知道页面上的特定元素何时变得可见。该元素可能是一个广告或视频,我们希望计算其可见性。或者我们可能希望延迟加载图像直到其可见。解决此问题的常见方法是使用轮询,定期计算元素相对于视口的位置。然而,轮询效率低下,浪费电池寿命,并且不适用于跨域内容。IntersectionObserver API 为此问题提供了更好的解决方案,目前已在 Safari 技术预览版、macOS 10.14.4 测试版和 iOS 12.2 测试版中可用。
API 概述
每个 IntersectionObserver 都有一组目标元素,并观察这些目标与特定根元素或视口的交集。当目标和根的交集越过指定阈值时,观察者会调用回调函数。这是一个简单的入门示例
var intersectionCount = 0;
function callback(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting)
intersectionCount++;
});
}
var observer = new IntersectionObserver(callback);
observer.observe(target);
在上面的示例中,我们没有指定根元素,因此我们正在观察与视口的交集。我们将希望使用的回调函数传递给 IntersectionObserver 构造函数。一旦目标的任何部分与视口相交,我们就会收到回调;一旦目标与视口不再相交,我们也会收到另一个回调。
如果我们想知道元素何时全部或大部分可见,而不仅仅是任何部分可见,该怎么办?我们可以在构造观察器时指定一个交集阈值
observer = new IntersectionObserver(callback, { threshold: 0.95 } );
现在,一旦目标元素的 95% 与视口相交,我们将收到一个回调;一旦交集比例低于 95%,我们将收到另一个回调。
扩展这个想法,我们可以提供一个我们感兴趣的阈值列表
observer = new IntersectionObserver(callback, {
threshold: [ 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 ] });
这是一个使用阈值列表的实时演示。将目标滚动到视图中,然后缓慢地将其滚动出视图再滚动回视图,注意当交集比例越过阈值时,观察结果会被记录下来。
同样的原理也适用于 iframe 内的目标(演示)。在这种情况下,有两种方法可以将目标滚动出视图——滚动窗口使 iframe 移出视图,以及滚动 iframe 本身——这两种操作都会触发 IntersectionObserver 回调。
假设我们希望提前知道某个元素即将变得可见。IntersectionObserver 构造函数允许我们指定一个边距,用于在计算交集时扩展根元素。
observer = new IntersectionObserver(callback, { rootMargin: '100px 0px' } );
在计算交集时,视口将在顶部和底部扩展 100 像素,而左右两侧不会扩展。
使用负的根边距值,我们可以在计算交集时缩小根。例如,在这个演示中,我们将视口四边各缩小 50 像素
observer = new IntersectionObserver(callback, {
threshold: [ 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 ],
rootMargin: '-50px'
});
最后,我们可以指定一个特定的元素进行交集观察,而不是视口
observer = new IntersectionObserver(callback, { root: someElement } );
现在让我们来看看每次调用回调时传递给它的条目。假设我们想计算一个元素至少 80% 可见的时长
observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting)
entry.target.startTime = entry.time;
else if (entry.target.startTime)
entry.target.duration = entry.time - entry.target.startTime;
});
}, { threshold: 0.8 });
每个交集观察器条目都是一个字典,其中包含有关特定目标和根之间交集的详细信息。这包括时间戳、交集矩形以及表示目标在交集矩形内所占比例的交集比率。
更多示例
图片懒加载
假设我们有一个包含许多图片的很长的页面。与其一次性加载所有图片(包括屏幕外的图片),我们可以先使用低分辨率占位符图片,并延迟加载高分辨率版本,直到图片开始与视口相交(或者,使用 rootMargin,即将相交)。这里有一个演示说明了这种方法。为了更容易看到高分辨率图片加载过程,该演示没有使用 rootMargin,并且在图片开始与视口相交后等待 500 毫秒才切换到高分辨率版本。
元素可见时触发动画
为了确保动画仅在动画内容可见时才开始,我们可以在 IntersectionObserver 回调中触发动画
function intersectionCallback(entries) {
for (let entry of entries) {
if (entry.isIntersecting) {
entry.target.style.transform = 'translateX(20px)';
entry.target.style.transition = 'transform 2s';
} else {
entry.target.style.transform = 'none';
entry.target.style.transition-property = 'none';
}
}
}
每当目标元素开始与视口相交时,我们就会启动一个动画将其向左滑动;一旦它不再相交,我们就会将元素放回其原始位置。此演示包含一些由交集观察触发的动画示例。
反馈
我们很高兴 IntersectionObserver 已在 Safari 技术预览版、macOS 10.14.4 测试版和 iOS 12.2 测试版中可用!请尝试使用并针对您遇到的任何问题提交错误报告。