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

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

javascript 复制代码
<!--
  双栏联动滚动组件 - 技术解析
  功能特性:
  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 控制样式,易于主题定制

使用示例

javascript 复制代码
<template>
  <dual-scroll 
    :left-data="categories" 
    :right-data="contents"
  />
</template>

<script>
// 示例数据结构
const categories = ['水果', '蔬菜', '肉类']
const contents = [
  {
    title: '水果',
    content: ['苹果', '香蕉', '橙子']
  },
  {
    title: '蔬菜',
    content: ['白菜', '萝卜', '番茄']
  }
]
</script>
相关推荐
fakaifa4 小时前
【最新版】龙兵名片V152独立版系统源码+在线更新+搭建教程
小程序·uni-app·开源·php·源码下载·龙兵名片·名片小程序
旅行中的伊蕾娜8 小时前
uniapp可拖拽消息数徽标draggable-badge,仿手机qq聊天列表未读数徽标动效
uni-app
老白干8 小时前
uniapp工程中解析markdown文件
uni-app
前端_yu小白12 小时前
uniapp配置代理解决跨域问题
uni-app·代理跨域
ABCHERRY712 小时前
uniapp发布成harmony时报错找不到@uni_modules/uni-push包跟这个包@uni_modules/hmr-for-uni-app
前端·uni-app·打包·harmony
浪裡遊14 小时前
我的uniapp自定义模板
开发语言·前端·uni-app·个人开发
奔跑吧邓邓子17 小时前
【商城实战(51)】从uniapp商城到PC端的华丽转身:适配与优化全攻略
uni-app·商城实战·pc端适配
浪裡遊19 小时前
uniapp特有生命周期钩子
前端·vue.js·uni-app
浪裡遊21 小时前
uniapp中的路由、本地存储与网络请求
开发语言·前端·javascript·前端框架·uni-app