UniApp中實(shí)現(xiàn)類似錨點(diǎn)定位滾動(dòng)效果
一個(gè)uniapp小程序的項(xiàng)目,我們需要實(shí)現(xiàn)一個(gè)非常實(shí)用的功能——類似于錨點(diǎn)定位的交互效果,即在首頁(yè)中有多個(gè)tab(分類標(biāo)簽),每個(gè)tab對(duì)應(yīng)著不同的模塊。當(dāng)用戶點(diǎn)擊某個(gè)分類的tab時(shí),需要流暢地滾動(dòng)到對(duì)應(yīng)的內(nèi)容位置,提供更好的用戶體驗(yàn)。
思路
為了實(shí)現(xiàn)這個(gè)功能,我們可以分為以下幾個(gè)步驟:
實(shí)時(shí)監(jiān)聽(tīng)滾動(dòng)并選中對(duì)應(yīng)
tab
點(diǎn)擊
tab
跳轉(zhuǎn)至當(dāng)前模塊
功能實(shí)現(xiàn)
1.點(diǎn)擊 tab 跳轉(zhuǎn)至當(dāng)前模塊
當(dāng)用戶點(diǎn)擊某個(gè) tab 時(shí),我們根據(jù) tab 的 currentTab值 ,然后獲取到要滾動(dòng)到的距離
由于 boundingClientRect 獲取的 top 值是相對(duì)于視口的,所以實(shí)際上頁(yè)面需要滾動(dòng)的距離為相對(duì)于視口的距離加上當(dāng)前頁(yè)面的滾動(dòng)距離,即 res.top + scrollTop
html代碼
<u-tabs :list="quickList" :current='currentTab' @click="whoBtn"> <view slot="right" style="padding-left: 4px;" @tap="show = true" > <u-icon name="list" size="21" bold ></u-icon> </view> </u-tabs> <!-- 底部彈窗-全部應(yīng)用 --> <u-popup :show="show" :round="10" mode="bottom" closeable @close="close"> <view> <view class="popTit">全部應(yīng)用</view> <view class="popCon"> <view class="item" v-for="(item,index) in quickList" :key="index" @click="popClick(index)">{{ item.name }}</view> </view> </view> </u-popup>
js代碼
methods: { close() { this.show = false }, topBack(){ uni.pageScrollTo({ scrollTop:0, // 滾動(dòng)到頁(yè)面的目標(biāo)位置 這個(gè)是滾動(dòng)到頂部, 0 duration:300 // 滾動(dòng)動(dòng)畫(huà)的時(shí)長(zhǎng) }) }, // 點(diǎn)擊底部彈窗喵點(diǎn)定位 popClick(index){ this.meow(index) }, whoBtn(part){ this.meow(part.index) }, // 喵點(diǎn)定位 meow(index){ uni.createSelectorQuery().select(".data"+index).boundingClientRect((res)=>{//定位到你要的class的位置 uni.pageScrollTo({ scrollTop:res.top, duration: 300 }); }).exec() } }
2.實(shí)時(shí)監(jiān)聽(tīng)滾動(dòng)并選中對(duì)應(yīng)tab
在滾動(dòng)時(shí)實(shí)時(shí)通過(guò)
boundingClientRect
獲取模塊相對(duì)于窗口的值,通過(guò)uiapp提供的頁(yè)面級(jí)別的方法onPageScroll 去計(jì)算滾動(dòng)的高度
js代碼
// 監(jiān)聽(tīng)頁(yè)面滾動(dòng) onPageScroll (event) { if(event){ // 返回頂部按鈕 const { scrollTop } = event; scrollTop > 400 ? this.isShow = true : this.isShow = false //記錄當(dāng)前頁(yè)面的滾動(dòng)距離 this.scrollTop = scrollTop; if (this.isScrollByTab) return; const query = uni.createSelectorQuery().in(this); query.selectAll(".dataBoardBox").boundingClientRect((res)=>{//定位到你要的class的位置 this.$nextTick(() => { const index = findLastIndex(res, (rect) => rect.top < 80); if (index > -1) { this.currentTab = index; } }); }).exec() } },
通過(guò)滾動(dòng)時(shí)實(shí)時(shí)獲取,避免了獲取 top
值不準(zhǔn)確的問(wèn)題,之后判斷距離當(dāng)前窗口的距離,小于臨界值則選中對(duì)應(yīng)的tab
全部代碼
index.vue
<template> <view> <!-- 搜索欄 --> <view class="searchView"> <u-search shape="square" :showAction="false" placeholder="搜索" v-model="keyword"></u-search> <view class="addedBox"> <view class="addedBox-top"> <view class="text">已添加的應(yīng)用(4)</view> </view> <view class="addedBox-bom"> <view class="item" v-for="(item,index) in 2" :key="index"> <image src="/static/group-icon.png" mode="widthFix"></image> <text>上報(bào)</text> <image class="delete" src="/static/quick/delete.png" mode="widthFix" @tap="$u.toast(`刪除被點(diǎn)擊${index+1}`)"></image> </view> </view> </view> </view> <!-- 全部應(yīng)用 --> <view class="whole"> <view class="title">全部應(yīng)用</view> <u-sticky bgColor="#fff"> <u-tabs :list="quickList" :current='currentTab' @click="whoBtn"> <view slot="right" style="padding-left: 4px;" @tap="show = true" > <u-icon name="list" size="21" bold ></u-icon> </view> </u-tabs> </u-sticky> <!-- 數(shù)據(jù)看板 --> <view class="dataBoardBox data0"> <view class="title">數(shù)據(jù)看板</view> <view class="dataBoard-item" v-for="(item,index) in 5" :key="index"> <image src="/static/group-icon.png" mode="widthFix"></image> <view class="right"> <view class="right-1">統(tǒng)計(jì)分析</view> <view class="right-2">添加</view> </view> </view> </view> <!-- 事件工作 --> <view class="dataBoardBox data1"> <view class="title">事件工作</view> <view class="dataBoard-item" v-for="(item,index) in 5" :key="index"> <image src="/static/group-icon.png" mode="widthFix"></image> <view class="right"> <view class="right-1">工作日志</view> <view class="right-2">已添加</view> </view> </view> </view> <!-- 代辦事項(xiàng) --> <view class="dataBoardBox data2"> <view class="title">代辦事項(xiàng)</view> <view class="dataBoard-item" v-for="(item,index) in 5" :key="index"> <image src="/static/group-icon.png" mode="widthFix"></image> <view class="right"> <view class="right-1">我的代辦</view> <view class="right-2">已添加</view> </view> </view> </view> <!-- 代辦事項(xiàng) --> <view class="dataBoardBox data3"> <view class="title">代辦事項(xiàng)2</view> <view class="dataBoard-item" v-for="(item,index) in 5" :key="index"> <image src="/static/group-icon.png" mode="widthFix"></image> <view class="right"> <view class="right-1">我的代辦</view> <view class="right-2">已添加</view> </view> </view> </view> </view> <!-- 底部彈窗-全部應(yīng)用 --> <u-popup :show="show" :round="10" mode="bottom" closeable @close="close"> <view> <view class="popTit">全部應(yīng)用</view> <view class="popCon"> <view class="item" v-for="(item,index) in quickList" :key="index" @click="popClick(index)">{{ item.name }}</view> </view> </view> </u-popup> <!-- 回到頂部 --> <image v-show="isShow" class="toTop" @click="topBack" src="/static//quick/toTop.png" alt=""></image> </view> </template> <script> import { findLastIndex } from 'lodash-es'; export default { data() { return { isShow:false, show:false, zanIndex:null, //記錄滾動(dòng)的距離,用在切換tab頁(yè)面滾動(dòng)邏輯中 scrollTop: 0, currentTab:0, //當(dāng)前的頁(yè)面滾動(dòng)是否由切換tab觸發(fā) isScrollByTab: false, keyword: '', // 搜索值 quickList: [{ name: '數(shù)據(jù)看板', }, { name: '事件工作', }, { name: '代辦事項(xiàng)', }, { name: '代辦事項(xiàng)2', }] } }, // 監(jiān)聽(tīng)頁(yè)面滾動(dòng) onPageScroll (event) { if(event){ // 返回頂部按鈕 const { scrollTop } = event; scrollTop > 400 ? this.isShow = true : this.isShow = false //記錄當(dāng)前頁(yè)面的滾動(dòng)距離 this.scrollTop = scrollTop; if (this.isScrollByTab) return; const query = uni.createSelectorQuery().in(this); query.selectAll(".dataBoardBox").boundingClientRect((res)=>{//定位到你要的class的位置 this.$nextTick(() => { const index = findLastIndex(res, (rect) => rect.top < 80); if (index > -1) { this.currentTab = index; } }); }).exec() } }, methods: { close() { this.show = false }, topBack(){ uni.pageScrollTo({ scrollTop:0, // 滾動(dòng)到頁(yè)面的目標(biāo)位置 這個(gè)是滾動(dòng)到頂部, 0 duration:300 // 滾動(dòng)動(dòng)畫(huà)的時(shí)長(zhǎng) }) }, // 點(diǎn)擊底部彈窗喵點(diǎn)定位 popClick(index){ this.meow(index) }, whoBtn(part){ this.meow(part.index) }, // 喵點(diǎn)定位 meow(index){ uni.createSelectorQuery().select(".data"+index).boundingClientRect((res)=>{//定位到你要的class的位置 uni.pageScrollTo({ scrollTop:res.top, duration: 300 }); }).exec() } } } </script> <style lang="scss"> @import 'quickPage.scss' </style>
index.scss
.searchView{ width: 750rpx; background-color: #ffffff; padding: 30rpx 25rpx 0 25rpx; box-sizing: border-box; .addedBox{ width: 100%; margin-top: 40rpx; .addedBox-top{ width: 100%; height: 50rpx; .text{ font-weight: bold; font-size: 32rpx; } } .addedBox-bom{ margin-top: 40rpx; width: 100%; display: flex; flex-wrap: wrap; .item{ display: flex; flex-direction: column; align-items: center; position: relative; width: 140rpx; margin-bottom: 30rpx; image{ width: 80rpx; height: 80rpx; border-radius: 20rpx; } text{ width: 100%; margin-top: 10rpx; text-align: center; font-size: 28rpx; color: #909090; } .delete{ position: absolute; top: -20rpx; right: 8rpx; width: 40rpx; height: 40rpx; z-index: 10; background-color: #ffffff; } } } } } // 全部應(yīng)用 .whole{ margin-top: 25rpx; width: 750rpx; background-color: #F5F5F5; .title{ padding: 30rpx 25rpx 0 25rpx; width: 100%; background-color: #ffffff; box-sizing: border-box; font-weight: bold; font-size: 32rpx; } .dataBoardBox{ background-color: #ffffff; padding-bottom: 30rpx; .title{ padding: 30rpx 25rpx 0 25rpx; width: 100%; background-color: #ffffff; box-sizing: border-box; font-weight: bold; font-size: 32rpx; } .dataBoard-item{ padding: 30rpx 25rpx 0 25rpx; box-sizing: border-box; display: flex; image{ width: 80rpx; height: 80rpx; border-radius: 20rpx; } .right{ margin-left: 30rpx; flex: 1; border-bottom: 3rpx solid rgba(200, 200, 200, .4); display: flex; align-items: center; justify-content: space-between; padding-right: 30rpx; box-sizing: border-box; .right-1{ font-size: 28rpx; } .right-2{ font-size: 24rpx; border: 2rpx solid rgba(200, 200, 200, .4); width: 100rpx; height: 40rpx; line-height: 40rpx; text-align: center; border-radius: 10rpx; } } } } } // 底部彈窗-全部應(yīng)用 .popTit{ width: 100%; height: 80rpx; border-bottom: 2rpx solid rgba(200, 200, 200, .4); line-height: 80rpx; text-align: center; font-weight: bold; } .popCon{ width: 100%; padding: 20rpx 30rpx 50rpx 30rpx; box-sizing: border-box; display: flex; flex-wrap: wrap; justify-content: space-between; .item{ width: 215rpx; height: 60rpx; background-color: #f6f6f6; margin-bottom: 40rpx; text-align: center; line-height: 60rpx; border-radius: 10rpx; font-size: 26rpx; border: 2rpx solid rgba(200, 200, 200, .5); } } // 回到頂部 .toTop{ position: fixed; z-index: 2; right: 40rpx; bottom: 10vw; width: 70rpx; height:70rpx; background-color: #ffffff; }
總結(jié)
在這篇文章中,我們介紹了如何在uniapp小程序項(xiàng)目中實(shí)現(xiàn)點(diǎn)擊tab跳轉(zhuǎn)到對(duì)應(yīng)模塊并滾動(dòng)的功能。這個(gè)功能對(duì)于許多開(kāi)發(fā)者來(lái)說(shuō)非常有用,因?yàn)樗梢蕴岣哂脩趔w驗(yàn),并使頁(yè)面導(dǎo)航更加流暢。
到此這篇關(guān)于UniApp中實(shí)現(xiàn)類似錨點(diǎn)定位滾動(dòng)效果的文章就介紹到這了,更多相關(guān)UniApp中錨點(diǎn)定位滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Vue Element中Select下拉框選取值的問(wèn)題
下面小編就為大家分享一篇淺談Vue Element中Select下拉框選取值的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問(wèn)題及解決代碼
這篇文章主要介紹了vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問(wèn)題及解決代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09vue中el-autocomplete支持分頁(yè)上拉加載功能
最近在項(xiàng)目中使用了ElementUI的el-autocomplete,下面這篇文章主要介紹了vue中el-autocomplete支持分頁(yè)上拉加載功能的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11詳解如何使用vue和electron開(kāi)發(fā)一個(gè)桌面應(yīng)用
這篇文章主要為大家介紹了詳解如何使用vue和electron開(kāi)發(fā)一個(gè)桌面應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03Vuex modules模式下mapState/mapMutations的操作實(shí)例
這篇文章主要介紹了Vuex modules 模式下 mapState/mapMutations 的操作實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10vue儲(chǔ)存storage時(shí)含有布爾值的解決方案
這篇文章主要介紹了vue儲(chǔ)存storage時(shí)含有布爾值的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06vue手寫(xiě)加載動(dòng)畫(huà)項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了vue手寫(xiě)加載動(dòng)畫(huà)項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10vue中阻止click事件冒泡,防止觸發(fā)另一個(gè)事件的方法
下面小編就為大家分享一篇vue中阻止click事件冒泡,防止觸發(fā)另一個(gè)事件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02