useScroll
作用
监听元素的滚动位置
原理
监听目标的
scroll
的事件,更新目标位置信息
源码
ts
import useRafState from '../useRafState'
import useLatest from '../useLatest'
import type { BasicTarget } from '../utils/domTarget'
import { getTargetElement } from '../utils/domTarget'
import useEffectWithTarget from '../utils/useEffectWithTarget'
type Position = { left: number; top: number }
export type Target = BasicTarget<Element | Document>
export type ScrollListenController = (val: Position) => boolean
function useScroll(
target?: Target,
shouldUpdate: ScrollListenController = () => true,
): Position | undefined {
// 保存位置信息
const [position, setPosition] = useRafState<Position>()
// 保存 shouldUpdate 的最新值
const shouldUpdateRef = useLatest(shouldUpdate)
// 监听目标的 scroll 事件
useEffectWithTarget(
() => {
// 获取目标元素
const el = getTargetElement(target, document)
// 如果目标元素不存在,则直接返回
if (!el) {
return
}
// 更新位置信息的方法
const updatePosition = () => {
let newPosition: Position
// 如果目标元素是 document,则获取 document 的滚动位置
if (el === document) {
if (document.scrollingElement) {
newPosition = {
left: document.scrollingElement.scrollLeft,
top: document.scrollingElement.scrollTop,
}
} else {
// When in quirks mode, the scrollingElement attribute returns the HTML body element if it exists and is potentially scrollable, otherwise it returns null.
// https://developer.mozilla.org/zh-CN/docs/Web/API/Document/scrollingElement
// https://stackoverflow.com/questions/28633221/document-body-scrolltop-firefox-returns-0-only-js
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: (el as Element).scrollLeft,
top: (el as Element).scrollTop,
}
}
if (shouldUpdateRef.current(newPosition)) {
setPosition(newPosition)
}
}
// 初始化位置信息
updatePosition()
// 监听 scroll 事件
el.addEventListener('scroll', updatePosition)
// 返回移除监听的方法
return () => {
el.removeEventListener('scroll', updatePosition)
}
},
[],
target,
)
return position
}
export default useScroll