Files
basic/apps/core/src/components/AppBackToTop/index.vue
T
2026-04-01 03:38:00 +08:00

70 lines
1.8 KiB
Vue
Executable File

<script setup lang="ts">
import { useTimeoutFn } from '@vueuse/core'
defineOptions({
name: 'AppBackToTop',
})
const transitionClass = {
enterActiveClass: 'ease-out duration-300',
enterFromClass: 'opacity-0 translate-y-4 lg-translate-y-0 lg-scale-95',
enterToClass: 'opacity-100 translate-y-0 lg-scale-100',
leaveActiveClass: 'ease-in duration-200',
leaveFromClass: 'opacity-100 translate-y-0 lg-scale-100',
leaveToClass: 'opacity-0 translate-y-4 lg-translate-y-0 lg-scale-95',
}
onMounted(() => {
window.addEventListener('scroll', handleScroll)
handleScroll()
})
onBeforeUnmount(() => {
window.removeEventListener('scroll', handleScroll)
})
let timeout: (() => void) | undefined
const show = ref(false)
const scrollTop = ref(0)
function handleScroll() {
scrollTop.value = document.documentElement.scrollTop
}
watch(scrollTop, (val) => {
if (val >= 200) {
handleMouseenter()
handleMouseleave()
}
})
function handleMouseenter() {
timeout?.()
// 此处setTimeout是为了避免移动端下和click事件同时触发
setTimeout(() => {
show.value = true
}, 0)
}
function handleMouseleave() {
;({ stop: timeout } = useTimeoutFn(() => {
show.value = false
}, 2000))
}
function handleBackToTop() {
show.value && document.documentElement.scrollTo({
top: 0,
behavior: 'smooth',
})
}
</script>
<template>
<Teleport to="body">
<Transition v-bind="transitionClass">
<FaButton v-if="scrollTop >= 200" variant="outline" size="icon" class="rounded-full h-12 w-12 transition-all inset-b-4 fixed z-2000 dark:bg-background -inset-e-9 dark:hover:bg-background/50" :class="{ 'inset-e-3!': show }" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" @click="handleBackToTop">
<FaIcon name="i-icon-park-outline:to-top-one" class="size-6" />
</FaButton>
</Transition>
</Teleport>
</template>