目前用的两种方法实现:
第一种:原生js实现
注意:因为移动端可滚动区域可能会嵌套在其他架子下,所以需要用到ref获取滚动区域,
正常获取scrollTop:前者基于html,后者基于body
scrollTop = document.documentElement.scrollTop || document.body.scrollTop
获取指定滚动区域(想要谁滚动就指定谁)
scrollTop = commissionDataMainRef.value.scrollTop
首先div上声明ref
其次,在setup里
const commissionDataMainRef = ref();
然后return 出去
return { commissionDataMainRef }
最后就可以在js中用
commissionDataMainRef.value.scrollTop
代码如下:
定义导航栏
<ul class="navs">
<li v-for="(item, index) in indexList" :key="item" :class="{active: state.active===index}" @click="scrollTo(index)" >
{{item}}
</li>
</ul>
indexList.value = ['排名', '极速', '非极速', '退款', '积分']
声明方法
setup(props, { attrs, slots, emit, expose }) {
const state = reactive({
active: 0,
currentActive: null,
scrollTopLength: 0,
timer: null
})
const commissionDataMainRef = ref()
let indexList = ref([])
onBeforeRouteLeave((to, from, next) => {
window.removeEventListener('scroll', handleScroll)
next()
})
// 提成看板角色判断
const initCommission = async () => {
indexList.value = state.commissionType.teamType === '极速' ? ['排名', '极速', '非极速', '退款', '积分'] : ['排名', '非极速', '极速', '退款', '积分']
}
initCommission()
const initData = async () => {
scrollTo(props.workSpace === 'workSpaceIntegral' ? 4 : 0)
}
const handleScroll = () => {
// 获取所有锚点元素
const navContents = document.querySelectorAll('.scrollCommission>div')
// 所有锚点元素的 offsetTop
const offsetTopArr = []
navContents.forEach(item => {
offsetTopArr.push(item.offsetTop)
})
// 获取当前文档流的 scrollTop
clearTimeout(state.timer)
state.timer = setTimeout(isScrollEnd, 100);
const scrollTop = commissionDataMainRef.value.scrollTop
state.scrollTopLength = scrollTop
// 定义当前点亮的导航下标
let navIndex = 0
for (let n = 0; n < offsetTopArr.length; n++) {
// 如果 scrollTop 大于等于第n个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
// 那么此时导航索引就应该是n了
if (scrollTop >= offsetTopArr[n]) {
navIndex = n
// state.currentActive && (state.currentActive.type = false)
}
}
state.active = state.currentActive ? (state.currentActive.type ? state.currentActive.index : navIndex) : navIndex
// state.currentActive.type = false
}
const isScrollEnd = () => {
const scrollTop = commissionDataMainRef.value.scrollTop
if(scrollTop == state.scrollTopLength){
state.currentActive && (state.currentActive.type = false)
}
}
// 跳转到指定索引的元素
const scrollTo = (index) => {
state.active = index
state.currentActive = {
type: true,
index
}
// 获取目标的 offsetTop
// css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
const targetOffsetTop = document.querySelector(`.scrollCommission>div:nth-child(${index + 1})`).offsetTop
// 获取当前 offsetTop
// let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
let scrollTop = commissionDataMainRef.value.scrollTop
// 定义一次跳 50 个像素,数字越大跳得越快,但是会有掉帧得感觉,步子迈大了会扯到蛋
const STEP = 50
// 判断是往下滑还是往上滑
if (scrollTop > targetOffsetTop) {
// 往上滑
smoothUp()
} else {
// 往下滑
smoothDown()
}
// 定义往下滑函数
function smoothDown() {
// 如果当前 scrollTop 小于 targetOffsetTop 说明视口还没滑到指定位置
if (scrollTop < targetOffsetTop) {
// 如果和目标相差距离大于等于 STEP 就跳 STEP
// 否则直接跳到目标点,目标是为了防止跳过了。
if (targetOffsetTop - scrollTop >= STEP) {
scrollTop += STEP
} else {
scrollTop = targetOffsetTop
}
// document.body.scrollTop = scrollTop
// document.documentElement.scrollTop = scrollTop
commissionDataMainRef.value.scrollTop = scrollTop
// 关于 requestAnimationFrame 可以自己查一下,在这种场景下,相比 setInterval 性价比更高
requestAnimationFrame(smoothDown)
}
}
// 定义往上滑函数
function smoothUp() {
if (scrollTop > targetOffsetTop) {
if (scrollTop - targetOffsetTop >= STEP) {
scrollTop -= STEP
} else {
scrollTop = targetOffsetTop
}
// document.body.scrollTop = scrollTop
// document.documentElement.scrollTop = scrollTop
commissionDataMainRef.value.scrollTop = scrollTop
requestAnimationFrame(smoothUp)
}
}
}
// 监听滚动
nextTick(() => {
window.addEventListener('scroll', handleScroll, true)
})
return {
state,
commissionDataMainRef,
indexList,
scrollTo
}
},
第二种:vant组件的索引栏(比较省事,但自定义不好)
vant索引栏:https://vant-contrib.gitee.io/vant/#/zh-CN/index-bar