tab与滚动条联动
2022-02-24 本文已影响0人
拐服第一大码猴
搭配vantUI,点击tab滚动到对应位置,滚动时激活对应tab,原生js,纵享丝滑~~~
<template>
<div class="back_warp">
<div class="letGodBaike-container" ref="scrollEl" @scroll="onScroll">
<div class="letGodBaike-header">
<div class="letGodBaike-header-banner" ref="banner">
<img
src="https://img-nos.yiyouliao.com/inforec-20220224-bd38ba5a0bb3e99b610eff52d42a417d.jpg?time=1645704280&signature=BB2E3E5E20460CA4863C2CFD02775E01"
alt=""
/>
</div>
<!-- tabs -->
<div class="letGodBaike-header-tab" ref="headerTab">
<van-tabs
ref="vantab"
:class="['letGodBaike-header-fixed', fixed]"
v-model="active"
title-active-color="#333"
title-inactive-color="#808080"
color="#fff"
@click="elToScroll"
>
<van-tab v-for="item in list" :key="item">
<strong slot="title">{{ item }}</strong>
</van-tab>
</van-tabs>
</div>
</div>
<!-- 内容 -->
<div class="letGodBaike-list">
<ul
class="letGodBaike-list-ul"
v-for="(ul, index) in list"
:key="ul"
:ref="'ul' + index"
>
<li class="letGodBaike-list-ul-title">{{ ul }}</li>
<li class="letGodBaike-list-ul-li">
<div
class="letGodBaike-list-ul-li-item"
v-for="li in cList[ul]"
:key="li"
>
{{ li }}
</div>
</li>
</ul>
</div>
<!-- 到顶部 -->
<div v-show="fixed" class="letGodBaike-up" @click="run(0)"></div>
</div>
</div>
</template>
<script>
export default {
head() {
return {
title: this.$route.query.title,
}
},
data() {
return {
active: 0,
list: [
'新手入门',
'任务查询',
'地图',
'图鉴汇总',
'怪物图鉴',
'情场高手',
],
cList: {
新手入门: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
任务查询: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
地图: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
图鉴汇总: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
怪物图鉴: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
情场高手: ['入门tips1', '入门tips2', '入门tips3', '入门tips4'],
},
fixed: '',
}
},
mounted() {
console.log(
document
.querySelectorAll('.letGodBaike-list-ul')[0]
.getBoundingClientRect().top
)
},
methods: {
// 滚动到对应位置激活tab
scrollToEl() {
try {
let tabHeight = this.$refs.vantab.tabHeight
let domList = document.querySelectorAll('.letGodBaike-list-ul')
let list = [...domList].filter((v, i) => {
// 获取Ul距离屏幕顶部的距离
let top = v.getBoundingClientRect().top
// 当距离在tab高度误差范围内,激活tab
if (tabHeight - 30 < top && top < tabHeight + 30) {
this.active = i
}
})
} catch (error) {
throw error
}
},
// 监听滚动事件
onScroll({ target }) {
this.$nextTick(() => {
let bannerHeight = this.$refs.banner.offsetHeight
// 当滚动距离大于banner的高度时,tabs吸顶
if (target.scrollTop >= bannerHeight) {
this.fixed = 'fixed'
let tabHeight = this.$refs.vantab.tabHeight
this.$refs.headerTab.style.minHeight = tabHeight + 'px'
this.scrollToEl()
} else this.fixed = ''
})
},
// 点击tab滚动到对应的位置
elToScroll(index) {
try {
let screenHeight = window.innerHeight
let scrollHeight = this.$refs.scrollEl.scrollHeight
let lastHeight = scrollHeight - screenHeight
let ulHeight = this.$refs[`ul${index}`][0].offsetTop - 53
// 当位置在最后一屏时,滚动距离为容器高度减去屏幕高度
if (ulHeight > lastHeight) {
this.run(lastHeight)
} else {
this.run(ulHeight)
}
} catch (error) {
throw error
}
},
// 滚动速度
speedFn(time, height) {
return height / time
},
// 滚动函数
run(ulHeight) {
// 1s执行速度
let speed = Math.max(5, this.speedFn(1000, ulHeight))
this.myIntval(() => {
// 向上滚动
if (this.$refs.scrollEl.scrollTop > ulHeight) {
this.$refs.scrollEl.scrollTop -= speed
if (this.$refs.scrollEl.scrollTop < ulHeight) {
return false
}
// 向下滚动
} else {
this.$refs.scrollEl.scrollTop += speed
if ((+this.$refs.scrollEl.scrollTop).toFixed(0) >= ulHeight) {
return false
}
}
return true
}, 1)
},
// Intval
myIntval(fn, time) {
setTimeout(() => {
if (fn()) {
this.myIntval(fn, time)
}
}, time)
},
},
}
</script>
<style lang="scss" scoped>
.back_warp {
background-color: #fff;
height: 100%;
.letGodBaike-container {
background-color: #fff;
min-height: 100%;
height: 100%;
color: #808080;
overflow-y: auto;
font-size: 14px;
overscroll-behavior: none;
-webkit-overflow-scrolling: touch;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.letGodBaike-header {
// 百科banner
.letGodBaike-header-banner {
img {
width: 375px;
height: 276px;
}
}
// 百科tab
.letGodBaike-header-tab {
.letGodBaike-header-fixed {
&.fixed {
position: fixed;
top: 0;
}
}
}
}
// 百科列表
.letGodBaike-list {
padding: 0 16px 276px 16px;
.letGodBaike-list-ul {
.letGodBaike-list-ul-title {
width: 67px;
padding: 3px 0;
text-align: center;
background: #f96e31;
border-radius: 2px;
font-size: 10px;
color: #fff;
margin-top: 18px;
user-select: none;
}
.letGodBaike-list-ul-li {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
.letGodBaike-list-ul-li-item {
margin-top: 8px;
width: 110px;
background: #f6f8f9;
border-radius: 2px;
font-size: 14px;
color: #333333;
padding: 5px 0;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
&:active {
background: #f2f2f2;
}
}
}
}
}
// 到顶
.letGodBaike-up {
width: 112px;
height: 122px;
position: fixed;
bottom: 24px;
right: 0px;
background: url('~@/assets/imgs/leiGodApp/leiGodBaike_up.png') center
center no-repeat;
}
}
}
</style>