UniApp中實現(xiàn)類似錨點(diǎn)定位滾動效果
一個uniapp小程序的項目,我們需要實現(xiàn)一個非常實用的功能——類似于錨點(diǎn)定位的交互效果,即在首頁中有多個tab(分類標(biāo)簽),每個tab對應(yīng)著不同的模塊。當(dāng)用戶點(diǎn)擊某個分類的tab時,需要流暢地滾動到對應(yīng)的內(nèi)容位置,提供更好的用戶體驗。
思路
為了實現(xiàn)這個功能,我們可以分為以下幾個步驟:
實時監(jiān)聽滾動并選中對應(yīng)
tab點(diǎn)擊
tab跳轉(zhuǎn)至當(dāng)前模塊
功能實現(xiàn)
1.點(diǎn)擊 tab 跳轉(zhuǎn)至當(dāng)前模塊
當(dāng)用戶點(diǎn)擊某個 tab 時,我們根據(jù) tab 的 currentTab值 ,然后獲取到要滾動到的距離
由于 boundingClientRect 獲取的 top 值是相對于視口的,所以實際上頁面需要滾動的距離為相對于視口的距離加上當(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, // 滾動到頁面的目標(biāo)位置 這個是滾動到頂部, 0
duration:300 // 滾動動畫的時長
})
},
// 點(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.實時監(jiān)聽滾動并選中對應(yīng)tab
在滾動時實時通過
boundingClientRect獲取模塊相對于窗口的值,通過uiapp提供的頁面級別的方法onPageScroll 去計算滾動的高度
js代碼
// 監(jiān)聽頁面滾動
onPageScroll (event) {
if(event){
// 返回頂部按鈕
const { scrollTop } = event;
scrollTop > 400 ? this.isShow = true : this.isShow = false
//記錄當(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()
}
},通過滾動時實時獲取,避免了獲取 top 值不準(zhǔn)確的問題,之后判斷距離當(dāng)前窗口的距離,小于臨界值則選中對應(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>上報</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)計分析</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>
<!-- 代辦事項 -->
<view class="dataBoardBox data2">
<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>
<!-- 代辦事項 -->
<view class="dataBoardBox data3">
<view class="title">代辦事項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,
//記錄滾動的距離,用在切換tab頁面滾動邏輯中
scrollTop: 0,
currentTab:0,
//當(dāng)前的頁面滾動是否由切換tab觸發(fā)
isScrollByTab: false,
keyword: '', // 搜索值
quickList: [{
name: '數(shù)據(jù)看板',
}, {
name: '事件工作',
}, {
name: '代辦事項',
}, {
name: '代辦事項2',
}]
}
},
// 監(jiān)聽頁面滾動
onPageScroll (event) {
if(event){
// 返回頂部按鈕
const { scrollTop } = event;
scrollTop > 400 ? this.isShow = true : this.isShow = false
//記錄當(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, // 滾動到頁面的目標(biāo)位置 這個是滾動到頂部, 0
duration:300 // 滾動動畫的時長
})
},
// 點(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àn)點(diǎn)擊tab跳轉(zhuǎn)到對應(yīng)模塊并滾動的功能。這個功能對于許多開發(fā)者來說非常有用,因為它可以提高用戶體驗,并使頁面導(dǎo)航更加流暢。
到此這篇關(guān)于UniApp中實現(xiàn)類似錨點(diǎn)定位滾動效果的文章就介紹到這了,更多相關(guān)UniApp中錨點(diǎn)定位滾動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問題及解決代碼
這篇文章主要介紹了vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問題及解決代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-09-09
詳解如何使用vue和electron開發(fā)一個桌面應(yīng)用
這篇文章主要為大家介紹了詳解如何使用vue和electron開發(fā)一個桌面應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Vuex modules模式下mapState/mapMutations的操作實例
這篇文章主要介紹了Vuex modules 模式下 mapState/mapMutations 的操作實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
vue中阻止click事件冒泡,防止觸發(fā)另一個事件的方法
下面小編就為大家分享一篇vue中阻止click事件冒泡,防止觸發(fā)另一個事件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02

