[前端开发] IntersectionObserver API 的使用、元素相交/可见性判断 及 图片惰性加载的实现

陪她去流浪 桃子 2019年06月02日 编辑 阅读次数:3790

相交简介

相交,也可以简单把它叫做重叠。如果两个矩形有重叠的区域,那么它们就是相交的。就像下面这样:

为什么要判断相交?

因为至少可以做以下事情:

  • 惰性加载: 当图片元素进入到浏览器窗口内时才加载
  • 实现无限滚动: 当页面底部元素出现时,继续加载更多内容
  • 计算广告展现量: 广告都没有出现,广告是不是白打了?
  • 按需呈现动画: 当用户滚动到某一区域时,呈现某个动画

判断相交的传统做法

在没有 IntersectionObserver 的时候,人们通常监听窗口滚动(scroll)事件,并调用某元素的Element.getBoundingClientRect()方法来获取 它当前相对于浏览器窗口的坐标信息。但是这种做法有一个非常明显的缺点:窗口滚动事件发生得非常频繁,大量计算,势必会造成浏览器性能问题。

IntersectionObserver 的引入

IntersectionObserver 自 HTML5 引入。 它提供了一种以异步方式来观察一个元素的相交性。

IntersectionObserver 的使用

创建对象

这个 API 的使用非常简单。

创建一个 IntersectionObserver 对象:

1
var observer = new IntersectionObserver(callback, options);

参数callback是一个回调函数,当元素的相交性发生变化时,这个函数会被回调。 而options参数则可以用于指定跟哪个元素进行相交判断,相交到哪种程度了进行回调。

观察对象

它有以下几个常用方法:

observe() 观察元素

以下代码观察一个img是否进入浏览器窗口:

1
observer.observe(img);

unobserve() 取消观察元素

以下代码取消观察一个img元素

1
observer.unobserve(img);

回调函数

回调函数长下面这样:

1
2
3
function(entries, observer) {

}

entries是一个IntersectionObserverEntry类型的数组,每当有元素的相交性发生变化时,这个类型包含该元素的相交有关的数据。 observer是前面创建的观察者。

IntersectionObserverEntry

它有以下几个常用属性:

  • intersectionRatio 相交比例。刚开始相交是0%,相交一半是50%,完全进入则是100%
  • isIntersecting 是否正在相交。intersectionRatio != 0 即正在相交。(后来增加的属性)
  • target 被观察的元素。
1
2
3
4
5
6
7
8
9
function(entries, observer) {
    entries.forEach(entry => {
        if(entry.isIntersecting) {
            console.log("元素相交");
        } else {
            console.log("元素不相交");
        }
    });
}

options 选项

(暂无)

实例:惰性图片加载

我刚刚在我的博客程序中实现了惰性图片加载, 所以我今天就拿这个图片惰性加载来作为实例。

前提:对 img 的修改

为了惰性加载图片,当然不能把 img 的 src 直接设置成图片地址,不然浏览器直接就显示了。而一般是放在data-src属性中,或增加lazy-load类。

1
<img data-src="https://example.com/me.png" width="100px" height="50px" />

找到所有需要惰性加载的图片

1
var images = document.querySelectorAll('img[data-src]');

对它们进行相交观察

1
2
var observer = new IntersectionObserver(onIntersection);
images.forEach(img => { observer.observe(img); });

回调处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 回调函数
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,但还并没有被浏览器或平台完全实现(主流浏览器都已实现),所以,酌情使用。

intersectionRatioisIntersecting 出现得早,所以优先使用。isIntersecting = intersectionRatio != 0

总之:作好兼容处理。

参考

标签:HTML