【微信小程序】scroll-tab-view滑动选项卡的实现

微信小程序实现滑动选项卡

关于“如何做小程序的滑动式Tab选项卡,方法是什么”, 有一些人不是很理解,今天简单实现一下小程序的滑动选项卡。

效果如下图所示:

tab-view

这种设计结构其实在移动端是非常常见的设计,无论是h5 , app 还是 小程序,今天我们就用微信小程序来实现这个功能组件。

首先我们可以把这个分成上下两部分结构,上面可以点击切换的 tab 标签和下面能切换的视图。

首先我们分析上面,其实对于前端同学来说实现上面部分有很多方式,无非就是几个标签,激活的那个标签加个样式而已,但是我们这里要多考虑如果tab较多或者标签的字数比较长,做过多语言的同学可能知道,中文翻译成其他各种外语的时候,有些单词可能会很长,所以我们在设计的时候考虑到这种场景,我们最好把上面tab的设计成可以滑动的,这样无论多长或者多少tab我们都不怕我们的布局会打乱。所以我们可以用小程序的 scroll-view 来实现上面的布局。

然后我们接着看下面,是不是很眼熟,没错,就是我们常见的轮播图结构。我们可以用swiper来实现视图区域,知道了怎么布局,接下来我们只需要将上下两部分联动起来就ok了。

我们只需要在项目的 components 文件夹下新建一个 scroll-tab-view 的组件(RN 同款的组件也叫这个名字,暂且也就叫这个吧),然后我们来实现布局部分:

<!--components/scroll-tab-view/index.wxml-->
<view>
  <!-- 顶部tab -->
  <scroll-view scroll-x="true" enable-flex="true" class="scroll_h" scroll-left="{{scrollLeft}}" >
    <view 
      wx:for="{{tabs}}" 
      wx:key="item" 
      data-current="{{index}}" 
      style="width: {{tabWidth + 'px'}}"
      class="['scroll_item', {{currentIndex === index ? 'active' : ''}}]"
      mut-bind:tap="changeTabs">
      <view class="tabItem" style="width: {{tabWidth + 'px'}}">
        <text>{{item}}</text>
      </view>
    </view>
    <!-- 选中的下划线 -->
    <view class="select_line" style="width: {{tabWidth + 'px'}}; left: {{(currentIndex * tabWidth) + 'px'}}"></view>
  </scroll-view>
  <!-- tab对应视图区域 -->
  <view class="scroll_content"> 
    <swiper class="tab_content" duration="300" current="{{currentIndex}}" bindchange="switchTabView">
      <swiper-item wx:for="{{tabs}}" wx:key="item">
        <scroll-view 
          scroll-y="true" 
          class="scoll_h" 
          refresher-enabled="true"
          refresher-default-style="black"
          refresher-threshold="{{80}}"
          refresher-triggered="{{isRefresh}}"
          refresher-background="#c5c5c5"
          bindrefresherrefresh="onRefresh"
        >
          <view class="content_view">
            <!-- 此处循环渲染插槽,用来显示我们tab对应的列表内容 -->
            <slot name="{{index}}"></slot>
          </view>
        </scroll-view>
      </swiper-item>
    </swiper>
  </view>
</view>

我们把选中时的下划线加上动画,让它看起来更加的丝滑,而不是直接通过 active 样式来修改border来实现。然后我们来给它加上样式。

/* components/scroll-tab-view/index.wxss */
.scroll_h {
  position: relative;
  display: flex;
  height: 40px;
  line-height: 40px;
  border-bottom: 0.5px solid #e4e4e4;
}
.scroll_item {
  height: 40px;
  display: inline-block;
}
.tabItem {
  display: flex;
  justify-content: center;
  /* width: 80px; */
  height: 50rpx;
}

.active {
  /* border-bottom: 6rpx solid #0d7eff; */
  color: #0d7eff;
  font-weight: 600;
}

.select_line {
  position: absolute;
  bottom: 20px; // 根据自己情况定位选中线位置
  height: 6rpx;
  background-color: #0d7eff;
  transition: left 0.2s ease-in-out;
}

.scroll_content {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 41px;
}

.tab_content {
  height: 100%;
}

.scoll_h {
  height: 100%;
}

.content_view {
  height: 100%;
  padding: 32rpx;
  background: #f1f1f1;
}

然后实现js部分:

// components/scroll-tab-view/index.js
Component({
  options:{
    multipleSlots:true   //这样就可以设置多个slot插槽
  },
  /**
   * 组件的属性列表
   */
  properties: {
    // tab选项卡标签的配置列表
    tabs: {
      type: Array,
      value: []
    },
    currentIndex: { // 当前选中的tab index
      type: [Number, String],
      value: 0
    },
    tabWidth: { // 每个tab设置的宽度
      type: Number,
      value: 80
    },
    isRefresh: {// 当前下拉刷新状态
      type: Boolean,
      value: false
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    scrollLeft: 0, // tab标题滚动条位置
  },

  /**
   * 组件的方法列表
   */
  methods: {
    changeTabs(e) {
      const currentIndex = e.currentTarget.dataset.current;
      this.triggerEvent('ChangeTab', {currentIndex});
      this.checkScrollBar(currentIndex);
    },
    // 切换视图
    switchTabView(e) {
      console.log('switchTabView', e);
      const currentIndex = e.detail.current;
      this.triggerEvent('ChangeTab', { currentIndex });
      this.checkScrollBar(currentIndex);
    },

    // 设置滚动条位置
    checkScrollBar(currentIndex) {
      // 获取屏幕宽度
      const { screenWidth } = wx.getSystemInfoSync();
      // 计算出完全显示在屏幕区的tab个数
      const $num = parseInt(screenWidth / this.data.tabWidth);
      if(currentIndex + 1 > $num) {
        this.setData({
          scrollLeft: $num * this.data.tabWidth
        })
      } else {
        this.setData({
          scrollLeft: 0
        })
      }
    },

    // 触发下拉刷新
    onRefresh() {
      this.triggerEvent('refresh', {
        current: this.data.currentIndex,
      })
    }
  }
})

这样我们就已经完成了我们组件的设计和实现,下面我们用该组件来实现我们上面图中的页面功能。

<!--pages/news/index.wxml-->
<view class="my_order">
  <scroll-tab-view 
    tabs="{{tabs}}" 
    currentIndex="{{currentIndex}}" 
    isRefresh="{{isRefresh}}"
    bindChangeTab="changeTab" 
    bindrefresh="refresh">
    
    <!-- 视图1 -->
    <view slot="0" class="list_contain">
      <view wx:for="{{list}}" wx:key="id" class="list_item">
        <view>{{item.title}}</view>
        <view class="list_date">{{item.date}}</view>
        <view class="list_content"> 
          <view class="list_img">
            <image src="{{item.url}}"></image>
          </view>
          <text>{{item.content}}</text>
        </view>
      </view>
    </view>

    <!-- 视图2 -->
    <view slot="1" class="list_contain">
      <!-- ... -->
    </view>

    <!-- 视图3 -->
    <view slot="2" class="list_contain">
      <!-- ... -->
    </view>

    <!-- 视图4 -->
    <view slot="3" class="list_contain">
      <!-- ... -->
    </view>
  </scroll-tab-view>
</view>

js:

// pages/news/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    tabs: ['健康', '情感', '职场', '育儿', '财经', '其他'],
    currentIndex: 0, // 当前选中的tab
    list: [], // 数据源
    showLoading: false, // loading
    isRefresh: false // 下拉刷新状态
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.getNewsList();
  },

  /**
   * 生命周期函数--监听页面显示
   * 自定义tabbar的部分,可以忽略
   */
  onShow: function () {
    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      const tabBar = this.getTabBar();
      let roleId = wx.getStorageSync('role');
      let selected = roleId ? 1 : 0;
      tabBar.setData({
        selected
      })
    }
  },
  
  // 切换tab,保存当前选中的index
  changeTab(e) {
    const { currentIndex } = e.detail;
    this.setData({ currentIndex });
  },

  // 获取列表数据
  getNewsList() {
    let _this = this;
    wx.request({
      url: 'https://miniapp.com/getNewsList',
      success(res) {
        const { list } = Reflect.get(res, 'data');
        _this.setData({
          showLoading: false,
          isRefresh: false, 
          list
        });
      },
      fail(e) {
        wx.showToast({
          title: e.errMsg,
        })
        _this.setData({ isRefresh: false })
      }
    })
  },

  /*
   * 列表下拉刷新(忽略)
   */
  refresh() {
    this.getNewsList();
  }
})

css:

/* pages/myOrder/myOrder.wxss */
.my_order {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: calc(env(safe-area-inset-bottom) + 48px);
}
.list_contain {
  height: 100%;
}
.list_item {
  display: flex;
  flex-direction: column;
  border-radius: 5px;
  background: #fff;
  margin-bottom: 10px;
  padding: 8px 16px;
}

.list_img {
  width: 80px;
  height: 50px;
  margin-right: 16px;
}

.list_img image {
  width: 80px;
  height: 50px;
}

.list_date {
  font-size: 24rpx;
  color: #aaaaaa;
}

.list_content {
  display: flex;
  flex-direction: row;
  margin-top: 20px;
}

json文件:

// 引入我们的组件
{
  "usingComponents": {
    "scroll-tab-view": "../../components/scroll-tab-view"
  },
  "navigationBarTitleText": "资讯"
}

组件就开发完成了,我们引入组件后要做的,只是在对应tab下面的 slot 中渲染我们自己的页面内容即可。

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

small_Axe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值