UniApp中实现类似锚点定位滚动效果
一个uniapp小程序的项目,我们需要实现一个非常实用的功能——类似于锚点定位的交互效果,即在首页中有多个tab(分类标签),每个tab对应着不同的模块。当用户点击某个分类的tab时,需要流畅地滚动到对应的内容位置,提供更好的用户体验。
思路
为了实现这个功能,我们可以分为以下几个步骤:
实时监听滚动并选中对应
tab
点击
tab
跳转至当前模块
功能实现
1.点击 tab 跳转至当前模块
当用户点击某个 tab 时,我们根据 tab 的 currentTab值 ,然后获取到要滚动到的距离
由于 boundingClientRect 获取的 top 值是相对于视口的,所以实际上页面需要滚动的距离为相对于视口的距离加上当前页面的滚动距离,即 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> <!-- 底部弹窗-全部应用 --> <u-popup :show="show" :round="10" mode="bottom" closeable @close="close"> <view> <view class="popTit">全部应用</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, // 滚动到页面的目标位置 这个是滚动到顶部, 0 duration:300 // 滚动动画的时长 }) }, // 点击底部弹窗喵点定位 popClick(index){ this.meow(index) }, whoBtn(part){ this.meow(part.index) }, // 喵点定位 meow(index){ uni.createSelectorQuery().select(".data"+index).boundingClientRect((res)=>{//定位到你要的class的位置 uni.pageScrollTo({ scrollTop:res.top, duration: 300 }); }).exec() } }
2.实时监听滚动并选中对应tab
在滚动时实时通过
boundingClientRect
获取模块相对于窗口的值,通过uiapp提供的页面级别的方法onPageScroll 去计算滚动的高度
js代码
// 监听页面滚动 onPageScroll (event) { if(event){ // 返回顶部按钮 const { scrollTop } = event; scrollTop > 400 ? this.isShow = true : this.isShow = false //记录当前页面的滚动距离 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
值不准确的问题,之后判断距离当前窗口的距离,小于临界值则选中对应的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">已添加的应用(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(`删除被点击${index+1}`)"></image> </view> </view> </view> </view> <!-- 全部应用 --> <view class="whole"> <view class="title">全部应用</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> <!-- 数据看板 --> <view class="dataBoardBox data0"> <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 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> <!-- 底部弹窗-全部应用 --> <u-popup :show="show" :round="10" mode="bottom" closeable @close="close"> <view> <view class="popTit">全部应用</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, //当前的页面滚动是否由切换tab触发 isScrollByTab: false, keyword: '', // 搜索值 quickList: [{ name: '数据看板', }, { name: '事件工作', }, { name: '代办事项', }, { name: '代办事项2', }] } }, // 监听页面滚动 onPageScroll (event) { if(event){ // 返回顶部按钮 const { scrollTop } = event; scrollTop > 400 ? this.isShow = true : this.isShow = false //记录当前页面的滚动距离 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, // 滚动到页面的目标位置 这个是滚动到顶部, 0 duration:300 // 滚动动画的时长 }) }, // 点击底部弹窗喵点定位 popClick(index){ this.meow(index) }, whoBtn(part){ this.meow(part.index) }, // 喵点定位 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; } } } } } // 全部应用 .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; } } } } } // 底部弹窗-全部应用 .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; }
总结
在这篇文章中,我们介绍了如何在uniapp小程序项目中实现点击tab跳转到对应模块并滚动的功能。这个功能对于许多开发者来说非常有用,因为它可以提高用户体验,并使页面导航更加流畅。
到此这篇关于UniApp中实现类似锚点定位滚动效果的文章就介绍到这了,更多相关UniApp中锚点定位滚动内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Vue3+ts+setup getCurrentInstance使用时遇到的问题以及解决办法
getCurrentInstance方法用于获取当前组件实例,仅在setup和生命周期中起作用,下面这篇文章主要给大家介绍了关于Vue3+ts+setup getCurrentInstance使用时遇到的问题以及解决办法,需要的朋友可以参考下2022-08-08iview table render集成switch开关的实例
下面小编就为大家分享一篇iview table render集成switch开关的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2018-03-03
最新评论