[譯] 精通 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 關注公衆號

轉載請註明出處

相關文章
相關標籤/搜索