[译] 精通 Intersection Observer API

原文:www.hweaver.com/intersectio…javascript

现代网站重度依赖 scroll 事件并非什么秘密了。滚动能够触发图片懒加载或延迟请求数据、初始化动画、支持无尽内容的加载,如此等等。糟糕的是这些 scroll 事件都不太可靠,也都是资源消耗大户。这在实现效果方面引发了问题,也经常让浏览器不堪重负。html

做为一种处理 scroll 事件的新方式,交集观察者(Intersection Observer API) 应运而生。该 API 容许用户观察指定元素 A,并监视其与特定元素 B (或浏览器视口)的 交集(intersection) 状态。前端

既有的实现究竟有何问题?考虑一个当下典型的站点页面,有不少 scroll 事件在发生 -- 广告模块、从底部滚动进来的新内容、时不时须要运行动画的元素,或是页面中的不少图片,都会滚动至被用户看到后才会加载或执行。这些 scroll 事件关联了无数的循环调用方法,其中不乏好比须要得到必要位置信息的 Element.getBoundingClientRect() 等等,都是性能敏感的。java

这些方法都运行在主线程中,这意味着一个地方出现问题就会殃及全部事情。Intersection Observer API 让浏览器免于应付交集事件,经过使用关联特定元素的交集状态的回调函数取而代之。浏览器能够更有效地管理这些事件,性能也获得了优化。git

须要注意的是浏览器兼容性,截至本文被翻译时的统计以下:github

Intersection Observer API 的兼容性

概念 & 基本用法

为了彻底理解为什么 Intersection Observer API 更益于性能,先来看看基础知识。数组

IntersectionObserver 定义

定义一个 Intersection Observer 实例时,有一些关键的术语。浏览器

根(root) 指的是等待一个对象与其产生交集的某个元素。默认来讲,就是浏览器视口(viewport),但任何合法的元素都是可使用的。前端优化

除了以 root 做为一个单独 IntersectionObserver 的基础,观察者还能够监视许多不一样的 目标(target)。目标也多是任意合法的元素,当任何一个目标和根元素发生交集时,观察者会触发一个回调函数。函数

根元素碰撞

基本用法

创建一个简单的 IntersectionObserver 很是方便。首先调用 IntersectionObserver构造器,并向其传入一个回调函数和一个预设的选项:

const options = {
	root: document.querySelector('#viewport'),
	rootMargin: '0px',
	threshold: 1.0
};

const observer = new IntersectionObserver(callback, options);
复制代码

如上所示,选项中有一些可用的属性:

root

用来检查是否和目标元素发生交集的根元素。该选项接受任何合法元素,可是根元素必须是目标元素的祖先,这一点很重要。若是不指定根元素,或设为 null,则浏览器视口就做为默认的根元素。

rootMargin

该属性被用来扩展或缩减根元素的尺寸。接受和 CSS 中的 margin 相同格式的值,好比一个单独的值 10px 或定义不一样边的多个值 10px 11% -10px 25px

threshold

最后,threshold(译注:阈yù值)选项指定了一个最小量,表示目标元素和根元素交集时,其自身知足该最小量才会触发回调。取值为 0.0 – 1.0 之间的一个浮点数,因此 75% 左右的交集率应该写成 0.75。若是但愿在多个点触发回调,也能够传入一个值的数组,如 [0.33, 0.66, 1.0]

一旦 IntersectionObserver 实例被建立,剩下所要作的就是提供一个或多个目标元素以供观察:

const target = document.querySelector('#target');
observer.observe(target);
复制代码

今后,回调函数将会在目标(或多目标)接近交集阈值的时刻被触发。

const callback = function(entries, observer) {
	entries.forEach((entry) => {
		// 在这干点什么
	});
}
复制代码

交集的计算

理解交集如何计算是重要的。首先,Intersection Observer API 将任意物体都视为矩形以便计算。这些矩形在包含目标内容的前提下,将被尽量小的计算。

Bounding box outlines
目标矩形的边界轮廓

对于根元素,基于 rootMargin 的值考虑其矩形边界,这个值会填充或减少根元素的尺寸。

Root Margin Calculations
计算 Root Margin

最后相当重要的是,要理解不一样于传统 scroll 事件的是,Intersection Observer 并非在每次交集改变后不间断地轮询。相反,回调只在阈值大约达到时被调用。若是须要屡次检测,提供多个阈值就好了。

Demo 1 – 动画

在第一个小项目中,咱们用一种简单的方式来看看 Intersection Observer API。

// 动画回调函数
const scrollImations = (entries, observer) => {
	entries.forEach((entry) => {
		console.log(111, entry.isIntersecting, entry.intersectionRatio);
		
		if(entry.isIntersecting && entry.intersectionRatio >= 1) {
		    // 彻底看到元素时
			entry.target.classList.add('box--visible');
		} else {
			entry.target.classList.remove('box--visible');
		}
	});
}

// 建立观察者
const options = {
	threshold: 1.0,
};
const observer = new IntersectionObserver(scrollImations, options);

// 指定观察目标
const boxes = document.querySelectorAll('.box');
boxes.forEach((box) => {
	observer.observe(box);
});
复制代码

进入视口后触发的动画

向下滚动,一系列元素会出现。用一个 IntersectionObserver 实例监视 3 个目标元素。当它们彻底进入视口(root)后,向目标元素上附加一个样式类名,触发对应的 CSS 动画。

Demo 2 – 页内导航

对于单页中随着滚动、相应某个区域的出现而高亮的导航条,Intersection Observer 是很适用的。

页内导航

// 初始化观察者
const options = {
	threshold: 0.45
}

const observer = new IntersectionObserver(changeNav, options);

// 指定目标元素
const sections = document.querySelectorAll('section');
sections.forEach((section) => {
	observer.observe(section);
});
复制代码

changeNav() 回调函数简单的检查目标 section 元素是否足够多的出如今屏幕上,而后恰当地指定样式类名。

const changeNav = (entries, observer) => {
	entries.forEach((entry) => {
		// 检查元素发生了碰撞
		if(entry.isIntersecting && entry.intersectionRatio >= 0.55) {
			// 删除旧的 active 样式类
			document.querySelector('.active').classList.remove('active');
			// 取得知足条件的目标元素 id
			var id = entry.target.getAttribute('id');
			// 找到匹配的元素并添加类名
			var newLink = document.querySelector(`[href="#${id}"]`).classList.add('active');
		}
	});
}
复制代码

浏览器支持 & Polyfill

对于尚不支持该特性的浏览器,有 polyfill 很好的填补了空白。官方的代码和文档能够在这里找到: github.com/w3c/Interse…

更简单的方法是适用 polyfill.io (polyfill.io)。能够单独指定须要加载的 Polyfill,且知足条件的浏览器才会加载。这能够保证页面的轻量,同时又不用过多配置。其用法以下:

<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
复制代码

一旦 polyfill 被加载,以上 demos 就能在 Safari、IE7+ 等浏览器上运行了。

总结

如你所见,Intersection Observer API 简单易用又富创造性。尽管可能须要 polyfill,但浏览器支持也在持续改善。该 API 将成为前端优化的利器。



--End--

搜索 fewelife 关注公众号

转载请注明出处

相关文章
相关标签/搜索