tab切換錨點(diǎn)效果及滾動(dòng)時(shí)激活相應(yīng)的tab
效果如下
- H5端
H5端tab切換及tab錨點(diǎn)效果文章來源:http://www.zghlxwxcb.cn/news/detail-768581.html
- 小程序端
小程序端tab切換及tab錨點(diǎn)效果文章來源地址http://www.zghlxwxcb.cn/news/detail-768581.html
代碼實(shí)現(xiàn)-重要幾點(diǎn)
- 渲染頁面循環(huán)數(shù)組時(shí),每一個(gè)item都需要給不重復(fù)的id
//topicList后端返回的數(shù)據(jù)
<view :id="'a' + (i + 1)" v-for="(v,i) in topicList" :key="v.id">
- tab使用uviewUI庫實(shí)現(xiàn)的,list值里面每一個(gè)元素也需要給上id值。如[‘1-20’,‘21-40’,…],那么21-40給的是第21個(gè)item的id。
這里也可以去動(dòng)態(tài)計(jì)算,如后端返回76條,一個(gè)tab數(shù)值間隔為20,那么76則是[‘1-20’,‘21-40’,‘41-60’,‘61-76’],注意數(shù)組的最后一個(gè),結(jié)尾是76而不是80
<template>
<u-tabs :list="tabList" :current="currentTab" @change="changeTab" :bar-style="{'bottom':'6rpx'}"></u-tabs>
</template>
// 根據(jù)后端返回?cái)?shù)據(jù)總條數(shù)計(jì)算tab
getTopicTab() {
// this.topicList.length這是渲染數(shù)據(jù)總條數(shù),20是每個(gè)tab數(shù)值間隔值,cycleIndex則是針對數(shù)據(jù)總條數(shù)一共有幾個(gè)tab
let cycleIndex = Math.ceil(this.topicList.length / 20); // 向上取整6.01 -> 7
let tabArr = [];
for(let i = 1; i <= cycleIndex; i++) {
tabArr.push({
value: 20 * i,
name: ((20 * (i - 1)) + 1) + '-' + (i == cycleIndex ? this.topicList.length : (20 * i)),
id: '#a' + ((20 * (i - 1)) + 1)
});
};
this.tabList = tabArr;
},
- 監(jiān)聽頁面滾動(dòng)周期onPageScroll,針對滾動(dòng)時(shí),該激活哪個(gè)tab。
- 接下來就是一些dom元素高度計(jì)算,使用uni.createSelectorQuery()獲取JQ對象,然后使用select(id/class名).boundingClientRect獲取當(dāng)前id的item距離頂部的top值;根據(jù)頂部基準(zhǔn)的top值和item的top值,之間的差值,通過uni.pageScrollTo滾動(dòng)頁面
- 計(jì)算高度值時(shí),需要注意,頂部是否有其他dom元素,有也需要將該dom元素高度,考慮在內(nèi)
實(shí)現(xiàn)思路
- 進(jìn)入頁面時(shí),循環(huán)渲染數(shù)據(jù)之后,獲取每個(gè)tab對應(yīng)item距離頂部的距離,用數(shù)組distanceArr存起來(后面會(huì)用到)。這個(gè)頂部以誰基準(zhǔn)呢,以頁面中最外層的view為基準(zhǔn)。如上圖,(H5端)最外層dom元素的class名為page-content(后面計(jì)算高度會(huì)用到),(wx小程序端)最外層dom元素的class名為page-risk
- 根據(jù)后端返回?cái)?shù)據(jù)總條數(shù),渲染數(shù)據(jù),給每個(gè)item賦值不同的id。如上述 代碼實(shí)現(xiàn) 的第1點(diǎn)
- 根據(jù)后端返回?cái)?shù)據(jù)總條數(shù),動(dòng)態(tài)計(jì)算tab,渲染this.tabList。如上訴 代碼實(shí)現(xiàn) 的第2點(diǎn)
- 點(diǎn)擊tab,錨點(diǎn)到相應(yīng)位置。在點(diǎn)擊時(shí)拿到被點(diǎn)擊item的index,this.tabList[index]這樣就拿到了,當(dāng)前被點(diǎn)擊tab的數(shù)據(jù)(value,name,id)。計(jì)算dom高度:1).先獲取頂部基準(zhǔn)的top值,uni.createSelectorQuery().select(‘.page-risk’).boundingClientRect(data => { data.top });2).然后根據(jù)被點(diǎn)擊tab的id,獲取top值,然后根據(jù)差值,通過uni.pageScrollTo滾動(dòng)頁面到目標(biāo)位置。(注意這里需要注意 代碼實(shí)現(xiàn) 的第5點(diǎn),頂部是否有其他dom元素,它們高度也需要計(jì)算在內(nèi),這里稱為**偏移量**)
- 監(jiān)聽頁面滾動(dòng)周期onPageScroll事件,根據(jù)頁面滾動(dòng)激活相應(yīng)的tab。這里使用到了第1點(diǎn)提到的數(shù)組distanceArr
全部代碼
export default {
data() {
tabList: [], // 切題tab集合
topicList: [], // 題庫集合
distanceArr: [], // 每道題ID對應(yīng)的top值
isTabChange: false, // 防止在點(diǎn)擊tab的時(shí)候,頁面的滾動(dòng)導(dǎo)致重復(fù)計(jì)算、抖動(dòng)問題
},
// 監(jiān)聽頁面滾動(dòng)
onPageScroll (event) {
if (this.isTabChange) {
return
};
const { scrollTop } = event;
// 偏移量,由于吸頂?shù)膖ab、頭部的顯示信息也有高度,素以做了偏移量
// (這里是代碼實(shí)現(xiàn)第5點(diǎn)及實(shí)現(xiàn)思路第4點(diǎn)提到的偏移量)
const skewY = 103;
if (scrollTop >= skewY) {
this.$nextTick(() => {
// distanceArr-進(jìn)入頁面時(shí)第一時(shí)間獲取每個(gè)tab對應(yīng)item的top值集合
const length = this.distanceArr.length;
const index = this.distanceArr.findIndex(el => el - skewY - scrollTop > skewY);
// 當(dāng)index == -1 的時(shí)候,實(shí)際當(dāng)前滾動(dòng)的距離超出了最大值,也就是在最后一個(gè)tab顯示的內(nèi)容
// 當(dāng)index > 0 的時(shí)候,說明能在當(dāng)前的scrollTop值找到,即index的前一位
// currentTab 0激活第一個(gè)切題tab 1第二個(gè)
this.currentTab = index > 0 ? index - 1 : length - 1;
})
}
},
methods: {
// 獲取題庫
getList() {
this.topicList = []
},
// 根據(jù)this.topicList計(jì)算切題tab
getTopicTab() {
let cycleIndex = Math.ceil(this.topicList.length / 20); // 向上取整6.01 -> 7
let tabArr = [];
for(let i = 1; i <= cycleIndex; i++) {
tabArr.push({
value: 20 * i,
name: ((20 * (i - 1)) + 1) + '-' + (i == cycleIndex ? this.topicList.length : (20 * i)),
id: '#a' + ((20 * (i - 1)) + 1)
});
};
this.tabList = tabArr;
},
// 獲取每個(gè)tab對應(yīng)item的top值集合('20-41'獲取第21個(gè)item的top值)
getDistanceArr () {
const _this = this;
_this.$nextTick(() => {
const query = uni.createSelectorQuery().in(_this);
_this.tabList.map(el => {
query.select(el.id).boundingClientRect(data => {
// 獲取當(dāng)前ID距離頂部的top值
if(!_this.distanceArr.includes(data.top)) {
_this.distanceArr.push(data.top)
}
}).exec()
})
});
},
// 切題tab
changeTab(index) {
// 防止在點(diǎn)擊tab的時(shí)候,頁面的滾動(dòng)導(dǎo)致重復(fù)計(jì)算、抖動(dòng)問題
// true時(shí)監(jiān)聽頁面滾動(dòng)生命周期代碼則不執(zhí)行
this.isTabChange = true;
this.currentTab = index;
const _this = this;
// this.$nextTick 保證當(dāng)前isTabChange 為true后執(zhí)行代碼
// 避免在istabChange變?yōu)閠rue的時(shí)候,執(zhí)行代碼,監(jiān)聽事件還是會(huì)繼續(xù)執(zhí)行重新計(jì)算currenTab值
_this.$nextTick(() => {
const query = uni.createSelectorQuery().in(_this);
let id = _this.tabList[index].id;
let dataTop;
query.select(id).boundingClientRect(data => {
dataTop = data.top;
}).exec();
query.select('.page-risk').boundingClientRect(res => {
const scrollTop = dataTop - res.top // 獲取差值
const skewY = 103 // 偏移(頂部返回和切換題tab的高度及第一題magin/padding的和)
// 頁面開始進(jìn)行滾動(dòng)到目標(biāo)位置
uni.pageScrollTo({
scrollTop: scrollTop > 0 ? scrollTop - skewY : scrollTop + skewY,
duration: 300,
complete: function () {
const timer = setTimeout(() => {
_this.isTabChange = false // 關(guān)閉(放開對onPageScroll的限制)
clearTimeout(timer)
}, 500) // 解決ios和安卓、鴻蒙系統(tǒng)兼容性問題
}
});
}).exec()
})
},
// 這里業(yè)務(wù)是題庫,所以當(dāng)必填項(xiàng)沒有填寫時(shí),就會(huì)錨點(diǎn)到該位置
// 前提是需要將必填項(xiàng)且沒有填寫的item的id值傳遞過來
getPageScrollTo(id) {
const _this = this;
_this.$nextTick(() => {
const query = uni.createSelectorQuery().in(_this);
let index = '#a' + (id + 1);
let dataTop;
query.select(index).boundingClientRect(data => {
dataTop = data.top;
}).exec();
query.select('.page-risk').boundingClientRect(res => {
const scrollTop = dataTop - res.top; // 獲取差值
const skewY = 103; // 偏移
// 頁面開始進(jìn)行滾動(dòng)到目標(biāo)位置
uni.pageScrollTo({
scrollTop: scrollTop > 0 ? scrollTop - skewY : scrollTop + skewY,
duration: 300,
});
}).exec()
})
},
}
}
到了這里,關(guān)于uniapp tab切換及tab錨點(diǎn)效果(兼容wx小程序及H5端)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!