uniapp 实现tabbar分类导航及滚动联动效果

news2024/11/14 9:57:46

思路:使用两个scroll-view,tabbar分类导航使用scrollleft移动,内容联动使用页面滚动onPageScroll监听滚动高度

效果图
效果图

<template>
	<view class="content" >
		<view :class="[isSticky ? 'tab-sticky': '']">
			<view class="base-combox" >
				<uni-icons type="location-filled" color="#85D8CE" size="25" />
				<text class="tag-text">{{base[0].name}}</text>
			</view>
			<view class="u-tab" :class="[!isSticky ? 'tab-sticky': '']">
				<scroll-view scroll-x="true" scroll-with-animation="true" class="u-tab-view menu-scroll-view" :scroll-left="scrollLeft">
					<view v-for="(item,index) in tabbar" :key="index" class="u-tab-item" :class="[isActive == index ? 'u-tab-item-active' : '']"
					 @tap.stop="swichMenu(index)">
						<view class="u-line-1">{{item.name}}</view>
					</view>
				</scroll-view>
			</view>
			
		</view>
		
		<scroll-view :scroll-top="scrollTop" scroll-y scroll-with-animation class="right-box" @scroll="upScroll">
			<view class="page-view">
				<view class="class-item" :id="'item' + index" v-for="(item , index) in tabbar" :key="index">
					<view class="item-title">
						<text>{{item.name}}</text>
					</view>
					<view class="item-container">
						<view class="thumb-box" v-for="(item1, index1) in item.children" :key="index1" @tap="goList(item1)">
							<image v-if="item1.icon != ''" class="item-menu-image" :src="item1.icon" mode=""></image>
							<view v-else class="item-menu-image row-c" style="background-color: #F4F6F8;"><text style="font-size: 20rpx;color: #d0d0d0;">加载失败</text></view>
							<view class="item-menu-name">{{item1.name}}</view>
						</view>
					</view>
				</view>
			</view>
		</scroll-view>
	</view>
</template>

<script>
	export default {
		components: {
			
		},
		data() {
			return {
				moduleData: [],
				base:[{name:"请选择地点",id:'0'}],
				showType:'horizontally', //vertically,horizontally
				menuIcon:'../../static/list_h.png',//../../static/list_v.png
				tabbar: [],
				isActive : 0,
				scrollLeft: 0, // 横向滚动条位置
				scrollTop: 0,
				baseComboxH: 0,   // 地址区的高度
				tabbarScrollW: 0, // 导航区宽度
				tabbarScrollH: 0, // 导航区高度
				isSticky: true, // 是否吸顶
				timer: null // 定时器
			}
		},
		onLoad(option) {
			this.tabbar = [{id:1,name:"菜单一",children:[{id:11,name:"子菜单一"},{id:12,name:"子菜单二"}]},
			{id:2,name:"菜单二",children:[{id:11,name:"子菜单一子菜单一子菜单一子菜单一",icon:"/static/bq2.png"},{id:12,name:"子菜单二"},{id:13,name:"子菜单三"},{id:14,name:"子菜单四"},{id:15,name:"子菜单五"},{id:16,name:"子菜单六"}]},
			{id:3,name:"菜单三",children:[{id:21,name:"子菜单一"},{id:22,name:"子菜单二"}]},
			{id:3,name:"菜单四",children:[{id:31,name:"子菜单一"},{id:32,name:"子菜单二"}]},
			{id:5,name:"菜单五",children:[{id:41,name:"子菜单一"},{id:42,name:"子菜单二"}]},
			{id:6,name:"菜单六",children:[{id:51,name:"子菜单一"},{id:52,name:"子菜单二"}]}]
			
		},
		onReady() {
			
		},
		mounted(){
			this.getScrollW();
		},
		onPageScroll(e){
			//console.log(e,e.scrollTop)
			if(this.timer){
				clearTimeout(this.timer)
			}
			this.timer = setTimeout(() => { // 节流
				this.timer = null;
				// scrollHeight为右边菜单垂直中点位置
				// let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
				// scrollHeight为右边菜单头部位置
				let scrollHeight = e.scrollTop + this.tabbarScrollH + this.baseComboxH + 26;
				let len = this.tabbar.length
				for (let i = 0; i < len-1; i++) {
					let height1 = this.tabbar[i].top;
					let height2 = this.tabbar[i + 1].top;
					// console.log(height2,scrollHeight,height1)
					if (scrollHeight >= height1 && scrollHeight < height2) {
						this.tabbarStatus(i);
						return ;
					}
				}
				if(scrollHeight >= this.tabbar[len-1].top){
					this.tabbarStatus(len-1);
					return ;
				}
			}, 10)
		},
		methods: {
			goList(value) {
				var item = {title:value.title,labelName:value.labelName}
				uni.navigateTo({
					url:value.url+'?item='+encodeURIComponent(JSON.stringify(item))
				})
			},
			
			/**
			* 点击上方的tab切换
			* @index 传入的 ID
			*/
			async swichMenu(index) {
				if (index == this.isActive ) return;
				//this.scrollLeft = 0;
				this.$nextTick(function(){
					//for (let i = 0; i < index - 1; i++) {
						//this.scrollLeft += this.tabbar[i].width
					//};
					// this.isActive = index;
					// // 效果三(当前点击子元素居中展示)  不受子元素宽度影响
					// this.scrollLeft = this.tabbar[index].left - this.tabbarScrollW / 2 + this.tabbar[index].width / 2;
					this.tabbarStatus(index);
					//this.scrollTop = this.tabbar[index].top
					uni.pageScrollTo({
					    duration:200, // 毫秒
						scrollTop: (this.tabbar[index].top - this.tabbarScrollH - this.baseComboxH - 18) // 位置
					});
					// console.log(this.scrollLeft,this.scrollTop)
				})
			},
			// 获取标题区域宽度,和每个子元素节点的宽度以及元素距离左边栏的距离
			getScrollW() {
				const query = uni.createSelectorQuery().in(this);
				query.select('.u-tab-view').boundingClientRect(data => {
					  // 拿到 scroll-view 组件宽度高度
					  this.tabbarScrollW = data.width
					  this.tabbarScrollH = data.height
				 }).exec();
				 query.select('.base-combox').boundingClientRect(data => {
				 	  // 拿到 base-combox 高度
				 	  this.baseComboxH = data.height
				  }).exec();
				 
				query.selectAll('.u-tab-item').boundingClientRect(data => {
					 let dataLen = data.length;
					  for (let i = 0; i < dataLen; i++) {
						  //  scroll-view 子元素组件距离左边栏的距离
						  this.tabbar[i].left = data[i].left;
						 //  scroll-view 子元素组件宽度
						 this.tabbar[i].width = data[i].width
					}
				}).exec();
				query.selectAll('.class-item').boundingClientRect(data => {
					 let dataLen = data.length;
					  for (let i = 0; i < dataLen; i++) {
						  //  scroll-view 子元素组件距离上边栏的距离
						  this.tabbar[i].top = data[i].top;
					}
				}).exec()
			
			},
			upScroll(e){
				if(e.detail.scrollTop>50){
					this.isSticky = true
				}else{
					this.isSticky = false
				}
				console.log(e.detail.scrollTop)
			},
			/**
			* 设置上方菜单的滚动状态
			* @index 传入的 ID
			*/
			async tabbarStatus(index) {
				this.isActive = index;
				// 效果三(当前点击子元素居中展示)  不受子元素宽度影响
				this.scrollLeft = this.tabbar[index].left - this.tabbarScrollW / 2 + this.tabbar[index].width / 2;
			}
			
		}
	}
</script>

<style scoped>
	page{
		background-color: #fafafa !important;
		display: block;
		/* overflow: hidden; */
	}
	
	.content {
	    min-height: 100vh;
		display: flex;
		flex-direction: column;
	}
	
	.base-combox{
		align-items: center;
		justify-content: center;
		height: 50rpx;
	}
	
	.u-tab-item-active {
		position: relative;
		color: #000;
		font-size: 16px;
		font-weight: 600;
	}
	
	.u-tab-item-active::after {
		content: ''; // 必须
		display: block;
		width: 30px;
		height: 4px;
		background-color: #000;
		margin: 0 auto;
		border-radius: 10px;
	}
	
	.u-tab{
		border-bottom: 1rpx solid #f2f2f2;
		background-color: #ffffff;
		z-index: 99;
		width: 100%;
		align-items: center;
		height: 100rpx;
		margin-top: 10px;
	}
	
	.u-tab-view{
		box-sizing: border-box;
		padding-left: 30rpx;
		padding-right: 30rpx;
		width: 100%;
		white-space: nowrap;
	}
	.u-tab-item{
		display: inline-block;
		box-sizing: border-box;
		line-height: 60rpx;
		margin-right: 35rpx;
		font-size: 16px;
		padding-top: 10px;
	},
	.u-line-1{
		padding-bottom: 7px;
	}
	
	/* 隐藏scroll-view滚动条 */
	/deep/::-webkit-scrollbar{
		display: none;
	}
	uni-scroll-view .uni-scroll-view::-webkit-scrollbar {
		display: none
	}
	
	.right-box{
		/* height: 100vh; */
	}

	.page-view {
		padding: 16rpx;
		flex-direction: column;
	}
	
	.class-item {
		display: block;
		margin-bottom: 30rpx;
		background-color: #fff;
		padding: 16rpx;
		border-radius: 8rpx;
	}
	
	.class-item:last-child {
		min-height: calc(100vh - 102rpx - 50rpx - 20rpx - 88rpx - 32rpx - 64rpx );
		
	}
	
	.item-title {
		font-size: 26rpx;
		color: $u-main-color; 
		font-weight: bold;
	}
	
	.item-menu-name {
		margin-top: 8rpx;
		font-weight: normal;
		font-size: 24rpx;
		color: $u-main-color;
	}
	
	.item-container {
		display: flex;
		flex-wrap: wrap;
	}
	
	.thumb-box {
		width: 25%;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-direction: column;
		margin-top: 20rpx;
	}
	
	.item-menu-image {
		width: 60rpx;
		height: 60rpx;
	}
	
	.tab-sticky{
		display: flex;
		flex-direction: column;
		position: -webkit-sticky;
		position: sticky;
		top: var(--window-top);
		z-index: 99;
		background-color: #fff;
	}

</style>


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

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

相关文章

aws xray通过设置采样规则对请求进行过滤

参考资料 https://github.com/aws/aws-xray-sdk-pythonpython api reference&#xff0c;https://docs.aws.amazon.com/xray-sdk-for-python/latest/reference/node api reference&#xff0c;https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/ 初始化环境…

【征稿倒计时!华南理工大学主办 | IEEE出版 | EI检索稳定】2024智能机器人与自动控制国际学术会议 (IRAC 2024)

#华南理工大学主办&#xff01;#IEEE出版&#xff01;EI稳定检索&#xff01;#组委阵容强大&#xff01;IEEE Fellow、国家杰青等学术大咖领衔出席&#xff01;#会议设置“优秀论文”“优秀青年学者报告”“优秀海报”等评优奖项 2024智能机器人与自动控制国际学术会议 &#…

Unity3D学习FPS游戏(12)敌人检测和攻击玩家

前言&#xff1a;上一篇实现了敌人能动&#xff0c;有了点乐趣&#xff0c;但是敌人和玩家没什么对抗性。本篇将实现敌人追击玩家&#xff0c;并攻击玩家。 敌人攻击玩家 敌人检测玩家目标思路-碰撞检测的Trigger触发实现 敌人攻击目标思路-模仿玩家发射子弹的思路实现 效果 敌…

nginx代理后jsp如何获取http协议

1. nginx配置增加返回协议类型&#xff08;http或https&#xff09; location / {proxy_set_header X-Forwarded-Proto $scheme; } 2. 修改jsp配置 原jsp配置&#xff1a; <%String basePath request.getScheme()"://"request.getServerName()":"r…

#渗透测试#SRC漏洞挖掘#蓝队基础之网络七层杀伤链01

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话&#xff0c;就会报Promise.withResolvers 不是一个方法的错误&#xff0c;原因是Promise.withResolvers是ES15新特性&#xff0c;想了解可参考链接&#xff0c;这里的解决方案是将插件里的涉及到Promise.withResolvers的地…

HBase使用create创建表时报错ERROR: KeeperErrorCode = NoNode for /hbase/master

场景模拟 1. 正常情况 模拟ERROR: KeeperErrorCode NoNode for /hbase/master错误场景。 正常情况下创建hbase表如下图所示。 2. 删除hbase集群的zk节点 进入zookeeper客户端。 zkCli.sh删除hbase的zk节点。 deleteall /hbase退出zookeeper客户端。 quit3. 重启hbase集…

前端web

题目&#xff1a;制作带有下拉悬停菜单的导航栏 效果图 一、先制作标签 <body> <div id"menu"> <div id"container"> <div class"item">游戏1 <div class…

实测运行容器化Tomcat服务器

文章目录 前言一、拉取Tomcat 9.0镜像二、运行容器化Tomcat服务器三、访问Tomcat官网首页测试 总结 前言 运行容器化Tomcat服务器&#xff0c;首先确保正确安装docker&#xff0c;并且已启动运行&#xff0c;具体安装docker方法见笔者前面的博文《OpenEuler 下 Docker 安装、配…

PHP图书绘本借阅管理系统小程序源码

&#x1f4da; 图书绘本借阅管理系统&#xff1a;打造孩子的阅读乐园 &#x1f4da; &#x1f3f7;️ 引言&#xff1a;为什么我们需要图书绘本借阅管理系统&#xff1f; 在孩子的成长旅程中&#xff0c;阅读是不可或缺的一部分。然而&#xff0c;面对琳琅满目的图书和绘本&a…

炼码LintCode--数据库--基础语法--刷题笔记_01

目录 炼码LintCode数据库入门级别的笔记未完待续~~~ 炼码LintCode 数据库 入门级别的笔记 笔记如下&#xff0c;把所有涉及到的入门级别的知识点简单总结了一下。 以及一点点举一反三的写法。 增 INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);批量增 INSERT INT…

【含开题报告+文档+PPT+源码】基于SpringBoot和Vue框架的棋谱管理系统

开题报告 随着信息技术的飞速发展&#xff0c;尤其是互联网、大数据和人工智能等技术的进步&#xff0c;棋类运动在全球范围内得到了更广泛的推广和深入的研究。棋谱作为棋类运动的重要组成部分&#xff0c;是记录对弈过程、分析棋艺、传承棋道文化的关键载体。然而&#xff0…

three.js 杂记

clip&#xff1a; 1&#xff1a; 着色器 #ifdef USE_CLIP_DISTANCE vec4 worldPosition modelMatrix * vec4( position, 1.0 ); gl_ClipDistance[ 0 ] worldPosition.x - sin( time ) * ( 0.5 ); #endif gl_Position projectionMatrix * modelViewMatrix * vec4( positio…

并发基础:(淘宝笔试题)三个线程分别打印 A,B,C,要求这三个线程一起运行,打印 n 次,输出形如“ABCABCABC....”的字符串【举一反三】

🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀 🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷于探索一些框架源码和算法技巧奥秘,还乐于分享这些宝贵的知识和经验。 💡 无论你是刚刚踏…

C# 异步Task异常处理和堆栈追踪显示

Task的问题 在C#中异步Task是一个很方便的语法&#xff0c;经常用在处理异步&#xff0c;例如需要下载等待等方法中&#xff0c;不用函数跳转&#xff0c;代码阅读性大大提高&#xff0c;深受大家喜欢。 但是有时候发现我们的异步函数可能出现了报错&#xff0c;但是异常又没…

ssm102“魅力”繁峙宣传网站的设计与实现+vue(论文+源码)_kaic

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;“魅力”繁峙宣传网站系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了“魅力”繁峙宣传网站系统的发展&#x…

MySQL技巧之跨服务器数据查询:基础篇-A数据库与B数据库查询合并--封装到存储过程中

MySQL技巧之跨服务器数据查询&#xff1a;基础篇-A数据库与B数据库查询合并–封装到存储过程中 我们的最终目的是什么&#xff1f;当然的自动执行这些合并操作&#xff01; 上一篇 MySQL技巧之跨服务器数据查询&#xff1a;基础篇-A数据库与B数据库查询合并 我们已经知道怎么合…

短视频新纪元:AI数字人重塑视频运营格局

步入2024年&#xff0c;AI数字人技术如同一股不可忽视的力量&#xff0c;正深刻影响着视频运营的每一个角落。据行业权威机构艾媒咨询的统计数据显示&#xff0c;2023年&#xff0c;数字人相关企业注册数量激增948家&#xff0c;增长率高达68%&#xff0c;这一数据直观反映了数…

Ajax 获取进度和中断请求

HTML加入一些内容方便看效果和做交互&#xff1a; <div><p>当前传输进度&#xff1a;<span id"progress">0%</span></p><button id"send">发送</button><button id"btn">中断</button> …

ctfshow-web入门-SSTI(web361-web368)上

目录 1、web361 2、web362 3、web363 4、web364 5、web365 6、web366 7、web367 8、web368 1、web361 测试一下存在 SSTI 注入 方法很多 &#xff08;1&#xff09;使用子类可以直接调用的函数来打 payload1&#xff1a; ?name{{.__class__.__base__.__subclasses__…