「神奇的锚点定位:探索UniApp中实现滚动定位效果,与1024程序员节同欢,解析技术之美」

news2024/11/15 15:52:19

 

  

🎬 江城开朗的豌豆:个人主页

 🔥 个人专栏 :《 VUE 》 《 javaScript 》

 📝 个人网站 :《 江城开朗的豌豆🫛 》 

⛺️ 生活的理想,就是为了理想的生活 !

在这里插入图片描述

目录

 ⭐  文章简介

 📘  文章背景

 📘  功能思路

 📘  功能实现

1.点击 tab 跳转至当前模块

2.实时监听滚动并选中对应 tab

📘 全部代码

🔥 文章总结

⭐  写在最后


 ⭐  文章简介

        在这个即将到来的1024程序员节,我们将迎来一场技术盛宴。作为程序员的你,相信对于技术的热爱早已融入了生命的每一个角落。而正是因为这份热爱,我们才能不断追求创新、探索未知、解决问题

        在这一年里,科技行业发展迅猛,无数令人惊叹的新技术不断涌现。人工智能、区块链、云计算、物联网……这些概念成为了我们日常工作中的关键词。面对如此繁杂的技术世界,我们时刻保持着学习的姿态,不断拓展自己的技术边界。

        而正是因为这种渴望掌握新知识的态度,我们才能够站在技术的前沿,体验最新最潮的技术趋势。在1024程序员节中,我们不仅有机会与各路技术大咖进行深入交流,还能够倾听他们分享的宝贵经验和最新研究成果。

        这里汇聚着来自各行业、各领域的技术爱好者,无论你是前端工程师、后端开发者、移动应用程序员还是数据科学家,都能够找到属于自己的技术乐园。我们相信,在这个社区中你将不断获得激励和成长,与众多志同道合的技术达人一同探索更广阔的技术世界。

        

        同时,我们也会关注最新的前端趋势和发展动态。随着Web技术的不断演进,前端开发也在不断推陈出新。我们会及时介绍最新的前端框架、工具和技术,使你能够站在前沿,与时俱进。通过掌握最新的前端技术,你将能够在竞争激烈的Web开发领域中有更大的竞争力。

 📘  文章背景

        最近,我参与了一个uniapp小程序的项目,我们需要实现一个非常实用的功能——类似于锚点定位的交互效果,即在首页中有多个tab(分类标签),每个tab对应着不同的模块。当用户点击某个分类的tab时,需要流畅地滚动到对应的内容位置,提供更好的用户体验。

        今天下午,我投入全身心地完成了锚点定位功能的开发任务。经过几个小时的不懈努力和测试,最终成功实现了这个功能!在这个过程中,我一步步探索、解决问题,感受着一个问题一个问题地被解决的成就感。

  📘  功能思路

 效果图展示

为了实现这个功能,我们可以分为以下几个步骤:

  1. 实时监听滚动并选中对应 tab
  2. 点击 tab 跳转至当前模块

   📘  功能实现

1.点击 tab 跳转至当前模块

当用户点击某个 tab 时,我们根据 tabcurrentTab值 ,然后获取到要滚动到的距离

由于 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跳转到对应模块并滚动的功能。这个功能对于许多开发者来说非常有用,因为它可以提高用户体验,并使页面导航更加流畅。

希望对大家有帮助!

谢谢各位大佬!

⭐  写在最后

        程序员们,看完以上文章,希望对你有所帮助,让我们一起迎接这个令人激动的1024程序员节吧!在这里,我们将一同畅想未来,释放创造力,共同引领技术的前沿。让我们相互学习、相互启发,创造出更加美好、更加智慧的世界!

        

祝愿

        大家在1024程序员节中收获满满,技术进步,事业腾飞!让我们一同点亮技术之光,为世界的变革贡献自己的力量!

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1111004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序中如何使用自定义组件应用及搭建个人中心布局

一&#xff0c;自定义组件 从小程序基础库版本 1.6.3 开始&#xff0c;小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。 开发者可以将页面内的功能模块抽象成自定义组件&#xff0c;以便在不同的页面中重复使用&#xff1b;也可以将复杂的…

【Godot引擎开发】简单基础,外加一个小游戏DEMO

博主&#xff1a;_LJaXi 专栏&#xff1a; Godot | 横版游戏开发 Godot 物体规律移动内置虚函数浮点计算浮点数计算数组APIInput单例与自定义单例节点NodeSprite2DArea2DCollisionShape2DKinematicBody2DRigidBody2D Pong游戏场景安排玩家1玩家2小球记分系统文件概要 下面是介绍…

软件工程与计算总结(二十一)软件维护与演化

一.软件维护 1.软件可修改性和软件维护 产品交付给用户并投入运营之后&#xff0c;接下来的工作被看做软件维护。 因为软件不存在“磨损”的情况&#xff0c;所以与其他工程学科相比&#xff0c;软件维护只需要完成少量的使用帮助、故障解决等工作——但并不意味着维护是简单…

IOday8

#include <head.h>//要发送数据的结构体类型 struct msgbuf {long mtype; /* 消息类型*/char mtext[1024]; /* 正文数据 */}; //宏定义正文大小 #define SIZE sizeof(struct msgbuf)-sizeof(long) int main(int argc, const char *argv[]) {key_t key;if((keyft…

oracle-AWR报告生成方法

AWR报告生成方法 1. 以oracle用户登陆服务器 2. 进入到要保存awr报告的目录 3. 以sysdba身份连接数据库 sqlplus / as sysdba4. 执行生成AWR报告命令 ?/rdbms/admin/awrrpt.sql5. 选择AWR报告的文件格式 6. 选择生成多少天的AWR报告 7. 选择报告的快照起始和结束ID 8. 输入生…

Java 对象是什么样子的?

Java 对象是什么样子的&#xff1f; class Student{ int age; String name; }Student s new Student(18, “zhangsan”); 这里的 s 变量&#xff0c;就是我们常说的引用&#xff0c;这里是强引用。指向对象中的 Java对象。 很多人可能认为&#xff0c;堆中存储了 age 18, na…

Linux学习之进程一

目录 一&#xff0c;什么是进程&#xff1f; 1.何为pcb&#xff08;process control block&#xff09;&#xff1f; 2.linux的task_struct 二&#xff0c;了解task_struct的核心字段 标识符 ps指令 getpid指令 ppid&#xff08;parent process id&#xff09; getppid…

DASFAA2023 | 关系数据库和图数据库关键技术融合趋势

编者按&#xff1a; 本期论文导读为大家介绍近年来关系数据库和图数据库关键技术的融合趋势及代表性工作。相关内容来自DASFAA 2023 Tutorial “Fusion of Relational and Graph Database Techniques: An Emerging Trend”&#xff0c;由北京交通大学刘钰博士、中北大学郭青松…

【持续更新】tutorial-Linux-Markdown-etc(Linux、命令、Markdown、md、Tex、LaTex)

1. Linux命令 1.1 常用 查看文件夹下文件数量: ls -l | wc -l7zip: 解压&#xff1a;7z x compressed_file.7z -o/path/to/destination # 注意-o和目标路径是连起来的&#xff0c;没有空格压缩&#xff1a;7z a compressed_file.zip destination_path conda 查看 conda 拥有的…

@JsonCreator(mode = JsonCreator.Mode.DELEGATING) @JsonValue解释

@JsonCreator(mode = JsonCreator.Mode.DELEGATING)public MessageId(Long id) {this.id = id;}<

【中级网络工程师】下午网络配置题

目录 dis ip routing-table 路由表如何查看 静态路由配置&#xff1a; rip和ospf配置 ACL配置 定义ACL过程 关键字traffic-filter inbound 和 traffic-filter outbpund 两种安装模式 DHCP服务 基于全局分配&#xff1a; 基于接口分配&#xff1a; 如何将划分的VLAN和创…

如何确认栈中申请的变量地址

一般一个程序被加载到内存后执行而成为一个进程。进程在内存中是分区域加载的&#xff0c;分别是代码段、数据段、bss段等等。 函数中定义的变量一般存在于栈中。现在我们通过实验验证一下&#xff0c;函数中定义的变量&#xff0c;到底存在与进程哪个位置。 1.测试程序 #in…

opencv入门到精通——图片,视频,摄像头的读取与保存

简介 OpenCV是一个流行的开源计算机视觉库&#xff0c;由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包&#xff0c;可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和计算机视觉应用。OpenCV主要使用C语言编写&#xff0c;同时也支持Python、Java、C等…

unity操作_碰撞器 c#

碰撞器Collider 在场景中选择一个物体Cube 观察检查器Inspector 自带Cube会默认挂载盒子碰撞器Box Colilider 增加组件可以增加更多中碰撞器 Edit Collider 编辑碰撞器形状 Is Trigger选项 Is Trigger &#xff1a;是否是触发器&#xff0c;如果启用此属性 则该碰撞体将用于触…

分享一下微信小程序里的预约链接怎么做

微信小程序是一种无需下载安装即可使用的应用程序&#xff0c;它依托于微信平台&#xff0c;为用户提供了更加便捷的使用体验。在小程序中&#xff0c;我们可以制作预约链接&#xff0c;以便用户直接在微信中进行预约&#xff0c;提高服务效率。下面我们将探讨如何制作微信小程…

RabbitMQ中的核心概念和交换机类型

目录 一、RabbitMQ相关概念二、Exchange类型三、RabbitMQ概念模型总结 一、RabbitMQ相关概念 Producer&#xff1a;生产者&#xff0c;就是投递消息的一方。生产者创建消息&#xff0c;然后发布到RabbitMQ中。消息一般可以包含两个部分&#xff1a;消息体和附加消息。 消息体…

利用Nginx可视化管理工具+Cpolar实现本地服务远程访问

文章目录 前言1. docker 一键安装2. 本地访问3. Linux 安装cpolar4. 配置公网访问地址5. 公网远程访问6. 固定公网地址 前言 Nginx Proxy Manager 是一个开源的反向代理工具&#xff0c;不需要了解太多 Nginx 或 Letsencrypt 的相关知识&#xff0c;即可快速将你的服务暴露到外…

nextjs-一个基于React的全栈框架

一、nextjs基本介绍 Next.js是一个基于React的轻量级框架&#xff0c;用于构建React应用程序。它在React的基础上提供了一些增强功能&#xff0c;包括服务器渲染&#xff08;SSR&#xff09;、静态生成&#xff08;SSG&#xff09;、路由等。Next.js的目标是简化React应用程序…

工业自动化控制通信协议Profinet系列-2、编译p-net在虚拟机树莓派上运行示例

工业自动化控制通信协议Profinet系列-2、编译p-net在虚拟机树莓派上运行示例 文章目录 工业自动化控制通信协议Profinet系列-2、编译p-net在虚拟机树莓派上运行示例一、前言二、树莓派虚拟机编译安装测试1、树莓派desktop下载及虚拟机安装2、下载编译安装p-net及运行demo程序 三…

【Javascript保姆级教程】if判断语句的三种形式

文章目录 前言一、if语句1.1 怎么使用1.2 示例代码1.3 示例代码2 二、if...else语句2.1 怎么使用2.2 示例代码2.3 示例代码 三、if...else if...语句3.1 怎么使用3.2 示例代码3.3 示例代码 总结 前言 嗨&#xff0c;欢迎来到Javascript保姆级教程&#xff01;在编写代码时&…