在 uni-app
开发中,横向滚动菜单栏(如 Tab 切换栏)是常见的交互需求。为了让当前选中的 Tab 自动居中显示,需要动态获取 scroll-view
中子元素的位置和宽度。
1. 实现效果
- 横向滚动菜单栏中,点击某个 Tab 时,自动计算滚动位置,使该 Tab 居中显示。
- 动态获取
scroll-view
的容器宽度和每个子元素的位置、宽度信息。
2. 实现代码
模板部分
<template>
<scroll-view
scroll-x="true"
scroll-with-animation="true"
:scroll-left="scrollLeft"
class="scroll-view"
>
<view class="flex-row items-center justify-start">
<view
class="item mr-20 flex-row items-center justify-center"
v-for="(v, i) in list"
:key="i"
:class="['item', v.isChecked ? 'item-active' : '']"
@click="changeTabs(i)"
>
<image
class="w-24 h-24 mr-6"
src="@/static/image/home/block_address_icon.png"
mode="aspectFit"
></image>
<text>{{ v.name }}</text>
</view>
</view>
</scroll-view>
</template>
脚本部分
<script>
export default {
data() {
return {
list: [], // Tab 数据(需包含 name, isChecked 字段)
contentScrollW: 0, // scroll-view 容器宽度
curIndex: 0, // 当前选中索引
scrollLeft: 0, // 横向滚动位置
};
},
mounted() {
this.getScrollW();
},
methods: {
// 获取容器宽度和子元素信息
getScrollW() {
const query = uni.createSelectorQuery().in(this);
query.select(".scroll-view").boundingClientRect();
query.selectAll(".item").boundingClientRect();
query.exec((res) => {
if (res && res[0]) {
// 1. 获取 scroll-view 容器宽度
this.contentScrollW = res[0].width;
// 2. 获取子元素的 left 和 width
const items = res[1] || [];
this.list = this.list.map((item, index) => {
return {
...item,
left: items[index]?.left || 0,
width: items[index]?.width || 0,
};
});
}
});
},
// 切换 Tab 并计算滚动位置
changeTabs(index) {
if (index === this.curIndex) return;
this.curIndex = index;
this.list = this.list.map((item, idx) => ({
...item,
isChecked: idx === index,
}));
// 计算居中滚动位置
const targetLeft = this.list[index].left;
const targetWidth = this.list[index].width;
this.scrollLeft = targetLeft - this.contentScrollW / 2 + targetWidth / 2;
},
},
};
</script>
样式部分
<style scoped>
.scroll-view {
white-space: nowrap;
width: 100%;
}
.item {
display: inline-flex;
padding: 10rpx 20rpx;
transition: all 0.3s;
}
.item-active {
background: #f5f5f5;
border-radius: 8rpx;
}
</style>
3. 实现原理
步骤 1:获取容器和子元素信息
- 使用
uni.createSelectorQuery
获取scroll-view
的宽度(contentScrollW
)。 - 通过
selectAll
获取所有子元素的位置(left
)和宽度(width
),并存储到list
中。
步骤 2:计算滚动位置
点击 Tab 时,通过以下公式计算 scrollLeft
:
scrollLeft = targetLeft - (容器宽度 / 2) + (子元素宽度 / 2)
- **
targetLeft
**: 子元素左侧距离scroll-view
左边的距离。 - **
容器宽度 / 2
**: 让子元素滚动到容器中间。 - **
子元素宽度 / 2
**: 进一步微调,确保子元素完全居中。
4. 关键点总结
-
合并查询请求
使用query.select
和query.selectAll
合并多个 DOM 查询,通过一次exec
获取全部结果,避免异步问题。 -
响应式更新数据
更新list
时直接替换数组(this.list = [...]
),确保 Vue 的响应式系统触发视图更新。 -
居中滚动公式
通过scrollLeft = targetLeft - contentScrollW / 2 + targetWidth / 2
精确计算滚动位置。 -
性能优化
- 使用
scroll-with-animation
开启滚动动画。 - 避免频繁调用
getScrollW
,仅在mounted
中执行一次。
- 使用
5. 常见问题
- 子元素未渲染:确保
getScrollW
在mounted
或数据加载完成后调用。 - 横向滚动失效:检查 CSS 样式,确保
scroll-view
和子元素使用white-space: nowrap
和display: inline-flex
。