当你使用 Intersection Observer(以下简称 IO)时,你可以轻松地观察一个元素是否进入了浏览器的视口(即可见区域)。这对于实现懒加载、无限滚动、元素动画等功能非常有用。本文将介绍 Intersection Observer 的基本概念,并展示如何在 React 中使用它。

Intersection Observer 简介

IO 是一种浏览器原生的 API,用于异步观察元素与其祖先或视口之间的交叉状态。它提供了一种监听元素可见性变化的方式,而不需要通过轮询或事件绑定来实现。这使得观察大量元素的可见性变得高效且性能友好。

IO 的基本概念包括以下几个要素:

  • 观察器(Observer):创建一个观察器实例,用于定义要观察的目标元素和观察时的回调函数。
  • 目标元素(Target):要观察的 DOM 元素,可以是页面上的任意元素。
  • 交叉状态(Intersection):目标元素与其祖先或视口之间的交叉区域。包括交叉比例、交叉区域的位置和大小等信息。

在 React 中使用 Intersection Observer

在 React 中使用 Intersection Observer 可以轻松实现元素的可见性检测。下面是使用 Intersection Observer 的基本步骤:

  1. 安装 intersection-observer 库:由于 Intersection Observer 并非所有浏览器都原生支持,你需要安装 intersection-observer 库来提供兼容性支持。你可以使用 npm 或 yarn 进行安装。

  2. 导入并创建观察器实例:在需要观察元素可见性的组件中,导入 intersection-observer 库并创建一个观察器实例。

import React, { useEffect, useRef } from 'react';
import 'intersection-observer'; // 导入 intersection-observer 库

function MyComponent() {
  const targetRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      // 观察回调函数
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          // 目标元素进入视口
          console.log('Target is visible');
        } else {
          // 目标元素离开视口
          console.log('Target is not visible');
        }
      });
    });

    if (targetRef.current) {
      observer.observe(targetRef.current); // 开始观察目标元素
    }

    return () => {
      if (targetRef.current) {
        observer.unobserve(targetRef.current); // 停止观察目标元素
      }
    };
  }, []);

  return <div ref={targetRef}>Target Element</div>;
}

export default MyComponent;

在上述示例中,我们创建了一个名为 MyComponent 的 React 组件。通过 useRef 创建了对目标元素的引用(targetRef),并将其传递给 ref 属性。

然后,在组件的 useEffect 钩子中,我们创建了一个新的 Intersection Observer 实例,并定义了观察回调函数。回调函数中的 entries 参数是一个包含目标元素交叉状态信息的数组。我们通过遍历每个 entry 对象,判断目标元素是否进入了视口(entry.isIntersecting)。

接下来,我们使用 observe 方法开始观察目标元素。当组件销毁时,我们通过 unobserve 方法停止观察目标元素。

最后,在 JSX 中,我们将目标元素的 ref 属性设置为 targetRef,使其与引用对象关联起来。

使用react-intersection-observer库的useInView自定义hook

当在 React 中使用 Intersection Observer 时,你还可以使用 react-intersection-observer 这个库来简化代码。它提供了一个名为 useInView 的自定义 Hook,帮助你更方便地观察元素的可见性。下面是如何使用 useInView 的示例:

  1. 安装 react-intersection-observer 库:你可以使用 npm 或 yarn 进行安装。
npm install react-intersection-observer
  1. 导入并使用 useInView:在需要观察元素可见性的组件中,导入 useInView 并调用它。
import React from 'react';
import { useInView } from 'react-intersection-observer';

function MyComponent() {
  const { ref, inView } = useInView();

  return (
    <div ref={ref}>
      {inView ? 'Target is visible' : 'Target is not visible'}
    </div>
  );
}

export default MyComponent;

在上述示例中,我们使用 useInView 自定义 Hook 创建了 refinView 变量。ref 变量需要传递给目标元素的 ref 属性,而 inView 变量用于判断目标元素是否进入视口。

在 JSX 中,我们将目标元素的 ref 属性设置为 ref 变量,使其与引用对象关联起来。然后根据 inView 的值,我们展示不同的文本内容。

这样,你就可以使用 useInView 自定义 Hook 来简化 Intersection Observer 的使用,而无需手动创建观察器实例和编写观察回调函数。

注意:使用 react-intersection-observer 需要确保你的项目中已经安装了 react 版本为 16.8 或以上。

希望这个补充对你有所帮助!

实战示例

这里我写了一个简单的示例,将该FadeInOnViewport组件包裹在其他组件外,可以令其他组件在进入视口时有个淡入的动画效果,且大部分组件都可直接使用,基本不会影响原来的布局。

// FadeInOnViewport.jsx
import React, { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import tw, {styled} from "twin.macro";
import {randomNum} from "@/utils/utils";

export const Wrapper = styled.div`
  display: contents;
  & *{
    transition: all 0.6s ease-out;
    ${({fade_in}) => fade_in ? `opacity: 1` : 'opacity: 0'};
  }
  ${({_tw}) => _tw ? _tw : ''};
`

function FadeInOnViewport(props) {
    const [hasEnteredViewport, setHasEnteredViewport] = useState(false);
    const [ref, inView] = useInView();
    const [key] = useState(randomNum(1, 9999));

    useEffect(() => {
        if (inView && !hasEnteredViewport) {
            setHasEnteredViewport(true);
        }
    }, [inView, hasEnteredViewport]);

    return (
        <Wrapper fade_in={hasEnteredViewport} _tw={props.tw} key={props.key ? props.key : key}>
            <span ref={ref} tw={'invisible'}> </span>
            {props.children}
        </Wrapper>
    );
}

export default FadeInOnViewport;

结语

通过使用 Intersection Observer,我们可以实现对元素可见性的高效观察。它提供了一种简单的方式来检测元素与视口之间的交叉状态,无需手动处理滚动事件或轮询。

在 React 中使用 Intersection Observer 也非常简单,只需导入库并创建观察器实例即可。通过将目标元素的 ref 属性与引用对象关联,我们可以轻松观察目标元素的可见性变化。

希望本文对你理解和使用 Intersection Observer 在 React 中的方法有所帮助。Happy coding!


A Student on the way to full stack of Web3.