使用IntersectionObserver实现图片懒加载

1,617 阅读4分钟

传统的图片懒加载实现思路是判断图片的 top 区域是否小于父级区域,且自身高度加上 top 大于 0, 例如

// 元素是否可视
const viewVisible = el => {
  const { top, width, height, left } = el.getBoundingClientRect();
  const w = window.innerWidth || document.documentElement.clientWidth;
  const h = window.innerHeight || document.documentElement.clientHeight;
  return top < h && top + height >= 0 && left < w && left + width >= 0;
};

不过这样写的话有一个问题就是我们监听的事件会重复多次触发,所以我们还需要写一个节流或者防抖函数,不过使用IntersectionObserver我们可以轻松实现,首先看下兼容性

IntersectionObserver兼容性
可以看到差强人意,不过好在可以polyfill,下面就介绍使用

使用方法

IntersectionObserver使用方法很简单

var observer = new IntersectionObserver(callback[, options]);

callback接收两个参数,一个 IntersectionObserverEntry 对象列表(list),以及触发 IntersectionObserverEntry 的实例

方法 说明
disconnect() 使 IntersectionObserver 对象停止监听工作。
observe() 使 IntersectionObserver 开始监听一个目标元素。
unobserve() 使 IntersectionObserver 停止监听特定目标元素。

注意 callback的回调函数会触发两次,第一次是元素开始可见,第二次是元素不可见的时候。

选项(options)

注意可选属性

属性 说明
root 监听元素的祖先元素 Element 对象
rootMargin 一个在计算交叉值时添加至根的边界盒(bounding_box)中的一组偏移量,类型为字符串,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和 CSS 中的 margin 属性等同,默认值是"0px 0px 0px 0px"
threshold 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。若指定值为 0.0,则意味着监听元素即使与根有 1 像素交叉,此元素也会被视为可见.

实例

const dom = new IntersectionObserver(a => {
  console.log(a);
});
Array.from(document.querySelectorAll("img")).forEach(f => {
  dom.observe(f);
});

返回的 a 是一个数组,里面存放着一些元素信息,下面所有属性都是只读

属性 说明
boundingClientRect 返回包含目标元素的边界信息的 DOMRectReadOnly. 边界的计算方式与 Element.getBoundingClientRect() 相同.
intersectionRatio 返回 intersectionRect 与 boundingClientRect 的比例值,如果大于 0 则表示可见,完全可见为 1.
intersectionRect 返回一个 DOMRectReadOnly 用来描述根和目标元素的相交区域.
isIntersecting 返回一个布尔值, 如果目标元素与交叉区域观察者对象(intersection observer) 的根相交,则返回 true .如果返回 true, 则 IntersectionObserverEntry 描述了变换到交叉时的状态; 如果返回 false, 那么可以由此判断,变换是从交叉状态到非交叉状态.
rootBounds 返回一个 DOMRectReadOnly 用来描述交叉区域观察者(intersection observer)中的根.
target 与根出现相交区域改变的元素 (Element).
time 返回一个记录从 IntersectionObserver 的时间原点(time origin)到交叉被触发的时间的时间戳(DOMHighResTimeStamp).

实现

上面已经实现判断元素是否可见,下面我们只需要根据上面思路完善一下即可开发一个图片懒加载的类,首先先说明一下图片懒加载一般情况下为了防止图片src为空样式不统一和加载图片突然拉伸空间,会给默认的图片设置一个占位图片,或者给定一个样式

img {
  width: 400px;
  height: 400px;
  display: inline-block;
}
<img />

上面给了一个简单的演示,注意 src 属性不要写,因为即使为空也还会加载。 顺便说下火狐浏览器如果 img 标签没有其他属性的话会理解成内联元素,就是 span 标签一样的内联元素,所以如果不放置占位图片的话推荐重置 img 为 inline-block 然后设置固定宽高。

之后就是给图片动态添加 src 属性,比较推荐的是data-的自定义属性,许多库用的也是这个方法,替换完成之后将图片元素从IntersectionObserver移除。

最后

IntersectionObserver使用的是异步的方式,可以不用担心性能问题