# React Hook useScroll 滚动

# Hook 代码

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


type TDirection = "up" | "down" | "left" | "right" | "static"

interface IScrollInfo{
  scrollHeight: number;
  scrollWidth: number;
  scrollTop: number;
  scrollLeft: number;
  clientHeight:number;
  direction: TDirection;
  distance: number;
  scrollElement?: null | HTMLElement
}


const useScroll = (ref:RefObject<HTMLElement> | string,throttleNum: number = 300) : IScrollInfo => {
  const [scrollInfo, setScrollInfo] = useState<IScrollInfo>({
    scrollHeight:0,
    scrollTop:0,
    scrollLeft:0,
    clientHeight:0,
    scrollWidth:0,
    distance:0,
    direction:"static",
    scrollElement: null
  })
  const beforeScrollTopRef = useRef<number>(0)
  const beforeScrollLeftRef = useRef<number>(0)
  useEffect(() => {
    let scrollElement: HTMLElement | null = null
    if(typeof ref === "string"){
      scrollElement = document.querySelector(ref)
    } else {
      scrollElement = ref.current
    }
    setScrollInfo({
      ...scrollInfo,
      scrollElement: scrollElement
    })
    const handleScroll = () => {
      if(scrollElement){
        let beforeScrollTop = beforeScrollTopRef.current
        let beforeScrollLeft = beforeScrollLeftRef.current
        const distanceVertical = scrollElement.scrollTop - beforeScrollTop
        const distanceHorizontal = scrollElement.scrollLeft - beforeScrollLeft
        let direction: TDirection = "static";
        if(distanceVertical > 0){
          direction = "down"
        } else if(distanceVertical < 0){
          direction = "up"
        } else {
          if(distanceHorizontal > 0){
            direction = "left"
          }else if(distanceHorizontal < 0){
            direction = "right"
          }
        }
        let distance = 0;
        if(direction === "down" || direction === "up"){
          distance = distanceVertical
        } else if(direction === "left" || direction === "right"){
          distance = distanceHorizontal
        }
        setScrollInfo({
          scrollHeight:scrollElement.scrollHeight,
          scrollWidth: scrollElement.scrollWidth,
          scrollTop:scrollElement.scrollTop,
          scrollLeft:scrollElement.scrollLeft,
          clientHeight:scrollElement.clientHeight,
          direction:direction,
          distance:Math.abs(distance),
          scrollElement: scrollElement
        })
        beforeScrollTopRef.current = scrollElement.scrollTop
        beforeScrollLeftRef.current = scrollElement.scrollLeft
      }
    }
    if (scrollElement) {
      scrollElement.addEventListener('scroll', throttle(handleScroll, throttleNum), {
        capture: false,
        passive: true,
      });
    }
    return () => {
      if (scrollElement) {
        scrollElement.removeEventListener('scroll', handleScroll);
      }
    }
  },[ref])
  return scrollInfo
}

export default useScroll


export const throttle = (fn:(...args: any) => any,delay: number = 200) => {
  let canRun: boolean = true;
  return (...nextArgs:any) => {
      if(!canRun) return;
      canRun = false;
      setTimeout(() => {
          fn(...nextArgs);
          canRun = true;
      },delay);
  }
}

// hook 测试用例
const App = () => {
  const useCount = useScroll(100)
  const myScrollRef = useRef(null)
  const scrollInfo = useScroll(myScrollRef)
  // scrollInfo 
  return <div ref={myScrollRef}>{useCount}</div>
}

# API 文档说明

# useCountDown Hook 参数说明

属性 说明 类型 默认值
ref 滚动容器,ref对象或者string selector RefObject<HTMLElement>| string
throttleNum 滚动节流间隔, 单位毫秒 number 300

# useCountDown Hook 返回值

属性 说明 类型 默认值
scrollHeight 滚动内容高度 number 0
scrollTop 滚动条距离距离顶部距离 number 0
clientHeight 滚动框高度 number 0
scrollWidth 滚动框宽度 number 0
distance 节流函数,距离上一次scrollTop状态的差值 number 0
direction 滚动方向 "up" | "down" | "left" | "right" | "static" static
scrollElement 滚动dom 对象 HTMLElment null