u
This commit is contained in:
1 parent
07e0001b87
commit
46cbb2f50d
543 files changed
+32576
-4231
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { onUnmounted, watch } from 'vue'
|
||||
|
||||
interface UseScrollWatcherOptions {
|
||||
target: Ref<HTMLElement | null>
|
||||
thresholdPercent?: number
|
||||
delay?: number
|
||||
onReachTop?: () => void
|
||||
onReachBottom?: () => void
|
||||
}
|
||||
|
||||
export function useScrollWatcher({
|
||||
target,
|
||||
thresholdPercent = 0.05,
|
||||
delay = 0,
|
||||
onReachTop,
|
||||
onReachBottom,
|
||||
}: UseScrollWatcherOptions) {
|
||||
if (!target)
|
||||
throw new Error('target 必须传入一个 ref')
|
||||
|
||||
let lastScrollTop = 0
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null
|
||||
const topTriggered = { value: false }
|
||||
const bottomTriggered = { value: false }
|
||||
|
||||
const handleScroll = (e: Event) => {
|
||||
const el = e.target as HTMLElement
|
||||
const scrollTop = el.scrollTop
|
||||
const scrollHeight = el.scrollHeight
|
||||
const clientHeight = el.clientHeight
|
||||
const scrollRange = scrollHeight - clientHeight
|
||||
if (scrollRange <= 0)
|
||||
return
|
||||
|
||||
const isScrollingUp = scrollTop < lastScrollTop
|
||||
const isScrollingDown = scrollTop > lastScrollTop
|
||||
lastScrollTop = scrollTop
|
||||
|
||||
const topThreshold = scrollRange * thresholdPercent
|
||||
const bottomThreshold = scrollRange * thresholdPercent
|
||||
|
||||
if (timeout)
|
||||
clearTimeout(timeout)
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
if (isScrollingUp && scrollTop <= topThreshold && !topTriggered.value) {
|
||||
onReachTop?.()
|
||||
topTriggered.value = true
|
||||
}
|
||||
else if (scrollTop > topThreshold) {
|
||||
topTriggered.value = false
|
||||
}
|
||||
|
||||
if (isScrollingDown && scrollHeight - scrollTop - clientHeight <= bottomThreshold && !bottomTriggered.value) {
|
||||
onReachBottom?.()
|
||||
bottomTriggered.value = true
|
||||
}
|
||||
else if (scrollHeight - scrollTop - clientHeight > bottomThreshold) {
|
||||
bottomTriggered.value = false
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
const bindScroll = (el: HTMLElement | null) => {
|
||||
if (!el)
|
||||
return
|
||||
el.addEventListener('scroll', handleScroll)
|
||||
lastScrollTop = el.scrollTop
|
||||
}
|
||||
|
||||
const unbindScroll = (el: HTMLElement | null) => {
|
||||
if (!el)
|
||||
return
|
||||
el.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
|
||||
// 监听 ref
|
||||
watch(target, (el, oldEl) => {
|
||||
if (oldEl)
|
||||
unbindScroll(oldEl)
|
||||
if (el)
|
||||
bindScroll(el)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (target.value)
|
||||
unbindScroll(target.value)
|
||||
if (timeout)
|
||||
clearTimeout(timeout)
|
||||
})
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (target.value)
|
||||
target.value.scrollTop = 0
|
||||
}
|
||||
|
||||
const scrollToBottom = () => {
|
||||
if (target.value) {
|
||||
const el = target.value
|
||||
el.scrollTop = el.scrollHeight - el.clientHeight
|
||||
}
|
||||
}
|
||||
|
||||
return { scrollToTop, scrollToBottom }
|
||||
}
|
||||
Reference in new issue
Block a user