diff --git a/config/hooks.ts b/config/hooks.ts index 26ffff428a..04f70b18ef 100644 --- a/config/hooks.ts +++ b/config/hooks.ts @@ -101,6 +101,7 @@ export const menus = [ 'useScroll', 'useSize', 'useFocusWithin', + 'useStickyFixed', ], }, { diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 55c7232b0d..c84d76f81b 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -76,6 +76,7 @@ import useWebSocket from './useWebSocket'; import useWhyDidYouUpdate from './useWhyDidYouUpdate'; import useMutationObserver from './useMutationObserver'; import useTheme from './useTheme'; +import useStickyFixed from './useStickyFixed'; export { useRequest, @@ -158,4 +159,5 @@ export { useResetState, useMutationObserver, useTheme, + useStickyFixed, }; diff --git a/packages/hooks/src/useStickyFixed/demo/demo1.tsx b/packages/hooks/src/useStickyFixed/demo/demo1.tsx new file mode 100644 index 0000000000..048f193c38 --- /dev/null +++ b/packages/hooks/src/useStickyFixed/demo/demo1.tsx @@ -0,0 +1,47 @@ +/** + * title: Basic usage + * desc: Need to input the sticky positioning element target and the rolling container scrollTarget, ScrollTarget defaults to document + * + * title.zh-CN: 基础用法 + * desc.zh-CN: 需要传入粘性定位元素target和滚动容器scrollTarget, scrollTarget默认为document + * + */ + +import React, { useRef, useState } from 'react'; +import { useStickyFixed } from 'ahooks'; + +export default () => { + const [topV, setTopV] = useState(0); + + const targetRef = useRef(null); + const scrollTargetRef = useRef(null); + + const [isFixed] = useStickyFixed(targetRef, { scrollTarget: scrollTargetRef }); + + const fixedStyle = { background: 'pink' }; + + return ( + <> +
+ top: setTopV(Number(e.target.value))} /> +
+ +
+
top content
+
+ sticky dom +
+
bottom content
+
+ +
isFixed :{`${isFixed}`}
+ + ); +}; diff --git a/packages/hooks/src/useStickyFixed/demo/demo2.tsx b/packages/hooks/src/useStickyFixed/demo/demo2.tsx new file mode 100644 index 0000000000..9b11821d74 --- /dev/null +++ b/packages/hooks/src/useStickyFixed/demo/demo2.tsx @@ -0,0 +1,46 @@ +/** + * title: Pass in DOM element + * desc: Pass in a function that returns the DOM element. + * + * title.zh-CN: 传入 DOM 元素 + * desc.zh-CN: 传入 function 并返回一个 dom 元素。 + * + */ + +import React, { useState } from 'react'; +import { useStickyFixed } from 'ahooks'; + +export default () => { + const [topV, setTopV] = useState(0); + + const [isFixed] = useStickyFixed(() => document.getElementById('target'), { + scrollTarget: () => document.getElementById('scrollTarget'), + }); + + const fixedStyle = { background: 'pink' }; + + return ( + <> +
+ top: setTopV(Number(e.target.value))} /> +
+ +
+
top content
+
+ sticky dom +
+
bottom content
+
+ +
isFixed :{`${isFixed}`}
+ + ); +}; diff --git a/packages/hooks/src/useStickyFixed/index.en-US.md b/packages/hooks/src/useStickyFixed/index.en-US.md new file mode 100644 index 0000000000..6c1dc711d4 --- /dev/null +++ b/packages/hooks/src/useStickyFixed/index.en-US.md @@ -0,0 +1,47 @@ +--- +nav: + path: /hooks +--- + +# useStickyFixed + +Observe whether the element of 'position: sticky' is in a fixed suction state + +## Examples + +### Basic usage + + + +### Pass in DOM element + + + +## API + +```typescript +const [isFixed] = useStickyFixed(targetRef, { scrollTarget }); +``` + +### Params + +| Property | Description | Type | Default | +| -------- | ---------------------------------------- | ----------------------------------------------------------- | ------- | +| target | `position: sticky` 's Dom element or ref | `() => Element` \| `Element` \| `MutableRefObject` | - | +| options | More config | `Options` | - | + +### Options + +| Property | Description | Type | Default | +| ------------- | --------------------------------------------------- | ----------------------------------------------------------- | ------- | +| scrollTarget | The element or ref of the DOM in the scrolling area | `() => Element` \| `Element` \| `MutableRefObject` | document| + + +### Result + +| Property | Description | Type | +| ------------- | ------------------------------------------------------ | --------- | +| isFixed | Is the `position: sticky` 's Dom in the 'fixed' state | `boolean` | + + + diff --git a/packages/hooks/src/useStickyFixed/index.ts b/packages/hooks/src/useStickyFixed/index.ts new file mode 100644 index 0000000000..e690ae1a98 --- /dev/null +++ b/packages/hooks/src/useStickyFixed/index.ts @@ -0,0 +1,48 @@ +import { useRef } from 'react'; +import { getTargetElement, type BasicTarget } from '../utils/domTarget'; +import useEffectWithTarget from '../utils/useEffectWithTarget'; +import useRafState from '../useRafState'; + +function useStickyFixed( + target: BasicTarget, + options?: { + scrollTarget?: BasicTarget; + }, +): [boolean] { + const { scrollTarget } = options || {}; + + const [state, setState] = useRafState(false); + const lastTopRef = useRef(0); + + useEffectWithTarget( + () => { + const scrollElement = getTargetElement(scrollTarget, document); + if (!scrollElement) { + return; + } + + const stickyElement = getTargetElement(target); + if (!stickyElement) { + return; + } + + const handleScroll = () => { + const rect = stickyElement.getBoundingClientRect(); + const currentTop = rect.top; + const lastTop = lastTopRef.current; + setState(currentTop === lastTop); + lastTopRef.current = currentTop; + }; + + scrollElement.addEventListener('scroll', handleScroll); + return () => { + scrollElement.removeEventListener('scroll', handleScroll); + }; + }, + [], + target, + ); + + return [state]; +} +export default useStickyFixed; diff --git a/packages/hooks/src/useStickyFixed/index.zh-CN.md b/packages/hooks/src/useStickyFixed/index.zh-CN.md new file mode 100644 index 0000000000..c80f7672e9 --- /dev/null +++ b/packages/hooks/src/useStickyFixed/index.zh-CN.md @@ -0,0 +1,43 @@ +--- +nav: + path: /hooks +--- + +# useStickyFixed + +观察粘性定位(`position: sticky`)的元素,是否处于吸顶固定状态 + +## 代码演示 + +### 基础用法 + + + +### 传入 DOM 元素 + + + +## API + +```typescript +const [isFixed] = useStickyFixed(targetRef, { scrollTarget }); +``` + +### Params + +| 参数 | 说明 | 类型 | 默认值 | +| ------- | -------------------------------- | ---------------------------------------------------------- | ------ | +| target | 粘性定位的 DOM 节点或者 Ref 对象 | `Element` \|`() => Element` \| `MutableRefObject` | - | +| options | 额外的配置项 | `Options` | - | + +### Options + +| 参数 | 说明 | 类型 | 默认值 | +| ------------ | -------------------------------- | ------------------------------------------------------------------------ | -------- | +| scrollTarget | 滚动区域的 DOM 节点或者 Ref 对象 | `Element` \| `Document` \|`() => Element` \| `MutableRefObject` | document | + +### Result + +| 参数 | 说明 | 类型 | +| ------- | --------------------------------- | --------- | +| isFixed | 粘性定位元素是否处于 `fixed` 状态 | `boolean` |