优化返回顶部图标

This commit is contained in:
jingrow 2025-11-02 03:16:34 +08:00
parent f3d7bca84c
commit c732f72d08

View File

@ -3,8 +3,13 @@
<div
v-if="visible"
class="back-to-top-btn"
@click="scrollToTop"
role="button"
tabindex="0"
:aria-label="t('Back to top')"
:title="t('Back to top')"
@click="scrollToTop"
@keydown.enter="scrollToTop"
@keydown.space.prevent="scrollToTop"
>
<!-- SVG 圆形进度条 -->
<svg class="progress-ring" viewBox="0 0 48 48">
@ -109,9 +114,7 @@ const getScrollInfo = (container: Element | Window): { scrollTop: number; scroll
//
const scrollToTop = () => {
const containers = registeredContainers.value.length > 0
? registeredContainers.value
: findScrollContainers()
const containers = registeredContainers.value
containers.forEach(container => {
if (container === window) {
@ -129,43 +132,54 @@ const scrollToTop = () => {
})
}
// 使 requestAnimationFrame
let rafId: number | null = null
//
const handleScroll = () => {
const containers = registeredContainers.value.length > 0
? registeredContainers.value
: findScrollContainers()
if (containers.length === 0) {
// 使 requestAnimationFrame
if (rafId !== null) {
return
}
//
let maxScrollTop = 0
let maxProgress = 0
containers.forEach(container => {
const info = getScrollInfo(container)
const scrollTop = info.scrollTop
const maxScroll = info.scrollHeight - info.clientHeight
rafId = requestAnimationFrame(() => {
const containers = registeredContainers.value
if (scrollTop > maxScrollTop) {
maxScrollTop = scrollTop
if (containers.length === 0) {
rafId = null
return
}
//
if (maxScroll > 0) {
const progress = Math.min(scrollTop / maxScroll, 1)
if (progress > maxProgress) {
maxProgress = progress
//
let maxScrollTop = 0
let maxProgress = 0
containers.forEach(container => {
const info = getScrollInfo(container)
const scrollTop = info.scrollTop
const maxScroll = info.scrollHeight - info.clientHeight
if (scrollTop > maxScrollTop) {
maxScrollTop = scrollTop
}
}
//
if (maxScroll > 0) {
const progress = Math.min(scrollTop / maxScroll, 1)
if (progress > maxProgress) {
maxProgress = progress
}
}
})
//
scrollProgress.value = maxProgress
//
visible.value = maxScrollTop > SCROLL_THRESHOLD
rafId = null
})
//
scrollProgress.value = maxProgress
//
visible.value = maxScrollTop > SCROLL_THRESHOLD
}
onMounted(() => {
@ -192,6 +206,12 @@ onUnmounted(() => {
container.removeEventListener('scroll', handleScroll)
})
registeredContainers.value = []
// requestAnimationFrame
if (rafId !== null) {
cancelAnimationFrame(rafId)
rafId = null
}
})
</script>
@ -219,6 +239,15 @@ onUnmounted(() => {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.back-to-top-btn:focus {
outline: 2px solid #22c55e;
outline-offset: 2px;
}
.back-to-top-btn:focus:not(:focus-visible) {
outline: none;
}
.progress-ring {
position: absolute;
top: 0;