uni-app【微信小程序】顶部tab栏与页面内容联动

本文详细描述了一个使用Vue开发的项目,其中涉及顶部Tab栏的横向滚动和底部内容区域的上下滚动同步。开发者分享了代码片段,展示了如何通过计算滚动位置来保证不同长度分类名称的兼容性,并提到了注意事项,如处理margin影响的滚动计算。
摘要由CSDN通过智能技术生成

功能需求

1、需要每次点击tab的位置滚动到第一个,同时下面页面也需要滚动

2、下面内容左右滚动时,顶部tab也需要发生改变

具体效果展示

一、实现的原理

分为2部分顶部tab标签和底部swiper

顶部tab标签需要横向滚动 scroll-view

二、直接上代码,我写注释了

<template>
  <view class="container">
    <!-- 顶部模板滚动 -->
    <scroll-view class="cates" scroll-x="true" scroll-with-animation :scroll-left="scrollViewScrollLeft">
      <view v-for="(cate, index) in cates" :key="index" class="cate-item" :class="{ 'cate-item-active': currentId === cate.id }" @click="handleCateClick(cate.id)">
        {{ cate.name }}
      </view>
    </scroll-view>
    <!-- 内容左右滑动 -->
    <swiper class="content" :current="currentSwiperIndex" @change="handleSwiperChange" :style="{ height: '600px' }">
      <swiper-item v-for="(cate, index) in cates" :key="index" class="swipe-content">
        <scroll-view scroll-y="true" :style="{ height: '600px' }">
          <view class="con-item" v-for="service in serviceList" :key="service.id">
            {{ service.name }}
          </view>
        </scroll-view>
      </swiper-item>
    </swiper>
  </view>
</template>

<script lang="ts" setup>
import { ref, reactive } from 'vue';

const cates = reactive([
  { id: '1', name: '模板1' },
  { id: '2', name: '模板22' },
  { id: '3', name: '模板3' },
  { id: '4', name: '模板44' },
  { id: '5', name: '模板5555' },
  { id: '6', name: '模板66' },
  { id: '7', name: '模板77' },
  { id: '8', name: '模板8' },
  { id: '9', name: '模板9999' },
  // 添加更多分类...
]);

// 商品或服务列表
const serviceList = ref([{ id: '1', name: '模板1' }]);

// 当前选中的分类ID
const currentId = ref(cates[0].id);

// 存储scroll-view的滚动位置
const scrollViewScrollLeft = ref(0);

// 定义当前swiper页面的索引
const currentSwiperIndex = ref(0);

const calculateScrollPosition = async (index: number = 0) => {
  // 创建一个查询器,用于收集页面中的特定节点信息。
  const query = uni.createSelectorQuery();

  // 通过查询器查询所有class为`cate-item`的元素,即所有分类项,并获取它们的边框大小信息。
  query.selectAll('.cate-item').boundingClientRect();

  // 通过查询器查询class为`cates`的元素,即滚动视图容器,并获取它的边框大小信息。
  query.select('.cates').boundingClientRect();

  // 执行查询,结果返回在回调函数的参数`res`中。
  query.exec((res) => {
    // 定义变量`totalWidthBeforeCurrent`用来记录当前选中分类项之前所有分类项的总宽度。
    let totalWidthBeforeCurrent = 0;

    // 遍历查询结果,res[0]包含了所有分类项的边框大小信息
    res[0].forEach((item, idx) => {
      // 对于当前选中分类项之前的所有分类项(即索引小于`index`的项),
      // 累加它们的宽度到`totalWidthBeforeCurrent`变量。
      if (idx < index) {
        totalWidthBeforeCurrent += item.width;
      }
    });

    // 根据计算出的`totalWidthBeforeCurrent`值,
    // 更新滚动视图的滚动位置,使得当前选中的分类项尽可能滚动到视图区域中可见。
    scrollViewScrollLeft.value = totalWidthBeforeCurrent;
  });
};
// 处理分类点击
const handleCateClick = (id: string) => {
  const index = cates.findIndex((cate) => cate.id === id);
  currentId.value = id;
  handleDataLoad();

  // 更新swiper索引
  currentSwiperIndex.value = index;

  // 计算滚动位置
  calculateScrollPosition(index);
};
// 处理swiper的change事件
const handleSwiperChange = (event: any) => {
  const { current } = event.detail;
  currentId.value = cates[current].id;
  handleDataLoad();

  // 计算滚动位置
  calculateScrollPosition(current);
};
// 处理数据加载
const handleDataLoad = () => {
  serviceList.value = [{ id: '1', name: `模板${currentId.value}` }];
};

onMounted(() => {
  calculateScrollPosition();
});
</script>

<style>
.container {
  width: 100%;
}
.cates {
  white-space: nowrap;
  background-color: #f5f5f5;
}
.cate-item {
  display: inline-block;
  padding: 10px;
  color: #333;
}
.cate-item-active {
  color: #9d630d;
}
.content {
  margin-top: 20px;
}
.swipe-content {
  text-align: center;
}
</style>

三、答疑解惑

1、为什么要写个计算方法

calculateScrollPosition 自动计算滚动的位置,这样不管我的分类名称是2个字3个字4个字不管多少个字我都可以兼容,如果定死宽度兼容不了

2、注意事项

如果你的tab会设置margin这个属性你需要注意得给他加到totalWidthBeforeCurrent上面

const calculateScrollPosition = async (index: number = 0) => {
  const query = uni.createSelectorQuery();
  query.selectAll('.cate-item').boundingClientRect();
  query.select('.cates').boundingClientRect();
  query.exec((res) => {
    let totalWidthBeforeCurrent = 0;
    res[0].forEach((item, idx) => {
      if (idx < index) {
        totalWidthBeforeCurrent += item.width;
        // 如果你要给tab 每个模板设置margin属性你设置多少就需要在这里加上多少
        // 加入你给每个 tab设置了 margin-right:20px
        totalWidthBeforeCurrent += 20;
      }
    });
    scrollViewScrollLeft.value = totalWidthBeforeCurrent;
  });
};

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值