自定义hook 实现监测某一个元素是否在可视窗口、用户是否向上滚动监听

119 阅读1分钟

背景

最近在写大模型流式返回的时候,默认是拼接文字直接滚动到底部的,对于初始化的时候 历史内过多则需要展示滚动到底部的按钮,此hook就是监听 设置的元素是否在可视区域,不在的展示滚动到底部,

实现

import { useRef, useState, useEffect } from 'react';

export function useBottom(chatBottomRef: any) {
  const [showGoBottom, setShowGoBottom] = useState(false);
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        // 当参考元素完全不在视口中时显示按钮
        entries.forEach((entry) => {
          setShowGoBottom(!entry.isIntersecting);
        });
      },
      {
        root: null, // 使用浏览器视口作为根元素
        threshold: 0.1, // 当元素至少有10%可见时认为可见
        rootMargin: '0px'
      }
    );

    // 开始观察参考元素
    if (chatBottomRef.current) {
      observer.observe(chatBottomRef.current);
    }

    // 组件卸载时停止观察
    return () => {
      if (chatBottomRef.current) {
        observer.unobserve(chatBottomRef.current);
      }
    };
  }, [chatBottomRef]);

  return {
    showGoBottom
  };
}

使用方法

const { showGoBottom } = useBottom(chatBottomRef);

<div
        className="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5"
        ref={containterRef}
      >

        {/* 底部滚动 */}
        <div ref={chatBottomRef} className="mb-40 h-[1px]"></div>
      </div>
 {/* 滚动动画 */}
      {showGoBottom && (
        <GoBottom streaming={streaming} go={resetBottom}></GoBottom>
      )}

检测是否用户向上滚动hook

import { useRef, useState, useEffect } from 'react';

export function useScrollupListener(elementRef) {
  const isScrollingUp = useRef(false);
  const lastScrollTop = useRef(0);

  useEffect(() => {
    const target = elementRef?.current || window;

    const handleScroll = () => {
      const scrollTop = elementRef
        ? target.scrollTop
        : window.scrollY || document.documentElement.scrollTop;

      // 判断是否向上滚动
      if (scrollTop < lastScrollTop.current) {
        isScrollingUp.current = true;
      }

      lastScrollTop.current = scrollTop;
    };

    target.addEventListener('scroll', handleScroll);

    return () => {
      target.removeEventListener('scroll', handleScroll);
    };
  }, [elementRef]);

  return { isScrollingUp };
}