监听元素是否可见

735 阅读1分钟

我们常常需要监听某个元素是否进入了"视口"(viewport),然后做一些操作。

const Home = (props) => {
  const ref = useOnScroll();
  return (
    <div className="home" ref={ref} id="home"> Home </div>
  );
};

方案一:
scroll 监听, getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。

import { useEffect } from 'react';
useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
}, []);

const handleScroll = ()=>{
    const el = document.getElementById('home');
    const rect = el.getBoundingClientRect();
    const vWidth = window.innerWidth || document.documentElement.clientWidth;
    const vHeight = window.innerHeight || document.documentElement.clientHeight;
    const outView = rect.right < 0 || rect.bottom < 0 || rect.left > vWidth || rect.top > vHeight;
    if (!outView) {
        console.log('在视口内')
    }
}

方案二:

IntersectionObserver 是一个强大的 JavaScript API,可以轻松地检测元素何时与视口或父元素相交。它让我们能够实现惰性加载、基于滚动的动画、无限滚动等功能,而不会引起性能问题和复杂的逻辑。

import { useEffect, useRef, RefObject } from 'react';
const useOnScroll = (): RefObject<HTMLDivElement> => {
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          console.log('在视口内')
        }
      },
      {
        threshold: 0.01,
      },
    );

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => {
      if (ref.current) {
        observer.unobserve(ref.current);
      }
    };
  }, []);
  return ref;
};

export { useOnScroll };

方案三:

npm i react-visibility-sensor
const VisibilitySensor = require('react-visibility-sensor');
 
function onChange (isVisible) {
  console.log('Element is now %s', isVisible ? 'visible' : 'hidden');
}
 
function MyComponent (props) {
  return (
    <VisibilitySensor onChange={onChange}>
      <div>...content goes here...</div>
    </VisibilitySensor>
  );
}