Uni-App 双栏联动滚动组件开发详解 (电梯导航)

news2025/3/28 6:07:52

本文基于提供的代码实现一个左右联动的滚动组件,以下是详细的代码解析与实现原理说明:

<!--
  双栏联动滚动组件 - 技术解析
  功能特性:
  1. 左侧导航栏与右侧内容区双向联动
  2. 自适应容器高度
  3. 平滑滚动定位
  4. 动态内容位置计算
-->
<template>
	<view class="container">
		<!-- 外层容器 -->
		<view class="nav-container" id="navContainer">
			<!-- 左侧导航 ScrollView -->
			<scroll-view
				:scroll-y="true"
				:style="{ height: containerHeight + 'px' }"
				class="nav-sidebar"
				:scroll-into-view="leftScrollId"
				scroll-with-animation
			>
				<!-- 导航项循环渲染 -->
				<view
					v-for="(item, index) in leftData"
					:key="index"
					:id="'navItem-' + index"
					:class="['nav-item', { active: currentIndex === index }]"
					@tap="handleNavClick(index)"
				>
					{{ item }}
				</view>
			</scroll-view>

			<!-- 右侧内容 ScrollView -->
			<scroll-view
				:scroll-y="true"
				:style="{ height: containerHeight + 'px' }"
				class="content-main"
				:scroll-into-view="rightScrollId"
				@scroll="handleContentScroll"
				scroll-with-animation
			>
				<!-- 内容区块循环渲染 -->
				<view
					v-for="(section, sIndex) in rightData"
					:key="sIndex"
					:id="'content-' + sIndex"
					class="content-section"
				>
					<view class="section-title">{{ section.title }}</view>
					<view
						v-for="(para, pIndex) in section.content"
						:key="pIndex"
						class="content-para"
					>
						{{ para }}
					</view>
				</view>
				<view :style="{ height: fillHeight + 'px' }"></view>
			</scroll-view>
		</view>
	</view>
</template>

<script>
	export default {
		// 组件参数定义
		props: {
			leftData: {
				// 左侧导航数据
				type: Array,
				default: () => ['章节1', '章节2', '章节3', '章节4', '章节5', '章节6'],
			},
			rightData: {
				// 右侧内容数据
				type: Array,
				default: () => [
					{
						title: '章节1',
						content: ['内容1'],
					},
					{
						title: '章节2',
						content: ['内容1'],
					},
					{
						title: '章节3',
						content: ['内容1'],
					},
					{
						title: '章节4',
						content: ['内容1'],
					},
					{
						title: '章节5',
						content: ['内容1'],
					},
				],
			},
		},

		// 组件状态管理
		data() {
			return {
				containerTop: 0, //容器距离顶部距离
				containerHeight: 500, // 容器动态高度
				currentIndex: 0, // 当前激活索引
				sectionPositions: [], // 章节位置缓存数组
				positionsReady: false, // 位置计算完成标志
				fillHeight: 50, // 填充盒子的高度,内容滚动最后一项增加高度方便滚动
			}
		},

		// 计算属性
		computed: {
			// 左侧导航自动定位ID(保持选中项在可视区)
			leftScrollId() {
				return `navItem-${Math.max(this.currentIndex - 2, 0)}` // 提前2项滚动
			},

			// 右侧内容自动定位ID
			rightScrollId() {
				return `content-${this.currentIndex}`
			},
		},

		// 生命周期钩子
		mounted() {
			this.initContainer()
				.then(() => this.calcSectionPositions())
				.catch(console.error)
		},

		// 组件方法
		methods: {
			/**
			 * 初始化容器尺寸
			 * 使用 Promise 保证高度计算完成
			 */
			initContainer() {
				return new Promise((resolve) => {
					uni.createSelectorQuery()
						.in(this)
						.select('#navContainer')
						.boundingClientRect((res) => {
							this.containerTop = res.top //距离父元素顶部高度
							this.containerHeight = res.height
							resolve()
						})
						.exec()
				})
			},

			/**
			 * 计算内容区块位置
			 * 使用 uni API 获取元素位置信息
			 */
			calcSectionPositions() {
				uni.createSelectorQuery()
					.in(this)
					.selectAll('.content-section')
					.boundingClientRect((res) => {
						// 缓存各章节顶部位置
						this.sectionPositions = res.map((item) => item.top - this.containerTop)
						this.positionsReady = true

					
						let lastHeight = res[res.length - 1].height
						console.log(this.containerHeight, 8454545)
						//如果滚动显示的区域大于右侧单个元素的高度就要加入填充高度让元素滚动的时候 左侧的标签可以正常切换
						if (lastHeight- 20 < this.containerHeight ) {
							this.fillHeight = this.containerHeight - last + 20
						}
					})
					.exec()
			},

			/**
			 * 导航点击处理
			 * @param {number} index - 点击的导航索引
			 */
			handleNavClick(index) {
				this.currentIndex = index // 更新当前索引
			},

			/**
			 * 内容滚动处理
			 * @param {Object} e - 滚动事件对象
			 */
			handleContentScroll(e) {
				if (!this.positionsReady) return

				const scrollTop = e.detail.scrollTop
				const positions = this.sectionPositions

				// 二分查找算法优化(当前使用顺序查找)
				let current = this.currentIndex
				while (current < positions.length && positions[current] < scrollTop + 50) {
					current++
				}
				this.currentIndex = Math.max(current - 1, 0)
			},
		},
	}
</script>

<!-- 样式设计说明 -->
<style>
	/* 容器布局 */
	.container {
		height: 20vh; /* 全屏高度 */
		background: #ffffff;
	}

	.nav-container {
		display: flex; /* 弹性布局 */
		height: 100%;
	}

	/* 左侧导航样式 */
	.nav-sidebar {
		width: 200rpx; /* 固定宽度 */
		background: #f5f7fa; /* 浅色背景 */
		border-right: 1px solid #e4e7ed;
	}

	.nav-item {
		padding: 24rpx;
		transition: all 0.3s; /* 平滑过渡效果 */
	}

	.nav-item.active {
		color: #409eff; /* 主题色 */
		background: #ecf5ff; /* 激活背景 */
	}

	/* 右侧内容样式 */
	.content-main {
		flex: 1; /* 剩余空间填充 */
		padding: 32rpx;
	}

	.section-title {
		font-size: 36rpx; /* 标题字号 */
		font-weight: 600;
	}

	.content-para {
		background: #fafafa; /* 段落背景 */
		border-radius: 8rpx;
	}
</style>

技术实现要点

1. 双向滚动联动机制

  • 导航 → 内容:通过 scroll-into-view 绑定计算属性,点击时自动定位
  • 内容 → 导航:监听滚动事件,计算当前可见章节并更新激活状态

2. 性能优化设计

  • 位置信息缓存:预先计算章节位置,避免频繁查询DOM
  • 节流处理:滚动事件默认自带节流,保证性能
  • 异步计算:使用 Promise 链保证初始化顺序

3. 自适应布局

  • 动态高度计算:通过 uni API 获取容器实际高度
  • Flex 布局:实现左右栏自适应排列

4. 扩展性考虑

  • 组件化设计:通过 props 接收数据,方便复用
  • 样式可配置:通过 class 控制样式,易于主题定制

使用示例

<template>
  <dual-scroll 
    :left-data="categories" 
    :right-data="contents"
  />
</template>

<script>
// 示例数据结构
const categories = ['水果', '蔬菜', '肉类']
const contents = [
  {
    title: '水果',
    content: ['苹果', '香蕉', '橙子']
  },
  {
    title: '蔬菜',
    content: ['白菜', '萝卜', '番茄']
  }
]
</script>

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

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

相关文章

当下主流 AI 模型对比:ChatGPT、DeepSeek、Grok 及其他前沿技术

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 人工智能&#xff08;AI&#xff09;领域近年来取得了巨大的突破&#xff0c;特别是在大语言模型&#xff08;LLM&#…

【自用】NLP算法面经(5)

一、L1、L2正则化 正则化是机器学习中用于防止过拟合并提高模型泛化能力的技术。当模型过拟合时&#xff0c;它已经很好地学习了训练数据&#xff0c;甚至是训练数据中的噪声&#xff0c;所以可能无法在新的、未见过的数据上表现良好。 比如&#xff1a; 其中&#xff0c;x1和…

体育直播视频源格式解析:M3U8 vs FLV

在体育直播领域&#xff0c;视频源的格式选择直接影响着直播的流畅度、画质以及兼容性。目前&#xff0c;M3U8 和 FLV 是两种最为常见的视频流格式&#xff0c;它们各有优劣&#xff0c;适用于不同的场景。本文将从技术原理、优缺点以及应用场景等方面对 M3U8 和 FLV 进行详细解…

Ubuntu20.04安装并配置Pycharm2020.2.5

一. 下载pycharm 社区版 1. 下载地址&#xff1a; PyCharm: the Python IDE for data science and web developmentThe Python IDE for data science and web development with intelligent code completion, on-the-fly error checking, quick-fixes, and much more.https:/…

Filter Solutions学习-02 【高级设计】界面介绍

这是高级界面的各种控件的功能。 其中说一下filter type。这不是根据自己想当然决定的&#xff0c;而是根据实际的需要&#xff0c;比如带外衰减的程度&#xff0c;带内波动&#xff08;平坦&#xff09;如何&#xff0c;还有群时延等等决定的。比如不要求矩形系数选什么。。 …

用Python实现交互式数据可视化:从基础图表到动态仪表板

用Python实现交互式数据可视化&#xff1a;从基础图表到动态仪表板 一、项目背景 本文将通过一个完整的Python项目&#xff0c;展示如何使用Plotly和ipywidgets构建从基础统计到动态交互的全栈数据可视化方案。 二、核心功能模块 1. 数据生成与预处理 np.random.seed(100)…

【深度学习与大模型基础】第6章-对角矩阵,对称矩阵,正交矩阵

一、对角矩阵 对角矩阵&#xff08;Diagonal Matrix&#xff09;是一种特殊的方阵&#xff0c;其非对角线上的元素均为零&#xff0c;只有对角线上的元素可能非零。具体来说&#xff0c;对于一个 nn的矩阵 A[]&#xff0c;如果满足 则 AA 称为对角矩阵。对角矩阵通常表示为&am…

初识R语言饼状图

目录 基础饼图 标签个性化 边界修改 密度条纹 边框颜色 基础饼图 rm(list ls())# Create Data Prop <- c(3,7,9,1,2) # Make the default Pie Plot P1 <- pie(Prop) dev.off() 标签个性化 P2 <-pie(Prop , labels c("Gr-A","Gr-B","…

Spring MVC 拦截器使用

javaweb过滤器和springmvc拦截器&#xff1a; 拦截器的概念 拦截器使用 1/创建拦截器类&#xff0c;类中实现 handler执行前&#xff0c;执行后与渲染视图后的具体实现方法 public class GlobalExceptionHandler implements HandlerInterceptor {// if( ! preHandler()){re…

汽车机械钥匙升级一键启动的优点

汽车机械钥匙升级一键启动的优点主要包括&#xff1a; 便捷性&#xff1a;一键启动功能的引入极大地提升了用车便捷性。车主无需翻找钥匙&#xff0c;只需在车辆感应范围内轻触启动键&#xff0c;即可轻松发动汽车。 安全性&#xff1a;移动管家专车专用一键启动系统配备了防…

中小企业如何低成本构建高效专属网络?

对于许多中小企业管理者而言&#xff0c;构建一套安全、灵活且可扩展的专网系统是数字化转型的“必修课”。本文将从实际业务场景出发&#xff0c;拆解企业组网的核心步骤&#xff0c;并提供可落地的实施方案建议&#xff0c;帮助您快速匹配适合自身需求的网络服务商。 一、组网…

【C++】 —— 笔试刷题day_6

刷题day_6&#xff0c;继续加油哇&#xff01; 今天这三道题全是高精度算法 一、大数加法 题目链接&#xff1a;大数加法 题目解析与解题思路 OK&#xff0c;这道题题目描述很简单&#xff0c;就是给我们两个字符串形式的数字&#xff0c;让我们计算这两个数字的和 看题目我…

pytorch 网络结构可视化Netron安装使用方法(已解决)

首先 要把保存的训练模型 转为onnx格式的文件&#xff0c;然后打开下面的链接&#xff0c;选择刚刚转的onnx文件。 下载 Netron&#xff1a; 您可以访问 Netron 的官方网站 在线使用&#xff0c;或者下载桌面版本。 mnist_cnn_model.onnx 确定后&#xff0c; 2、TensorRT学习…

第六:go 操作 redis-go

Redis 在项目开发中redis的使用也比较频繁&#xff0c;本文介绍了Go语言中go-redis库的基本使用。 Redis介绍 Redis是一个开源的内存数据库&#xff0c;Redis提供了多种不同类型的数据结构&#xff0c;很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外&am…

【蓝桥杯】每天一题,理解逻辑(4/90)【Leetcode 二进制求和】

题目描述 我们解析一下题目 我们可以理解到两个主要信息 给的是二进制的字符串返回他们的和 我们知道&#xff0c;十进制的加减法需要进位&#xff0c;例如&#xff1a;9716是因为91之后进了一位&#xff0c;二进制也是如此&#xff0c;只不过十进制是逢10进1&#xff0c;二…

快速入手-基于Django的mysql配置(三)

Django开发操作数据库更简单&#xff0c;内部提供了ORM框架。比如mysql&#xff0c;旧版本用pymysql对比较多&#xff0c;新的版本采用mysqlclient。 1、安装mysql模块 pip install mysqlclient 2、Django的ORM主要做了两件事 &#xff08;1&#xff09;CRUD数据库中的表&am…

docker部署dify

1.安装docker 参考链接 https://ascendking.blog.csdn.net/article/details/136407383 设置docker源 vim /etc/docker/daemon.json {"registry-mirrors": ["https://docker.registry.cyou", "https://docker-cf.registry.cyou", "http…

网络安全红蓝对抗实战演练,沉浸式对抗训练场上线!

在网络安全的世界里&#xff0c;没有永恒的盾牌&#xff0c;只有不断磨砺的利剑。近年来&#xff0c;某金融机构因系统漏洞导致千万级用户数据泄露&#xff0c;某制造企业因生产线遭遇勒索攻击被迫停产数日——这些真实案例揭示了一个残酷现实&#xff1a;传统的理论教学已无法…

舞狮表演(dp)

#include <bits/stdc.h> using namespace std; const int N1e35; int main() {int t;cin>>t;while(t--){int n;cin>>n;int a[N][N];for(int i1;i<n;i){for(int j1;j<n;j){int x;cin>>x;if(x&1) a[i][j]1; // 如果金额是奇数&#xff0c;a[i]…

【Qt】Qt + Modbus 服务端学习笔记

《Qt Modbus 服务端学习笔记》 1.因为项目的需要&#xff0c;要写一个modbus通信&#xff0c;csdn上感觉有些回答&#xff0c;代码是人工智能生成的&#xff0c;有些细节不对。我这个经过实测&#xff0c;是可以直接用的。 首先要包含Qt 的相关模块 Qt Modbus 模块主要包含以…