​UniApp 中动态获取 ScrollView 子元素位置并实现居中滚动

在 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:获取容器和子元素信息
  1. 使用 uni.createSelectorQuery 获取 scroll-view 的宽度(contentScrollW)。
  2. 通过 selectAll 获取所有子元素的位置(left)和宽度(width),并存储到 list 中。
步骤 2:计算滚动位置

点击 Tab 时,通过以下公式计算 scrollLeft

scrollLeft = targetLeft - (容器宽度 / 2) + (子元素宽度 / 2)
  • ​**targetLeft**: 子元素左侧距离 scroll-view 左边的距离。
  • ​**容器宽度 / 2**: 让子元素滚动到容器中间。
  • ​**子元素宽度 / 2**: 进一步微调,确保子元素完全居中。

4. 关键点总结

  1. 合并查询请求
    使用 query.select 和 query.selectAll 合并多个 DOM 查询,通过一次 exec 获取全部结果,避免异步问题。

  2. 响应式更新数据
    更新 list 时直接替换数组(this.list = [...]),确保 Vue 的响应式系统触发视图更新。

  3. 居中滚动公式
    通过 scrollLeft = targetLeft - contentScrollW / 2 + targetWidth / 2 精确计算滚动位置。

  4. 性能优化

    • 使用 scroll-with-animation 开启滚动动画。
    • 避免频繁调用 getScrollW,仅在 mounted 中执行一次。

5. 常见问题

  • 子元素未渲染:确保 getScrollW 在 mounted 或数据加载完成后调用。
  • 横向滚动失效:检查 CSS 样式,确保 scroll-view 和子元素使用 white-space: nowrap 和 display: inline-flex
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值