uniapp 滚动tab组件

第一种方法 :使用<scroll>中的 scroll-into-view 属性

缺点:无法保持选中tab居中

代码:

<template>
  <view class="help-list-view">
    <scroll-view
      scroll-x
      :scroll-into-view="scrollinto"
      :scroll-with-animation="true"
      :show-scrollbar="false"
      class="dir-box"
    >
      <view
        class="scroll-row-item"
        @click="changeTab(index)"
        v-for="(item, index) in tabBars"
        :key="index"
        :id="'tab' + index"
      >
        <text :class="tabIndex === index ? 'main-text-color' : 'default-text-color'">
          {{ item.directoryName }}
        </text>
      </view>
    </scroll-view>
    <swiper
      :style="{ height: fullHeight + 'px' }"
      :duration="150"
      :current="tabIndex"
      @change="onChangeTab"
    >
      <swiper-item v-for="(item, index) in tabBars" :key="index">
        <scroll-view scroll-y="true" :show-scrollbar="false" class="tab-content">
          <HelpList @clickItem="clickItem" :dataList="item.articles" />
        </scroll-view>
      </swiper-item>
    </swiper>
  </view>
</template>

<script>
import { mobileDirectoryList } from '@/api/helpCenter'
import HelpList from './components/helpList.vue'
export default {
  components: { HelpList },
  data() {
    return {
      tabIndex: 0,
      scrollinto: 'tab0',
      articleType: 3,
      fullHeight: 0,
      tabBars: [{ directoryName: '暂无', list: [{ title: '暂无' }] }]
    }
  },
  onLoad(e) {
    if (e.name == 'help') {
      this.articleType = 3
      uni.setNavigationBarTitle({
        title: '帮助中心'
      })
    } else if (e.name == 'policy') {
      this.articleType = 4
      uni.setNavigationBarTitle({
        title: '政策说明'
      })
    }
  },
  onShow() {
    mobileDirectoryList({ directoryId: this.articleType }).then(res => {
      console.log(res)
      this.tabBars = res
    })
  },
  async onPullDownRefresh() {
    await this.getData()
    uni.stopPullDownRefresh()
  },
  mounted() {
    var that = this
    uni.getSystemInfo({
      success: function (res) {
        that.fullHeight = res.windowHeight - 40
      }
    })
  },
  methods: {
    // 获取目录列表
    getData() {
      return mobileDirectoryList({ directoryId: 3 }).then(res => {
        this.tabBars = res
      })
    },
    // 切换选项卡
    changeTab(index) {
      if (this.tabIndex === index) {
        return
      }
      this.tabIndex = index
      this.scrollinto = 'tab' + index
      console.log(this.tabIndex, this.scrollinto)
    },
    // 监听滑动列表
    onChangeTab(e) {
      this.changeTab(e.detail.current)
    },
    clickItem(e) {
      const path = this.articleType==3?`?id=${e.id}&type=${this.articleType}`:`?id=${e.id}&type=${this.articleType}` 
      uni.navigateTo({
          url: `pages_b/pages/my-center/help-center/showArticle${path}`
        })
    }
  }
}
</script>

<style lang="scss" scoped>
.help-list-view {
  @include content-height(0px);
  padding-top: 12px;
  box-sizing: border-box;
  width: 100%;
  .main-text-color {
    font-weight: 600;
    font-size: 14px;
    color: #ff680c;
  }
  .default-text-color {
    font-weight: 400;
    font-size: 14px;
    color: #000000;
  }
  .dir-box {
    white-space: nowrap;
    width: 100%;
    padding: 6px 18px;
    padding-right: 0px;
    border-bottom: 1px #cccccc solid;
    box-sizing: border-box;
  }
  .scroll-row-item {
    display: inline-block;
    text-align: center;
    font-size: 12px;
    margin-right: 22px;
  }
  .tab-content {
    box-sizing: border-box;
    @include content-height(39px);
  }
}
</style>

第二种方法:通过动态的scrollLeft 设置

核心思想为:初始记录一下tab项 距离滚动条初始位置的值,每次点击计算并将其滚动到中间

此为scrollTab组件:

<template>
  <view>
    <scroll-view
      class="navigator-box"
      style="white-space: nowrap"
      :enable-flex="true"
      :scroll-left="scrollLeft"
      :show-scrollbar="false"
      scroll-x="true"
    >
      <view
        @click="e => scrollToIt(index, false, e, item)"
        :class="_active == index ? 'item active' : 'item'"
        :key="item[keyField]"
        v-for="(item, index) in tabList"
      >
        {{ item[showField] }}
      </view>
    </scroll-view>
  </view>
</template>

<script>
export default {
  props: {
    tabList: {
      type: Array,
      default() {
        return []
      }
    },
    active: {
      type: Number,
      default: 0
    },
    keyField: {
      type: String,
      default: 'seriesName'
    },
    showField: {
      type: String,
      default: 'navigationName'
    }
  },
  data() {
    return {
      scrollLeft: 0
    }
  },
  computed: {
    _active: {
      get() {
        return this.active
      },
      set(val) {
        this.$emit(`update:active`, val)
      }
    }
  },
  watch: {
    tabList:{
      handler(v) {
      if (v.length > 0) {
        // 延迟执行 此处h5编译时机拿不到节点
        this.$nextTick(() => {
          this.recordPos()
        })
      }
    },
    immediate:true,
    deep:true
  }
  },
  options: {
    styleIsolation: 'shared'
  },
  methods: {
    /**
     * 获取tab各个元素的初始位置
     */
    recordPos() {
      const query = uni.createSelectorQuery().in(this)
      query.selectAll('.navigator-box .item').boundingClientRect()
      query.exec(res => {
        this.offsetLefts = res[0]
      })
    },
    scrollToIt(index, noClick = true, e, item = {}) {
      // if(!this.offsetLefts){
      //   this.recordPos()
      // }
      if (index === this._active) return false
      this._active = index
      // 获取屏幕宽度
      const systemInfo = uni.getSystemInfoSync()
      const screenWidth = systemInfo.windowWidth
      this.$emit('scrollToIt', { index, noClick, e, item })
      // 计算左侧滚动宽度,为原始位置减去屏幕宽度一半,不足则滚动置为0
      this.$nextTick(() => {
        const activeEle = this.offsetLefts[index]
        const scrollLeft = activeEle.left - (screenWidth / 2 - activeEle.width / 2)
        this.scrollLeft = Math.max(0, scrollLeft)
      })
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep .navigator-box .uni-scroll-view-content {
  display: flex;
}

.navigator-box {
  background: #fff;
  position: sticky;
  top: 88rpx;
  z-index: 22;
  box-sizing: border-box;
  font-size: 28rpx;
}

/* #ifndef H5 */
.navigator-box {
  top: 0px;
}
/* #endif */

.navigator-box .title {
  margin-bottom: 10px;
  color: #fff;
  font-size: 16px;
}

.navigator-box .item {
  // 此处间隔要使用padding 因为要拿到width
  padding: 20rpx 30rpx;
  color: #333;
  text-align: center;
  font-size: 24rpx;
  display: inline-block;
}

.item.active {
  color: #ff680c;
}
</style>

使用:

      <ScrollTab
        ref="scrollTab"
        :tabList="tabBars"
        keyField="id"
        showField="directoryName"
        :active.sync="tabIndex"
        @scrollToIt="scrollToIt"
      />

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值