Skip to content

[RFC] useIsScrolling #2461

Open
Open
@JacobZyy

Description

@JacobZyy

用于获取页面或者页面中某个元素是否正在滚动

使用场景

某些场景下需要响应目标元素的滚动状态

例如:目标元素滚动时需要收起某个浮窗,滚动停止时又需要展示。

API

export type Target = BasicTarget<Element | Document>;

export type Options = {
  target?: Target;
  /** @default 'vertical' */
  scrollDirection?: 'vertical' | 'horizontal';
};

type Position = { left: number; top: number };

const getCurrentScrollPosition = (scrollEl: Target): Position => {
  if (!scrollEl) return { left: 0, top: 0 };
  let newPosition: Position;
  if (scrollEl === document) {
    if (document.scrollingElement) {
      newPosition = {
        left: document.scrollingElement.scrollLeft,
        top: document.scrollingElement.scrollTop,
      };
    } else {
      newPosition = {
        left: Math.max(
          window.pageXOffset,
          document.documentElement.scrollLeft,
          document.body.scrollLeft,
        ),
        top: Math.max(
          window.pageYOffset,
          document.documentElement.scrollTop,
          document.body.scrollTop,
        ),
      };
    }
  } else {
    newPosition = {
      left: (scrollEl as Element).scrollLeft,
      top: (scrollEl as Element).scrollTop,
    };
  }

  return newPosition;
};

const useIsScrolling = (option?: Options): boolean => {
  const { scrollDirection = 'vertical', target } = option ?? {};
  const [isScrolling, setIsScrolling] = useState<boolean>(false);
  const prevPosition = useRef<Position | undefined>();
  const latestScrolling = useLatest(isScrolling);

  useEffectWithTarget(
    () => {
      const scrollEl = getTargetElement(target, document);
      if (!scrollEl) return;
      const getIsScrolling = () => {
        const newPosition = getCurrentScrollPosition(scrollEl);
        if (!prevPosition.current) {
          prevPosition.current = newPosition;
          return;
        }
        const delay = latestScrolling.current ? 500 : 100;
        setRafTimeout(() => {
          if (scrollDirection === 'vertical') {
            setIsScrolling(prevPosition.current?.top !== newPosition.top);
          } else if (scrollDirection === 'horizontal') {
            setIsScrolling(prevPosition.current?.left !== newPosition.left);
          }
        }, delay);
        prevPosition.current.top = newPosition.top;
        prevPosition.current.left = newPosition.left;
      };
      getIsScrolling();
      scrollEl.addEventListener('scroll', getIsScrolling);
      return () => {
        scrollEl.removeEventListener('scroll', getIsScrolling);
      };
    },
    [],
    target,
  );

  return isScrolling;
};

export default useIsScrolling;

demo

import React, { useRef } from 'react';
import { useIsScrolling } from 'ahooks';

export default () => {
  const ref = useRef(null);
  const scroll = useIsScrolling({ target: ref });
  return (
    <>
      <p>{JSON.stringify(scroll)}</p>
      <div
        style={{
          height: '160px',
          width: '160px',
          border: 'solid 1px #000',
          overflow: 'scroll',
          whiteSpace: 'nowrap',
          fontSize: '32px',
        }}
        ref={ref}
      >
        <div>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aspernatur atque, debitis ex
          excepturi explicabo iste iure labore molestiae neque optio perspiciatis
        </div>
        <div>
          Aspernatur cupiditate, deleniti id incidunt mollitia omnis! A aspernatur assumenda
          consequuntur culpa cumque dignissimos enim eos, et fugit natus nemo nesciunt
        </div>
        <div>
          Alias aut deserunt expedita, inventore maiores minima officia porro rem. Accusamus ducimus
          magni modi mollitia nihil nisi provident
        </div>
        <div>
          Alias aut autem consequuntur doloremque esse facilis id molestiae neque officia placeat,
          quia quisquam repellendus reprehenderit.
        </div>
        <div>
          Adipisci blanditiis facere nam perspiciatis sit soluta ullam! Architecto aut blanditiis,
          consectetur corporis cum deserunt distinctio dolore eius est exercitationem
        </div>
        <div>Ab aliquid asperiores assumenda corporis cumque dolorum expedita</div>
        <div>
          Culpa cumque eveniet natus totam! Adipisci, animi at commodi delectus distinctio dolore
          earum, eum expedita facilis
        </div>
        <div>
          Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
          dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
        </div>
        <div>
          Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
          dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
        </div>
        <div>
          Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
          dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
        </div>
        <div>
          Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
          dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
        </div>
        <div>
          Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
          dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
        </div>
      </div>
    </>
  );
};

演示

20240131111416

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or requestv4

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions