本文基于提供的代碼實現一個左右聯動的滾動組件,以下是詳細的代碼解析與實現原理說明:
<!--雙欄聯動滾動組件 - 技術解析功能特性: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><!-- 導航項循環渲染 --><viewv-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><!-- 內容區塊循環渲染 --><viewv-for="(section, sIndex) in rightData":key="sIndex":id="'content-' + sIndex"class="content-section"><view class="section-title">{{ section.title }}</view><viewv-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項滾動},// 右側內容自動定位IDrightScrollId() {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.heightresolve()}).exec()})},/*** 計算內容區塊位置* 使用 uni API 獲取元素位置信息*/calcSectionPositions() {uni.createSelectorQuery().in(this).selectAll('.content-section').boundingClientRect((res) => {// 緩存各章節頂部位置this.sectionPositions = res.map((item) => item.top - this.containerTop)this.positionsReady = truelet lastHeight = res[res.length - 1].heightconsole.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) returnconst scrollTop = e.detail.scrollTopconst positions = this.sectionPositions// 二分查找算法優化(當前使用順序查找)let current = this.currentIndexwhile (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>