跟着 ahooks 写 Hook 之 useInViewport

707 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情

💻文档地址:ahooks.js.org/zh-CN/

👨‍💻github地址:github.com/alibaba/hoo…


先来认识一个 API

Intersection Observer

IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗 (viewport) 交叉状态的方法。祖先元素与视窗 (viewport) 被称为根 (root)。

当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。

浏览器兼容

图片.png

还不错,基本上都可以使用 (中间那个 IE 就不用管了)

图片.png

一句话:Intersection Observer API 提供给开发者,一种异步查询元素相对于其他元素或窗口位置的能力,常用于追踪一个元素在窗口的可视问题。

var io = new IntersectionObserver(
  entries => {
    console.log(entries);
  }
);

entries 是一个数组,每个成员都是一个IntersectionObserverEntry对象。IntersectionObserverEntry对象提供目标元素的信息,一共有六个属性。

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
  • isIntersecting:是一个布尔值,当目标元素与交集观察者的根元素相交时为真。
function useInViewport(target: BasicTarget, options?: Options) {
  const [state, setState] = useState<boolean>();
  const [ratio, setRatio] = useState<number>();

  useEffectWithTarget(
    () => {
      const el = getTargetElement(target);
      if (!el) {
        return;
      }

      const observer = new IntersectionObserver(
        (entries) => {
          for (const entry of entries) {
            setRatio(entry.intersectionRatio);
            setState(entry.isIntersecting);
          }
        },
        {
          ...options,
          root: getTargetElement(options?.root),
        },
      );

      observer.observe(el);

      return () => {
        observer.disconnect();
      };
    },
    [],
    target,
  );

  return [state, ratio] as const;
}

IntersectionObserver OptionIntersectionObserver 构造函数的第二个参数,用来配置监视器的部分信息。

  • root:设置监视器的根节点,不传则默认为视口。
  • rootMargin: 类似于 CSS 的 margin 属性。用来缩小或扩大 rootBounds,从而影响相交的触发。
  • threshold:属性决定相交比例为多少时,触发回调函数。取值为 0 ~ 1,或者 0 ~ 1的数组。 如下图,当我们把 threshold 设置为 [0, 0.25, 0.5, 0.75, 1],绿色方块分别在 0%,25%,50%,75%,100% 可见时,触发回调函数。
const [inViewport, ratio] = useInViewport(() => document.getElementById('children'), {
    threshold: [0, 0.25, 0.5, 0.75, 1],
    root: () => document.getElementById('parent'),
  });

参考文章:

ruanyifeng.com/blog/2016/1…

developer.mozilla.org/en-US/docs/…