UniApp中实现类似锚点定位滚动效果

 更新时间:2023年10月21日 15:56:49   作者:江城开朗的豌豆  
一个uniapp小程序的项目,我们需要实现一个非常实用的功能——类似于锚点定位的交互效果,即在首页中有多个tab(分类标签),每个tab对应着不同的模块,当用户点击某个分类的tab时,需要流畅地滚动到对应的内容位置,提供更好的用户体验,

一个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中锚点定位滚动内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue中的传值及赋值问题

    vue中的传值及赋值问题

    这篇文章主要介绍了vue中的传值及赋值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue中的路由传值与重调本路由改变参数

    vue中的路由传值与重调本路由改变参数

    这篇文章主要介绍了vue中的路由传值与重调本路由改变参数方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • vue项目启动后没有局域网地址问题

    vue项目启动后没有局域网地址问题

    这篇文章主要介绍了vue项目启动后没有局域网地址问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 使用vue v-for循环图片路径方式

    使用vue v-for循环图片路径方式

    这篇文章主要介绍了使用vue v-for循环图片路径方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue实现进入全屏和退出全屏的示例代码

    vue实现进入全屏和退出全屏的示例代码

    最近一个项目需要进行大屏展示,所以登录完就要处于一个全屏的状态,本文主要介绍了vue实现进入全屏和退出全屏的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • 在Vue 中获取下拉框的文本及选项值操作

    在Vue 中获取下拉框的文本及选项值操作

    这篇文章主要介绍了在Vue 中获取下拉框的文本及选项值操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Vue3+ts+setup getCurrentInstance使用时遇到的问题以及解决办法

    Vue3+ts+setup getCurrentInstance使用时遇到的问题以及解决办法

    getCurrentInstance方法用于获取当前组件实例,仅在setup和生命周期中起作用,下面这篇文章主要给大家介绍了关于Vue3+ts+setup getCurrentInstance使用时遇到的问题以及解决办法,需要的朋友可以参考下
    2022-08-08
  • 解决vue中无法动态修改jqgrid组件 url地址的问题

    解决vue中无法动态修改jqgrid组件 url地址的问题

    下面小编就为大家分享一篇解决vue中无法动态修改jqgrid组件 url地址的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • iview table render集成switch开关的实例

    iview table render集成switch开关的实例

    下面小编就为大家分享一篇iview table render集成switch开关的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • vue3常用的指令之v-bind和v-on指令用法

    vue3常用的指令之v-bind和v-on指令用法

    vue的v-on与v-bind,v-on就是用于绑定事件的,下面这篇文章主要给大家介绍了关于vue3常用的指令之v-bind和v-on指令用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12

最新评论