IntersectionObserver

120 阅读1分钟

最近再查看 react useEffect文档时,看到有个示例:定制 useIntersectionObserver Hook

通过了IntersectionObserver 方式实现了,可以异步观察目标元素与顶级文档或者根元素交叉状态的方法。那么是不是可以通过该方法代 scroll 事件呢,于是就查看了文档做了个例子。

IntersectionObserver 属性

root: 边界的元素或者文档,默认是顶级文档

rootMarin: 计算交叉时边角盒的偏移量,支持%px, 默认'0px 0px 0px 0px'

thresholds: 一个包含阈值的列表,按升序排列,列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)。如果构造器未传入值,则默认值为 0。

IntersectionObserver 实例

observe(): 监听的目标元素

disconnect(): 停止监听目标

使用示例:

const intersectionObserver = new IntersectionObserver((entries) => {
  // 如果 intersectionRatio 为 0,则目标在视野外,
  // 我们不需要做任何事情。
  if (entries[0].intersectionRatio <= 0) return;

  loadItems(10);
  console.log("Loaded new items");
});
// 开始监听
intersectionObserver.observe(document.querySelector(".scrollerFooter"));

参考mdn web docs:developer.mozilla.org/zh-CN/docs/…

IntersectionObserver 示例

useIntersectionObserver hook

import { useEffect, useState } from "react"

const useIntersectionObserver = (ref: any, options?: object) => {
  const [isIntersecting, setIsIntersecting] = useState<boolean>(false)

  useEffect(() => {
    const elm = ref.current
    const intersectionObserver = new IntersectionObserver((entries) => {
      setIsIntersecting(entries[0].isIntersecting)
      // 开始监听
    }, options)
    intersectionObserver.observe(elm)
    return () => {
      intersectionObserver.disconnect()
    }
  }, [ref])

  return isIntersecting
}

export default useIntersectionObserver

useIntersectionObserver 应用

import { useRef } from "react"
import useIntersectionObserver from '@/hooks/useIntersectionObserver'

const Rolling = () => {
  const redRef = useRef<HTMLDivElement>(null)
  const parentRef = useRef<HTMLDivElement>(null)
  const isIntersection = useIntersectionObserver(redRef, { root: parentRef.current, rootMargin: '50px 0px 0px 0px' })

  return (
    <div ref={parentRef} className="rolling-box" style={{ height: 'calc(100vh - 310px)', overflow: 'hideen', position: 'relative' }}>
      { !isIntersection && <div style={{ position: 'absolute',  left: '45%', top: '10px', zIndex: 2 }} >这是 <span style={{ color: 'blue' }}> red hidden </span></div> }
      <div className="rolling-box" style={{ height: '100%', overflow: 'auto', padding: '50px 0' }}>
        <div style={{height: '500px', border: '1px solid #eee',}}></div>
        <div ref={redRef} style={{height: '300px', border: '1px solid red', margin: '50px 0 0 0'}}>red</div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
        <div style={{height: '300px', border: '1px solid blue', margin: '50px 0 0 0'}}>blue</div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
        <div style={{height: '500px', border: '1px solid #eee', margin: '50px 0 0 0'}}></div>
      </div>
    </div>
  )
}

export default Rolling

red box 在指定视窗内: image.png

red box 不在指定视窗内: image.png