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

### 为什么要判断相交?
因为至少可以做以下事情:
- **惰性加载:** 当图片元素进入到浏览器窗口内时才加载
- **实现无限滚动:** 当页面底部元素出现时,继续加载更多内容
- **计算广告展现量:** 广告都没有出现,广告是不是白打了?
- **按需呈现动画:** 当用户滚动到某一区域时,呈现某个动画
### 判断相交的传统做法
在没有 IntersectionObserver 的时候,人们通常监听窗口滚动(scroll)事件,并调用某元素的`Element.getBoundingClientRect()`方法来获取
它当前相对于浏览器窗口的坐标信息。但是这种做法有一个非常明显的缺点:窗口滚动事件发生得非常频繁,大量计算,势必会造成浏览器性能问题。
### IntersectionObserver 的引入
IntersectionObserver 自 HTML5 引入。
它提供了一种以异步方式来观察一个元素的相交性。
## IntersectionObserver 的使用
### 创建对象
这个 API 的使用非常简单。
创建一个 IntersectionObserver 对象:
```js
var observer = new IntersectionObserver(callback, options);
```
参数`callback`是一个回调函数,当元素的相交性发生变化时,这个函数会被回调。
而`options`参数则可以用于指定跟哪个元素进行相交判断,相交到哪种程度了进行回调。
### 观察对象
它有以下几个常用方法:
#### `observe()` 观察元素
以下代码观察一个img是否进入浏览器窗口:
```js
observer.observe(img);
```
#### `unobserve()` 取消观察元素
以下代码取消观察一个img元素
```js
observer.unobserve(img);
```
### 回调函数
回调函数长下面这样:
```js
function(entries, observer) {
}
```
`entries`是一个`IntersectionObserverEntry`类型的数组,每当有元素的相交性发生变化时,这个类型包含该元素的相交有关的数据。
`observer`是前面创建的观察者。
### IntersectionObserverEntry
它有以下几个常用属性:
- **intersectionRatio** 相交比例。刚开始相交是0%,相交一半是50%,完全进入则是100%
- **isIntersecting** 是否正在相交。`intersectionRatio != 0` 即正在相交。(后来增加的属性)
- **target** 被观察的元素。
```js
function(entries, observer) {
entries.forEach(entry => {
if(entry.isIntersecting) {
console.log("元素相交");
} else {
console.log("元素不相交");
}
});
}
```
### options 选项
(暂无)
## 实例:惰性图片加载
我刚刚在我的博客程序中[实现了惰性图片加载](https://github.com/movsb/taoblog/commit/fc675b4062848dcb58b3cc8526f46fd51b8dc175),
所以我今天就拿这个图片惰性加载来作为实例。
### 前提:对 img 的修改
为了惰性加载图片,当然不能把 img 的 src 直接设置成图片地址,不然浏览器直接就显示了。而一般是放在`data-src`属性中,或增加`lazy-load`类。
```html
```
### 找到所有需要惰性加载的图片
```js
var images = document.querySelectorAll('img[data-src]');
```
### 对它们进行相交观察
```js
var observer = new IntersectionObserver(onIntersection);
images.forEach(img => { observer.observe(img); });
```
### 回调处理
```js
// 回调函数
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`。
总之:作好兼容处理。
## 参考
- [MDN: Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
- [Five Techniques to Lazy Load Images for Website Performance](https://www.sitepoint.com/five-techniques-lazy-load-images-website-performance/)
- [阮一峰:IntersectionObserver API 使用教程](http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html)
- [Find if two rectangles overlap](https://www.geeksforgeeks.org/find-two-rectangles-overlap/)
- [TaoBlog: feat: lazy loading images ](https://github.com/movsb/taoblog/commit/fc675b4062848dcb58b3cc8526f46fd51b8dc175#diff-7d10113993e8fe5e8da956762da18d88)
- [Can I use: IntersectionObserver ?](https://caniuse.com/#search=IntersectionObserver)