我们常常需要监听某个元素是否进入了"视口"(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>
);
}