vue项目实现Tab页面触底上拉切换下个Tab

lv_0_20251113151402 lv_0_20251113151518

如题,实现Tab页面触底上拉切换下一Tab的功能,关键流程:下滑触底——显示“上拉切换下一页”——继续上拉超过一定距离(如100px)——切换下一页,重置相关参数。

关键代码:

一. 处理滚动的业务逻辑封装为goodsListScroll.js,代码如下:

//触底上拉切换下一页相关代码
export default {
  props: {
    last_tab: false, //是否是最后一个tab
  },
  data() {
    return {
      startY: 0,
      maxScroll: 0, //最大可滚动距离
      showHint: false, //底部显示切换下一页提示条      
      touchStart: false, //触摸开始置为true,触发Tab切换后重置为false,终止handleTouchMove中的逻辑
      isScrolling: false, //显示“切换下一Tab”后触摸开始标记为true、切换Tab后重置为false
    };
  },
  beforeDestroy() {
    // 移除事件监听
    this.$refs.container.removeEventListener('touchstart', this.handleTouchStart);
    this.$refs.container.removeEventListener('touchmove', this.handleTouchMove);
  },
  methods: {
    //重新计算最大滚动值(因为内容可能变化),获取商品列表后调用
    calculateMaxScroll() {
      if (this.$refs.container) {
        const element = this.$refs.container;
        this.maxScroll = element.scrollHeight - element.clientHeight;
        //不可滚动页面直接显示        
        if (this.maxScroll === 0 && (this.active < this.subCateList.length - 1)) {
          this.showHint = true
        }
      }
    },
    //容器滚动监听事件
    handleScroll(event) {
      const element = event.target;
      this.scrollTop = element.scrollTop;

      // 计算滚动百分比
      this.scrollPercentage = this.maxScroll > 0 ?
        Math.min(100, Math.round((this.scrollTop / this.maxScroll) * 100)) :
        0;

      // 检查是否滚动到底部
      if (element.scrollHeight == element.clientHeight) return; //初始化的时候高度会相等
      const isAtBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10;

      // 显示或隐藏提示
      if (this.active < this.subCateList.length - 1) this.showHint = isAtBottom;
    },
    /**
     * @param {Object} e
     * 触摸开始标记,必须在这里设置,切换tab后置为false,终止handleTouchMove
     */
    handleTouchStart(e) {
      this.touchStart = true
    },
    /**
     * 下滑触底上拉切换tab
     * 1. touchStart:触摸开始标志,切换tab后为false,终止代码执行
     * 2. isScrolling:页面上拉触底后置为true,同时记录当前位置作为滑动开始位置
     * 3. startY:计算触底上拉的初始位置,与实际触摸开始无关
     */
    handleTouchMove(e) {
      //1. 滑动过程中已经触发过Tab切换的就不再执行
      if (!this.touchStart) return;

      //2. 已触底且isScrolling为false时获取滑动的位置,并将isScrolling置为true
      if (this.showHint && !this.isScrolling) {
        this.isScrolling = true
        //3. 设置计算滑动距离的初始位置
        this.startY = e.touches[0].clientY;
      }

      //滑动过程中实时获取当前位置,并计算滑动距离
      const currentY = e.touches[0].clientY;
      const deltaY = this.startY - currentY; // 正值表示向上滑动

      // 只有向上滑动才处理
      if (deltaY > 0) {
        // 当向上滑动距离超过100px时触发切换
        if (deltaY > 100) {
          this.switchToNextTab();
        }
      }
    },
    //切换至下一个Tab
    switchToNextTab() {
      // 移除事件监听
      this.$refs.container.removeEventListener('touchstart', this.handleTouchStart);
      this.$refs.container.removeEventListener('touchmove', this.handleTouchMove);
      if (this.active < this.subCateList.length - 1) {
        this.active = this.active + 1
        //重置其他参数
        this.touchStart = false
        this.showHint = false;
        this.isScrolling = false;
        this.startY = 0;
        this.maxScroll = 0
      }
    }
  }
};

以上为完整代码,关键点有这些:

1. calculateMaxScroll():计算容器最大滚动值,在获取到商品列表数据后调用,不可滚动页面直接显示“上拉切换下一页”提示条;

2. handleScroll():商品列表容器滚动监听事件,针对可滚动页面在滑动到页面底部时,显示“上拉切换下一页”提示条;

3. handleTouchStart():滑动开始事件,将滑动开始标志touchStart置为true;

4. handleTouchMove():滑动过程监听,触底上拉超过一定距离,触发切换下一Tab的逻辑switchToNextTab();

5. switchToNextTab():切换下一Tab逻辑,移除滚动监听,所有相关参数重置。

代码中已经加了详细的注释,可以结合注释来看。

二. 页面相关代码:

<template>
  <div id="container" class="h100" ref="container" @scroll="handleScroll">
    <!--tab栏-->
    <van-tabs id="tabs" class="p_fixed top left w100" v-model="active" :title-active-color="redColor" :background="primaryColor" :color="primaryColor" title-inactive-color="#a8a8a8" swipe-threshold="4" sticky>
      <van-tab v-for="(item,index) in cateList" :title="item.title">
      </van-tab>
    </van-tabs>
    <!--商品列表栏-->
    <div class="goods_list w100 flex space_between" v-show="goodsList">
      <!--商品列表展示代码-->
    </div>
    <!--触达切换tab-->
    <div v-if="!last_tab" class="switch-hint p_fixed c_f flex center f12" :class="{ show: showHint,hide:!showHint&&startY==0 }">
      上拉切换下一页
    </div>
  </div>
</template>

<script>
  import { Tabs, Tab } from 'vant';
  import goodsListScroll from "@/assets/js/mixin/goodsListScroll.js"
  export default {
    name: "CateGoods",
    mixins: [goodsListScroll],
    components: {
      Nodata,
      [Tab.name]: Tab,
      [Tabs.name]: Tabs
    },
    data() {
      return {
        active: 0,
        cateList: '', //子分类列表
        goodsList: '', //瀑布流商品列表   
      }
    },
    watch: {
      //监听tab选择索引,获取对应的商品列表
      active(newValue, oldValue) {
        if (newValue > -1) {
          this.goodsList = ''
          let sub_rack_id = this.subCateList[this.active].sub_rack_id
          //获取商品列表
          this.getGoodsList(sub_rack_id, this.rack_id)
        }
      }
    },
    methods: {
      /**
       * @param {Object} rack_id
       * 获取商品列表
       */
      getGoodsList(rack_id, rack_parent_id) {
        //调用接口获取商品列表数据,这里代码省略

        //获取商品列表后监听页面滚动
        if (!this.last_tab) {
          // 确保DOM更新后计算最大滚动值
          this.$nextTick(() => {
            this.scrollTopFn()
            // 添加触摸事件监听
            this.$refs.container.addEventListener('touchstart', this.handleTouchStart, { passive: false });
            this.$refs.container.addEventListener('touchmove', this.handleTouchMove, { passive: false });
            //计算页面可以滚动距离
            this.calculateMaxScroll();
          });
        }
      } else {
        this.goodsList = []
      }
    });
  }
</script>

<style lang="scss" scoped>
  #container {
    overflow-y: scroll;
  }

  /*滚动触底上拉切换下一个Tab相关样式*/
  .switch-hint {
    bottom: 30px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.7);
    padding: 12px 30px;
    border-radius: 25px;
    opacity: 0;
    transition: opacity 0.5s, transform 0.5s;
    pointer-events: none;
    z-index: 100;
  }

  .switch-hint.show {
    opacity: 1;
    transform: translateX(-50%) translateY(-10px);
  }

  .switch-hint.hide {
    transition: none;
  }
</style>

如上,页面+goodsListScroll即可实现Tab页面的触底上拉切换下一Tab的功能。

其中添加“上拉切换下一页”是为了优化用户体验,如果没有提示条,直接切换回显的有些突兀。

后记:

比较关键的点就是触底上拉切换Tab应该以触底上拉一定距离触发切换下一Tab的逻辑之后就应该结束,跟滑动是否结束无关。否则将会出现手指不松开一直上拉,可能会一直触发切换Tab,这个点要注意。

 

posted on 2025-11-17 14:57  逍遥云天  阅读(18)  评论(0)    收藏  举报

导航