use-sticky-scroll.ts 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. import React from 'react'
  2. import { useThrottleFn } from 'ahooks'
  3. export enum ScrollPosition {
  4. belowTheWrap = 'belowTheWrap',
  5. showing = 'showing',
  6. aboveTheWrap = 'aboveTheWrap',
  7. }
  8. type Params = {
  9. wrapElemRef: React.RefObject<HTMLElement>
  10. nextToStickyELemRef: React.RefObject<HTMLElement>
  11. }
  12. const useStickyScroll = ({
  13. wrapElemRef,
  14. nextToStickyELemRef,
  15. }: Params) => {
  16. const [scrollPosition, setScrollPosition] = React.useState<ScrollPosition>(ScrollPosition.belowTheWrap)
  17. const { run: handleScroll } = useThrottleFn(() => {
  18. const wrapDom = wrapElemRef.current
  19. const stickyDOM = nextToStickyELemRef.current
  20. if (!wrapDom || !stickyDOM)
  21. return
  22. const { height: wrapHeight, top: wrapTop } = wrapDom.getBoundingClientRect()
  23. const { top: nextToStickyTop } = stickyDOM.getBoundingClientRect()
  24. let scrollPositionNew = ScrollPosition.belowTheWrap
  25. if (nextToStickyTop - wrapTop >= wrapHeight)
  26. scrollPositionNew = ScrollPosition.belowTheWrap
  27. else if (nextToStickyTop <= wrapTop)
  28. scrollPositionNew = ScrollPosition.aboveTheWrap
  29. else
  30. scrollPositionNew = ScrollPosition.showing
  31. if (scrollPosition !== scrollPositionNew)
  32. setScrollPosition(scrollPositionNew)
  33. }, { wait: 100 })
  34. return {
  35. handleScroll,
  36. scrollPosition,
  37. }
  38. }
  39. export default useStickyScroll