跳到主要内容

useScrollLock

锁定元素的滚动行为,同时保留嵌套可滚动容器内的滚动功能。

基础用法

实时编辑器
function Demo() {
  const elementRef = useRef<HTMLDivElement>(null);
  const [locked, setLocked] = useScrollLock(elementRef);

  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",
          border: "1px solid var(--ifm-color-emphasis-200)",
        }}
      >
        <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>锁定状态: {JSON.stringify(locked)}</div>
        <button
          onClick={() => {
            setLocked(!locked);
          }}
          style={{
            padding: "8px 16px",
            border: "1px solid var(--ifm-color-emphasis-300)",
            backgroundColor: "var(--ifm-background-color)",
            color: "var(--ifm-color-content)",
            borderRadius: "4px",
            cursor: "pointer",
          }}
        >
          {locked ? "解锁" : "锁定"}
        </button>
      </div>
    </div>
  );
};

结果
左上角
左下角
右上角
右下角
滚动区域
锁定状态: false

对话框示例

实时编辑器
function DialogDemo() {
  const { cloneElement, ReactElement, useRef, useState } = React;
  
  const Dialog = ({ trigger }: { trigger: ReactElement }) => {
    const dialogRef = useRef<HTMLDialogElement>(null);
    const boxRef = useRef(null);
    const [isOpen, setIsOpen] = useState(false);
    const [, setLock] = useScrollLock(document.body);

    const open = () => {
      setIsOpen(true);
      dialogRef.current?.showModal();
      setLock(true);
    };

    const close = () => {
      setIsOpen(false);
      dialogRef.current?.close();
      setLock(false);
    };

    useEventListener("close", () => setIsOpen(false), dialogRef);
    useClickOutside(boxRef, close);

    return (
      <>
        {cloneElement(trigger, { onClick: open })}

        <dialog 
          ref={dialogRef}
          style={{
            padding: 0,
            border: "none",
            borderRadius: "8px",
            boxShadow: "0 10px 25px rgba(0, 0, 0, 0.1)",
            maxWidth: "500px",
            width: "90vw",
          }}
        >
          <div 
            ref={boxRef}
            style={{
              padding: "24px",
              maxHeight: "70vh",
              overflow: "auto",
              backgroundColor: "var(--ifm-background-color)",
              borderRadius: "8px",
            }}
          >
            <h3 style={{ margin: "0 0 16px 0", color: "var(--ifm-color-content)" }}>
              可滚动内容的对话框
            </h3>
            <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}>
              这个对话框演示了 useScrollLock 的使用。背景滚动被锁定,
              但你仍然可以在对话框内容中正常滚动。
            </p>
            <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}>
              Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque
              faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi
              pretium tellus duis convallis. Tempus leo eu aenean sed diam urna
              tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas.
              Iaculis massa nisl malesuada lacinia integer nunc posuere.
            </p>
            <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}>
              Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora
              torquent per conubia nostra inceptos himenaeos. Lorem ipsum dolor 
              sit amet consectetur adipiscing elit. Quisque faucibus ex sapien 
              vitae pellentesque sem placerat.
            </p>
            <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}>
              In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean 
              sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus 
              bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere.
            </p>
            <p style={{ lineHeight: "1.6", color: "var(--ifm-color-content-secondary)", margin: "16px 0" }}>
              Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora
              torquent per conubia nostra inceptos himenaeos. Sed cursus turpis 
              a purus aliquam condimentum. Nam bibendum cursus dolor.
            </p>
            <div style={{ 
              display: "flex", 
              justifyContent: "flex-end", 
              marginTop: "24px",
              gap: "12px"
            }}>
              <button
                onClick={close}
                style={{
                  padding: "8px 16px",
                  border: "1px solid var(--ifm-color-emphasis-300)",
                  backgroundColor: "var(--ifm-background-color)",
                  color: "var(--ifm-color-content)",
                  borderRadius: "4px",
                  cursor: "pointer",
                }}
              >
                关闭
              </button>
            </div>
          </div>
        </dialog>
      </>
    );
  };

  return (
    <div style={{ padding: "20px", textAlign: "center" }}>
      <Dialog 
        trigger={
          <button
            style={{
              padding: "12px 24px",
              border: "1px solid var(--ifm-color-primary)",
              backgroundColor: "var(--ifm-color-primary)",
              color: "var(--ifm-color-white)",
              borderRadius: "4px",
              cursor: "pointer",
              fontSize: "16px",
            }}
          >
            打开对话框
          </button>
        } 
      />
      <p style={{ 
        marginTop: "16px", 
        color: "var(--ifm-color-content-secondary)", 
        fontSize: "14px",
        maxWidth: "400px",
        margin: "16px auto 0"
      }}>
        打开对话框并尝试滚动。背景滚动将被锁定,
        但你可以在对话框内容中正常滚动。
      </p>
    </div>
  );
}

结果

可滚动内容的对话框

这个对话框演示了 useScrollLock 的使用。背景滚动被锁定, 但你仍然可以在对话框内容中正常滚动。

Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere.

Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat.

In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere.

Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos. Sed cursus turpis a purus aliquam condimentum. Nam bibendum cursus dolor.

打开对话框并尝试滚动。背景滚动将被锁定, 但你可以在对话框内容中正常滚动。

API

useScrollLock

Returns

readonly [boolean, (flag: boolean) => void]: 包含以下元素的元组:

  • 是否锁定。
  • 更新锁定值的函数。

Arguments

参数名描述类型默认值
targetdom元素BasicTarget<HTMLElement> (必填)-
initialState默认值boolean | undefinedfalse

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;
ads via Carbon