终于入坑小程序了,最近开发小程序,要实现一个 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);}
附上效果地址