终于入坑小程序了,最近开发小程序,要实现一个tab切换效果,看到知乎热榜上的滑动切换tab效果还挺舒服的,刚好项目也需要这个功能,就尝试实现一下,首先需要一些基本知识点

循环渲染数据

小程序有类似vue的写法,但是还有区别的

data: {
  arr: [1,2,3]
}
<view wx:for="{{arr}}">{{item}}</view>

wx:for-item

默认情况下遍历的每一项用item来获取,如果要指定item的名称,可以用wx:for-item

<view wx:for="{{arr}}" wx:for-item="i">{{i}}</view>

wx:key

同vue一样,设置一个key属性可以优化列表渲染,微信也可以设置一个key,如果数组是每一项是唯一的字符串或者数字,可以使用wx:key="*this"

<view wx:for="{{arr}}" wx:key="*this">{{item}}</view>

如果是对象数组,一般使用唯一值的字段,例如id

data: {
    list: [
        {id: 1, name: 'list1'},
        {id: 2, name: 'list2'}
    ]
}
<view wx:for="{{list}}" wx:key="id">{{item.name}}</view>

进入正题,按照需求定义两个tab选项卡,如果tab选项过多可以使用scroll-view

<view class='flex-row tabbar'>
    <text id="tabitemRecharge" class='tabitem {{activeTabId=="tabitemRecharge"?"tabitem-active":""}}' bindtap='tabclick'>充值记录</text>
    <text id="tabitemConsume" class='tabitem {{activeTabId=="tabitemConsume"?"tabitem-active":""}}' bindtap='tabclick'>消费记录</text>
    <view class='tabindicator' animation="{{indicatorAnim}}"></view>
</view>

<swiper class='scrollview-content' current-item-id='tabitemRecharge' bindchange='tabChange'>
    <swiper-item item-id="tabitemRecharge">
      <scroll-view scroll-y="true" class="tab-content recharge-list">
        <view class='list-item recharge-item' wx:for="{{rechargeList}}" wx:for-item="rItem" wx:key="id">{{rItem.name}}</view>
      </scroll-view>
    </swiper-item>
    <swiper-item item-id="tabitemConsume">
      <scroll-view scroll-y="true" class="tab-content recharge-list">
        <view class='list-item consume-item' wx:for="{{consumeList}}" wx:for-item="rItem" wx:key="id">{{rItem.name}}</view>
      </scroll-view>
    </swiper-item>
</swiper>

tab页面的数据结构如下

data: {
  tabitemConsume: {},        // 存放tab选项卡的rect,键对应的内容swiper-item的item-id
  tabitemRecharge: {},        // 存放tab选项卡的rect,键对应的内容swiper-item的item-id
  activeTabId: null,
  rechargeList: [],
  consumeList: []
}

这里命名属性的方式是可以方便通过swiper-item的item-id来获取对应的tab选项卡的位置信息,例如

var rect = this.data[swiper-item-id];

wx.createSelectorQuery

要实现tab指示器的大小和位置动画,需要测量每个tab的rect(大小和位置)。微信没有提供丰富的dom操作api,但是提供了通过wx.createSelectorQuery来获取节点的信息

onReady() {
  var query = wx.createSelectorQuery().in(this),
    _this = this;

  _this.animation = wx.createAnimation()

  query.select('#tabitemConsume').boundingClientRect(function (rect) {
    _this.setData({
      tabitemConsume: rect
    });
  })


  query.select('#tabitemRecharge').boundingClientRect(function (rect) {
    _this.setData({
      tabitemRecharge: rect
    });
    _this.setActiveTab('tabitemRecharge');
  })

  query.exec();
}

wx.createAnimation

小程序的动画用wx.createAnimation来实现,给元素设置一个animation属性,通过export方法来给animation属性赋值

每次调用export方法都会清空上一次设置动画,举个例子
<view style="width: 20px; height: 4px; background: #333;" animation="{{anim}}"></view>

this.animation = wx.createAnimation({
  timingFunction: 'ease',
  delay: 0
});

// width变成200,x位置移动到100的动画
this.animation.width(200).translate(100, 0).step();

this.setData({
  anim: this.animation.export()
})  

效果如下

tab指示器的动画

通过上面的wx.createAnimation就可以实现指示器的切换效果了

// tab选项卡激活设置动画
setActiveTab(id) {
  var rect = this.data[id];
  if (rect) {
    this.animation.width(rect.width).translate(rect.left, 0);
    this.setData({
      activeTabId: id,        // 激活的swiper视图
      indicatorAnim: this.animation.step().export()
    })
  }
}

tab选项卡内容块

由于要实现滑动切换tab,用swiper来实现横向滑动,scroll-view来实现纵向滚动

swiper组件

swiper是滑块视图容器组件,可以用来实现幻灯片轮播效果等等。

<swiper class='scrollview-content' current-item-id='{{activeTabId}}' bindchange='tabChange'>
    <swiper-item item-id="tabitemRecharge"></swiper-item>
    <swiper-item item-id="tabitemConsume"></swiper-item>
</swiper>

swiper有一个current-item-id属性,通过匹配swiper-item的item-id属性来设置swiper指定的视图内容,在bindchange事件获取视图的id指定对应的swiper的视图即可

tabChange(e) {
  var id = e.detail.currentItemId;
  this.setActiveTab(id);
}

附上效果地址