一、组件介绍
Backtop组件通常用于页面向下滚动后,提供快捷的返回顶部的按钮。
1.1 属性
- target: string类型,滚动触发的对象,传入css元素选择器,默认是
document
; - visibility-height: number类型,滚动对象滚动高度达到此值时,展示
Backtop
组件; - right/bottom: number类型,控制显示位置,距离右侧/底部的距离。
1.2 事件
- click: 点击组件时触发
二、源码分析
2.1 template
<template>
// 官方过渡组件,提供过渡效果
<transition name="el-fade-in">
<div
v-if="visible"
:style="{
'right': styleRight,
'bottom': styleBottom
}"
class="el-backtop"
@click.stop="handleClick"
>
// 默认插槽
<slot>
// 默认显示top icon
<i class="el-icon-caret-top"></i>
</slot>
</div>
</transition>
</template>
2.2 script
setup(props: IElBacktopProps, ctx) {
const el = ref(null)
const container = ref(null)
const visible = ref(false)
const styleBottom = computed(() => `${props.bottom}px`)
const styleRight = computed(() => `${props.right}px`)
const scope = 'ElBackTop'
// 滚动到顶部
const scrollToTop = () => {
const beginTime = Date.now()
const beginValue = el.value.scrollTop
// 兼容浏览器不支持requestAnimationFrame的情况
const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16))
// 回调函数
const frameFunc = () => {
const progress = (Date.now() - beginTime) / 500
if (progress < 1) {
// 不是一下子直接返回顶部,而是有一个向上的滚动过程
el.value.scrollTop = beginValue * (1 - easeInOutCubic(progress))
// 递归调用自身
rAF(frameFunc)
} else {
// 500ms后 中断递归
el.value.scrollTop = 0
}
}
rAF(frameFunc)
}
// 容器滚动事件的处理函数
const onScroll = () => {
visible.value = el.value.scrollTop >= props.visibilityHeight
}
const handleClick = event => {
scrollToTop()
ctx.emit('click', event)
}
// 节流
const throttledScrollHandler = throttle(onScroll, 300)
onMounted(() => {
// 默认滚动容器是document
container.value = document
el.value = document.documentElement
// 指定容器的情况
if (props.target) {
el.value = document.querySelector(props.target)
if (!el.value) {
throwError(scope, `target is not existed: ${props.target}`)
}
container.value = el.value
}
// 滚动容器注册scroll事件
on(container.value, 'scroll', throttledScrollHandler)
})
onBeforeUnmount(() => {
// 注销scroll事件
off(container.value, 'scroll', throttledScrollHandler)
})
return {
el,
container,
visible,
styleBottom,
styleRight,
handleClick,
}
}
2.3 总结
采用浏览器的requestAnimationFrame
API,并在其回调函数中递归调用自身,达到向上滚动的动态效果,避免出现直接滚动到顶部的突兀感,在500ms后滚动至顶部。