useScroll

跟踪滚动位置和统计数据

useScroll 响应式地跟踪目标元素(或 window/document)的滚动位置和状态。它返回一个包含 x 位置、y 位置、isScrolling 布尔值、arrivedState 对象({ top, bottom, left, right } 布尔值表示是否到达边缘)和 directions 对象(表示当前滚动方向)的元组。该 hook 支持节流、偏移配置和滚动及滚动结束事件的回调。

使用场景

  • 实现滚动相关的 UI,如粘性头部、滚动进度条或”回到顶部”按钮
  • 检测用户是否已滚动到容器的顶部或底部(例如用于加载更多内容)
  • 跟踪滚动方向以显示或隐藏导航元素

注意事项

  • SSR 安全:在服务端渲染期间位置返回 0isScrolling 返回 false,到达/方向状态返回默认值。服务端不会附加滚动监听器。
  • 节流:使用 throttle 选项限制滚动处理器触发频率,提高复杂滚动 UI 更新的性能。
  • 相关 hooks:参见 useInfiniteScroll 了解自动加载更多触发器,useScrollLock 了解禁用滚动,以及 useScrollIntoView 了解程序化滚动。

Usage

Live Editor

function Demo() {
  const elementRef = useRef<HTMLDivElement>(null);
  const [x, y, isScrolling, arrivedState, directions] = useScroll(elementRef);
  const { left, right, top, bottom } = useMemo(
    () => arrivedState,
    [arrivedState],
  );
  const {
    left: toLeft,
    right: toRight,
    top: toTop,
    bottom: toBottom,
  } = useMemo(() => directions, [directions]);

  const absoluteStyle: CSSProperties = {
    paddingTop: "0.25rem",
    paddingBottom: "0.25rem",
    paddingLeft: "0.5rem",
    paddingRight: "0.5rem",
    position: "absolute",
  };
  return (
    <div style={{ display: "flex" }}>
      <div
        ref={elementRef}
        style={{
          width: 300,
          height: 300,
          margin: "auto",
          borderRadius: "0.25rem",
          overflow: "scroll",
        }}
      >
        <div style={{ width: 500, height: 400, position: "relative" }}>
          <div
            style={{
              ...absoluteStyle,
              top: "0rem",
              left: "0rem",
            }}
          >
            左上角
          </div>
          <div
            style={{
              ...absoluteStyle,
              bottom: "0rem",
              left: "0rem",
            }}
          >
            左下角
          </div>
          <div
            style={{
              ...absoluteStyle,
              top: "0rem",
              right: "0rem",
            }}
          >
            右上角
          </div>
          <div
            style={{
              ...absoluteStyle,
              bottom: "0rem",
              right: "0rem",
            }}
          >
            右下角
          </div>
          <div
            style={{
              ...absoluteStyle,
              top: "33.33333%",
              left: "33.33333%",
            }}
          >
            滚动区域
          </div>
        </div>
      </div>
      <div
        style={{
          width: 280,
          margin: "auto",
          paddingLeft: "1rem",
          display: "flex",
          flexDirection: "column",
          gap: 5,
        }}
      >
        <div>
          位置: {x.toFixed(1)}, {y.toFixed(1)}
        </div>
        <div>正在滚动: {JSON.stringify(isScrolling)}</div>
        <div>到达顶部: {JSON.stringify(top)}</div>
        <div>到达右侧: {JSON.stringify(right)}</div>
        <div>到达底部: {JSON.stringify(bottom)}</div>
        <div>到达左侧: {JSON.stringify(left)}</div>
        <div>向上滚动: {JSON.stringify(toTop)}</div>
        <div>向右滚动: {JSON.stringify(toRight)}</div>
        <div>向下滚动: {JSON.stringify(toBottom)}</div>
        <div>向左滚动: {JSON.stringify(toLeft)}</div>
      </div>
    </div>
  );
};
Result

API

useScroll

Returns

readonly [number, number, boolean, UseScrollArrivedState, UseScrollDirection]: 包含以下元素的元组:

  • x 值。
  • y 值。
  • 是否在滚动。
  • 到达边界状态。
  • 滚动方向

Arguments

参数名描述类型默认值
targetdom元素BasicTarget<Element> | Document | Window (必填)-
options可选参数UseScrollOptions | undefined-

UseScrollOptions

参数名描述类型默认值
throttle滚动事件的节流时间,默认关闭。number0
idle滚动结束时的检查时间。当配置 throttle 时,此配置将设置为 (throttle +idle)。number-
offset将到达状态偏移 x 像素UseScrollOffset-
onScroll滚动的回调(e: Event) => void-
onStop滚动结束的回调(e: Event) => void-
eventListenerOptions滚动事件参数boolean | AddEventListenerOptions{capture: false, passive: true}

UseScrollArrivedState

参数名描述类型默认值
left到达左边boolean (必填)-
right到达右边boolean (必填)-
top到达顶部boolean (必填)-
bottom到达底部boolean (必填)-

UseScrollDirection

参数名描述类型默认值
left向左滚动boolean (必填)-
right向右滚动boolean (必填)-
top向上滚动boolean (必填)-
bottom向下滚动boolean (必填)-

BasicTarget

export type BasicTarget<T extends TargetType = Element> = (() => TargetValue<T>) | TargetValue<T> | MutableRefObject<TargetValue<T>>;

TargetValue

type TargetValue<T> = T | undefined | null;

TargetType

type TargetType = HTMLElement | Element | Window | Document | EventTarget;

UseScrollOffset

export interface UseScrollOffset {
  left?: number;
  right?: number;
  top?: number;
  bottom?: number;
}