Intersection Observer API
Intersection Observer API随着 Web 应用的丰富和成熟,检测元素是否可见的需求增多。之前一般是通过三方库来实现,各自有各自的实现方式,性能也有差异。Intersection Observer API 便是这种功能的一个原生支持。 适用场景
用法及参数初始化监视器let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
使用该 API,有两个角色会参与进来,
API 会在目标元素可见,即出现在容器中时执行指定的回调。 但回调也不是都执行,它受参数 所以还需要理解一个概念,Intersection Ratio,可理解成目标元素的身体出现在容器中多少时,才触发回调,进一步理解请看下图: Intersection Ratio 示例 -- 图片来自 Arnelle Balane 的文章 The Intersection Observer API 上面 除了将 而 执行监视利用构造器 let target = document.querySelector('#listItem');
observer.observe(target);
回调回调的结构如下: let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
回调的入参为一个 注意:虽然监测器的监听是异步的,但回调的执行是在主线程,所以回调中不宜进行耗时任务,如果确实需要应该使用 一个完整的示例实现一个页面无限滚动及图片懒加载。完整代码可在 wayou/intersection-observer-api 查看。 App.tsximport React, { useState, useEffect, useCallback } from "react";
import "./App.css";
const IMAGE_API = "https://picsum.photos/200";
const PAGE_SIZE = 10;
const IMAGES_PAGED = new Array(PAGE_SIZE).fill(IMAGE_API);
let loaderObserver: IntersectionObserver;
let lazyLoadObserver: IntersectionObserver;
let options = {
rootMargin: "0px",
threshold: 0
};
let page = 0;
const App = () => {
const [images, setImages] = useState<string[]>(IMAGES_PAGED);
const infiniteCallback = useCallback<IntersectionObserverCallback>(
(_entries, _observer) => {
console.info(`loading page ${++page}`);
setImages(prev => [...prev, ...IMAGES_PAGED]);
},
[]
);
const lazyLoadCallback = useCallback<IntersectionObserverCallback>(
(entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// stop observer and load the image
const lazyImage = entry.target as HTMLImageElement;
console.log("lazy loading ", lazyImage);
lazyImage.classList.remove("empty");
lazyImage.src = lazyImage.dataset.src!;
observer.unobserve(entry.target);
}
});
},
[]
);
//setup infinite loading
useEffect(() => {
if (!loaderObserver) {
loaderObserver = new IntersectionObserver(infiniteCallback, options);
}
const target = document.querySelector("footer");
if (target) {
loaderObserver.observe(target);
}
return () => {
if (target) {
loaderObserver.unobserve(target);
}
};
}, [infiniteCallback]);
// setup lazy loading
useEffect(() => {
if (!lazyLoadObserver) {
lazyLoadObserver = new IntersectionObserver(lazyLoadCallback, options);
}
const imgs = document.querySelectorAll("img.empty");
if (imgs) {
imgs.forEach(img => {
console.count("setup img observer");
lazyLoadObserver.observe(img);
});
}
return () => {
if (imgs) {
imgs.forEach(img => {
lazyLoadObserver.unobserve(img);
});
}
};
}, [lazyLoadCallback, images]);
return (
<div className="App">
<div className="content-wrap">
{images.map((image, index) => (
<img className="empty" key={index} data-src={image + `?f=${index}`} />
))}
</div>
<footer>loading more ....</footer>
</div>
);
};
export default App;
运行效果: 利用 Intersection Observer API 实现的无限图片懒加载运行效果 兼容性虽然是实验性 API,但支持情况还可以。根据 Can I Use #intersectionobserver 的数据,PC 端除 IE 外主流浏览器均已支持,移动端 UC 部分支持,其余浏览器支持良好。 相关资源 |
The text was updated successfully, but these errors were encountered: |