[前端开发] IntersectionObserver API 的使用、元素相交/可见性判断 及 图片惰性加载的实现
相交简介
相交,也可以简单把它叫做重叠。如果两个矩形有重叠的区域,那么它们就是相交的。就像下面这样:
为什么要判断相交?
因为至少可以做以下事情:
- 惰性加载: 当图片元素进入到浏览器窗口内时才加载
- 实现无限滚动: 当页面底部元素出现时,继续加载更多内容
- 计算广告展现量: 广告都没有出现,广告是不是白打了?
- 按需呈现动画: 当用户滚动到某一区域时,呈现某个动画
判断相交的传统做法
在没有 IntersectionObserver 的时候,人们通常监听窗口滚动(scroll)事件,并调用某元素的Element.getBoundingClientRect()
方法来获取
它当前相对于浏览器窗口的坐标信息。但是这种做法有一个非常明显的缺点:窗口滚动事件发生得非常频繁,大量计算,势必会造成浏览器性能问题。
IntersectionObserver 的引入
IntersectionObserver 自 HTML5 引入。 它提供了一种以异步方式来观察一个元素的相交性。
IntersectionObserver 的使用
创建对象
这个 API 的使用非常简单。
创建一个 IntersectionObserver 对象:
var observer = new IntersectionObserver(callback, options);
参数callback
是一个回调函数,当元素的相交性发生变化时,这个函数会被回调。
而options
参数则可以用于指定跟哪个元素进行相交判断,相交到哪种程度了进行回调。
观察对象
它有以下几个常用方法:
observe()
观察元素
以下代码观察一个img是否进入浏览器窗口:
observer.observe(img);
unobserve()
取消观察元素
以下代码取消观察一个img元素
observer.unobserve(img);
回调函数
回调函数长下面这样:
function(entries, observer) {
}
entries
是一个IntersectionObserverEntry
类型的数组,每当有元素的相交性发生变化时,这个类型包含该元素的相交有关的数据。
observer
是前面创建的观察者。
IntersectionObserverEntry
它有以下几个常用属性:
- intersectionRatio 相交比例。刚开始相交是0%,相交一半是50%,完全进入则是100%
- isIntersecting 是否正在相交。
intersectionRatio != 0
即正在相交。(后来增加的属性) - target 被观察的元素。
function(entries, observer) {
entries.forEach(entry => {
if(entry.isIntersecting) {
console.log("元素相交");
} else {
console.log("元素不相交");
}
});
}
options 选项
(暂无)
实例:惰性图片加载
我刚刚在我的博客程序中实现了惰性图片加载, 所以我今天就拿这个图片惰性加载来作为实例。
前提:对 img 的修改
为了惰性加载图片,当然不能把 img 的 src 直接设置成图片地址,不然浏览器直接就显示了。而一般是放在data-src
属性中,或增加lazy-load
类。
<img data-src="https://example.com/me.png" width="100px" height="50px" />
找到所有需要惰性加载的图片
var images = document.querySelectorAll('img[data-src]');
对它们进行相交观察
var observer = new IntersectionObserver(onIntersection);
images.forEach(img => { observer.observe(img); });
回调处理
// 回调函数
function onIntersection(entries, observer) {
entries.forEach(entry => {
// 表示正在相交,即:已经出现
if(entry.intersectionRatio != 0) {
let img = entry.target;
let src = setSrc(img);
// 设置后就可以取消观察这个img了
observer.unobserve(img);
console.log("Lazy loading", src);
}
});
}
// 用于设置 img 的 src 为 data-src
function setSrc(img) {
let src = img.getAttribute('data-src');
img.setAttribute('src', src);
return src;
}
效果展示
注意我是设置到出现 50% 时自动加载图片。缓慢滚动以查看效果!
其它
IntersectionObserver
虽然是一个出现了几年的API,但还并没有被浏览器或平台完全实现(主流浏览器都已实现),所以,酌情使用。
intersectionRatio
比 isIntersecting
出现得早,所以优先使用。isIntersecting = intersectionRatio != 0
。
总之:作好兼容处理。