滚动导航组件的封装
要实现一个如vant库中类似的滚动导航组件
基本监测函数实现实现
可监测传入组件的元素总高度 ,元素垂直方向滚动了的距离 ,元素可视区域高度,是否到达底部
import { onDeactivated, onMounted, onUnmounted, ref } from 'vue';
import { throttle } from 'underscore'
//此函数可传入对应监听的组件,如果没传入,则默认监听window的滚动
export default function useScroll(elRef) {
let el = window
const isReachBottom = ref(false)
const clientHeight = ref(0)
const scrollTop = ref(0)
const scrollHeight = ref(0)
// 防抖/节流
const scrollListenerHandler = throttle(() => {
if (el === window) {
clientHeight.value = document.documentElement.clientHeight
scrollTop.value = document.documentElement.scrollTop
scrollHeight.value = document.documentElement.scrollHeight
} else {
clientHeight.value = el.clientHeight
scrollTop.value = el.scrollTop
scrollHeight.value = el.scrollHeight
}
//当视口高度+已滚动的高度大于元素高度时,说明已到达底部,可进行相应操作拉取数据
if (clientHeight.value + scrollTop.value>= scrollHeight.value) {
console.log("滚动到底部了")
isReachBottom.value = true
}
}, 100)
onMounted(() => {
if (elRef) el = elRef.value
el.addEventListener("scroll", scrollListenerHandler)
})
onUnmounted(() => {
el.removeEventListener("scroll", scrollListenerHandler)
})
//导出的这些值可以在其他地方使用
return { isReachBottom, clientHeight, scrollTop, scrollHeight }
}
获取各个组件的相关内容
我们可以用:ref=‘函数’语法,在组件挂载时执行函数,来获取到页面元素的名字,位置等相关信息
<detail-infos name="描述" :ref="getSectionRef" :top-infos="mainPart.topModule"/>
// 把相关内容都存储在sectionEls中
const getSectionRef = (value) => {
if (!value) return
const name = value.$el.getAttribute("name")
sectionEls.value[name] = value.$el
}
点击跳转
因为我们已经获取到相关dom中的信息,当点击时拿到索引,可以从sectionEls中获取到相关信息,进行跳转,用isclick用来确定是滚动还是点击,防止点击时逐渐变色;
const tabClick = (index) => {
const key = Object.keys(sectionEls.value)[index]
const el = sectionEls.value[key]
let distance = el?.offsetTop
if (index !== 0) {
distance = distance - 44
}
//isclick是用来确定是滚动还是点击,防止点击时逐渐变色
isClick = true
currentDistance = distance
detailRef.value.scrollTo({
top: distance,
behavior: "smooth"
})
}
页面滚动, 滚动时匹配对应的tabControll的index
查找算法,在所有的区域的offsetTops中找第一个offsetTop大于页面滚动距离的前一个组件索引
watch(scrollTop, (newValue) => {
//点击结束
if (newValue === currentDistance) {
isClick = false
}
if (isClick) return
// 1.获取所有的区域的offsetTops
const els = Object.values(sectionEls.value)
const values = els.map(el => el?.offsetTop)
// 2.根据newValue去匹配想要索引
let index = values.length - 1
for (let i = 0; i < values.length; i++) {
if (values[i] > newValue + 44) {
index = i - 1
break
}
}
// console.log(index)
tabControlRef.value?.setCurrentIndex(index)
})