类似vant中的tab实现

在使用 vant weapp 开发微信小程序过程中tab组件无法满足现需求,不能在tab项中添加图片;于是自己动手写了一个tab组件,并实现点击后将选中的tab移动到视线中央;

html

<div class="nav_left" ref="nav_left">
  <div class="nav_group" ref="nav_group">
    <div
            class="nav_btn"
            ref="nav_btn"
            v-for="(item, index) in navList"
            :key="item.id"
            :class="item.id === activeSpotType ? 'map_top_active' : ''"
            @click="navClick(index)"
            >
      <img
           class="icon"
           v-if="item.id === activeSpotType"
           :src="item.iconSrc"
           />
      <img class="icon" v-else :src="item.iconSrc" />
      {{ item.name }}
    </div>
  </div>
</div>

css (css使用的是scss预编译),

.nav_left {
  display: inline-block;
  vertical-align: top;
  width: calc(100% - 35px);
  height: 100%;
  .nav_group {
    width: 100%;
    height: 100%;
    white-space: nowrap;
    display: flex;
    overflow-x: scroll;
    scroll-behavior: smooth; // 滚动行为 平稳
    &::-webkit-scrollbar {
      display: none;
    }
  }
  .nav_btn {
    margin-top: 7px;
    padding: 6px 20px;
    display: inline-block;
    font-size: 14px;
    border: none;
    height: 32px;
    line-height: 17px;
    background: #FFF;
    box-sizing: border-box;
    outline: none;
    border-radius: 18px;
    .icon{
      width: 16px;
      height: 17px;
      margin-right: 2px;
      vertical-align: bottom;
    }
  }
  .map_top_active {
    color: #FFF;
    background: rgba(81, 166, 255, 1);
    transition: all ease 0.3s;
  }
}

js

export default {
  data(){
      activeSpotType: '12301',
      navList: [
       {
          id: '12301',
          name: '女装',
          selectedIcon: '选中时的图标链接',
          unselectedIcon: '未选中时的图标链接',
        },
       {
          id: '12302',
          name: '男装',
          selectedIcon: '选中时的图标链接',
          unselectedIcon: '未选中时的图标链接',
        },
       {
          id: '12303',
          name: '数码',
          selectedIcon: '选中时的图标链接',
          unselectedIcon: '未选中时的图标链接',
        },
       {
          id: '12304',
          name: '家电',
          selectedIcon: '选中时的图标链接',
          unselectedIcon: '未选中时的图标链接',
        },
       {
          id: '12305',
          name: '生鲜',
          selectedIcon: '选中时的图标链接',
          unselectedIcon: '未选中时的图标链接',
        }
      ]  
  },
  methods {
    // tab点击事件
    navClick(index) {
      this.activeSpotType = this.navList[index].id;
      this.handleScroll(index)
    },

    // 选中tab时滚动居中
    handleScroll(index) {
      let boxHalf = this.$refs.nemu.offsetWidth / 2;
      // 单个item的宽度
      let itemLeft = this.$refs.nav_btn[index].offsetLeft;
      // 选中状态的item宽度
      let itemWidth = this.$refs.nav_btn[index].offsetWidth;
      
      // 使用js添加滚动过渡动画
      let scrollLeft = 0;
      if (boxHalf <= itemLeft) {
        // 设置滚动距离实现当前选中item居中显示
        // 添加 css: overflow-x: scroll; scroll-behavior: smooth; // 设置之后滚动更丝滑
        // this.$refs.nav_group.scrollLeft = (itemLeft - boxHalf) + itemWidth / 2
        scrollLeft =  (itemLeft - boxHalf) + itemWidth / 2
      }else {
        // this.$refs.nav_group.scrollLeft = 0
        scrollLeft = 0;
      }
      this.scrollSmoothTo(this,scrollLeft)
      
      // 使用css添加滚动过渡动画
      if (boxHalf <= itemLeft) {
        // 设置滚动距离实现当前选中item居中显示
        // 在 overflow-x: scroll;的元素中添加css: scroll-behavior: smooth;
        this.$refs.nav_group.scrollLeft = (itemLeft - boxHalf) + itemWidth / 2
      }else {
        this.$refs.nav_group.scrollLeft = 0
      }
    },

    // 居中时添加过渡动画; scroll-behavior 的兼容性很差,移动端基本不能用使用,所以还得使用js来实现过渡效果 
    scrollSmoothTo (_this,position) {
      if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback, element) {
          return setTimeout(callback, 17);
        };
      }
      // 当前滚动高度
      let currentLeft = _this.$refs.nav_group.scrollLeft;
      // 滚动step方法
      var step = function () {
        // 距离目标滚动距离
        var distance = position - currentLeft;
        // 目标滚动位置
        currentLeft = currentLeft + distance / 5;
        if (Math.abs(distance) < 1) {
          _this.$refs.nav_group.scrollLeft = position
        } else {
          _this.$refs.nav_group.scrollLeft = currentLeft
          requestAnimationFrame(step);
        }
      };
      step();
    },
  }
}

实例图:

拓展:平滑滚动 - scroll-behavior 

这个属性的兼容性不太友好;
移动端基本不能用;
在PC端使用不考虑兼容性的情况话还是很不错的(scroll-behavior: smooth; 一行代码解决所有问题)

posted @ 2021-11-17 10:05  yangchin9  阅读(326)  评论(0编辑  收藏  举报