app一般會有這樣的導航欄卿啡,標題下的指示器(橫線)會隨著頁面的移動而移動嘱支。
最近要做一個小程序,也需要一個導航欄厕九,但翻了一些網(wǎng)上的demo蓖捶,都沒有類似的效果。android中可以通過TabLayout + ViewPager實現(xiàn)扁远,于是決定照貓畫虎俊鱼,寫一個小程序的自定義TabLayout。
思路是畅买,小程序提供了swiper組件可以實現(xiàn)左右滑動的效果并闲,那么只要再有一個view可以隨著swiper頁面移動就好了。
在項目中新建一個page谷羞,并把該page聲明為自定義控件帝火。
//json
{
"component":true
}
導航欄的標題應該是調(diào)用者用參數(shù)的方式告訴我們的,所以tablayout需要一個屬性接收標題湃缎。
//js
properties: {
titles:{
type:Array,
value: [],
}
}
tablayout布局分為兩部分犀填,標題部分和滑動頁面部分。
標題部分包括導航欄的標題和標題下的會移動的小橫線(這里稱為indicator)嗓违,標題綁定titles數(shù)據(jù)九巡,同時給indicator綁定一個動畫。
//wxml
<!-- title -->
<view id="head" style='display:flex; flex-direction:column; align-items:center;'>
<view>
<view style='height:55rpx; display:flex; flex-direction:row; text-align:center;'>
<view wx:for="{{titles}}" style='width:100rpx;'>
<text data-index='{{index}}' bindtap='clickTitle'>{{item}}</text>
</view>
</view>
<!-- indicator -->
<view style='width:100rpx; height:5rpx; background:lightgray;' animation="{{animation}}"></view>
</view>
</view>
滑動頁面部分靠瞎,相當于viewpager的角色,為了保證和標題一致求妹,同樣需要綁定titles數(shù)據(jù)乏盐,監(jiān)聽swiper滑動和滑動結(jié)束狀態(tài)。在<swiper-item>中添加<slot>標簽制恍,使調(diào)用者可以動態(tài)為swiper添加布局父能,可以用for循環(huán)的index作為<slot>的key。
//wxml
<!-- page -->
<swiper style='height:100%;' current="{{swiperIndex}}" bindtransition="swiperTrans" bindanimationfinish="swiperAnimationfinish" bindchange='swiperChange'>
<view wx:for="{{titles}}" wx:key="*this">
<swiper-item style='background:lightblue; display:flex; align-items:center; justify-content:center'>
<slot name="{{index}}"></slot>
</swiper-item>
</view>
</swiper>
因為可能有多個頁面净神,所以還要添加一個多slot支持
//js
options: {
multipleSlots: true // 在組件定義時的選項中啟用多slot支持
}
通過swiper中頁面的滑動距離計算indicator的需要移動多少何吝,除需要知道swiper的滑動距離外溉委,還需要知道每個swiper-item的寬度和indicator所在布局的寬度,通過兩個寬度的比例計算出indicator的位移爱榕。
這里swiper寬度為屏幕寬度瓣喊,可在頁面加載時獲取,indicator滑動范圍可自行定義黔酥。不過有個問題藻三,通過wx.getSystemInfoSync().screenWidth獲取的屏幕寬度單位是px,而給indecator賦值時單位是rpx跪者,單位不同不能用于計算棵帽,好在小程序默認的屏幕寬度為750rpx,可用于計算比例渣玲。
data:{
//屏幕寬度
screenWidth:"",
//微信規(guī)定的屏幕寬度750 rpx
wxScreenWidth:750,
//指示器滑動范圍寬度逗概,單位寬度
indicatorLayoutWidth:100
}
...
lifetimes: {
attached() {
// 獲取屏幕寬度
var that = this;
that.setData({ screenWidth: wx.getSystemInfoSync().screenWidth });
}
}
在swiper滑動時需要判斷滑動位置,在左右盡頭是不能繼續(xù)滑動的忘衍,所以在在swiper滑動完成后判斷一下狀態(tài)逾苫。
data:{
//標題 swiper-item 所在位置
titleIndex: 0,
//滑動狀態(tài):滑動到左邊(1)、滑動到右邊(2)淑履、其他位置(0)
scrollStatus:1
}
...
swiperAnimationfinish: function(e) {
var that = this;
that.setData({
titleIndex: e.detail.current
});
//計算指示器位移狀態(tài)
if (that.data.titleIndex == (that.data.titles.length-1)) {
// console.log("move to the right")
that.setData({ scrollStatus: 2 });
} else if (that.data.titleIndex == 0) {
// console.log("move to the left")
that.setData({ scrollStatus: 1 });
}else {
that.setData({ scrollStatus: 0 });
}
}
之后就可以通過監(jiān)聽swiper的滑動隶垮,讓indicator一起聯(lián)動了。
//tablayout.js
data:{
//indiator 動畫
animation: "",
}
...
methods:{
swiperTrans:function (e) {
var that = this;
// swipter位移 中間變量
var dx;
//e.detail.dx 頁面滑動距離秘噪,手指向左滑動距離為正狸吞,反之為負
if (e.detail.dx >= 0)
if (that.data.scrollStatus == 2)//頁面處于最右,且仍向左滑動時指煎,頁面位置保持在最右蹋偏。
dx = that.data.screenWidth * that.data.titleIndex;
else
dx = e.detail.dx + that.data.screenWidth * that.data.titleIndex;
else if (that.data.scrollStatus == 1) //頁面在初始位置,且仍向右滑動時至壤,頁面停留在初始位置威始。
dx = 0
else
dx = e.detail.dx + that.data.screenWidth * that.data.titleIndex;
//indicator與swipter之間移動比例
var scale = (that.data.indicatorLayoutWidth / that.data.wxScreenWidth).toFixed(2);//保留兩位小數(shù),否則indicator動畫有誤差
//indicator 位移
var ds = dx * scale;
this.transIndicator(ds);
},
//indicator 平移動畫
transIndicator(x) {
let option = {
duration: 100,
timingFunction: 'linear'
};
this.animation = wx.createAnimation(option);
this.animation.translateX(x).step();
this.setData({
animation: this.animation.export()
})
}
}
在點擊小標題時像街,swiper的滑動到對應頁面黎棠。
//js
clickTitle(e) {
//點擊切換卡片
var that = this;
that.setData({
//swiper 綁定了 current="{{swiperIndex}}"
swiperIndex: e.currentTarget.dataset.index
});
}
此時就可以在其他頁面進行調(diào)用了,調(diào)用的時候按模塊引入其他頁面就可以了镰绎。
<import src="../extend/extend.wxml"/>
<tablayout titles="{{titles}}">
<view slot="0"><template is="extend"></template></view>
<view slot="1"><template is="extend"></template></view>
<view slot="2"><template is="extend"></template></view>
<view slot="3"><template is="extend"></template></view>
</tablayout>
實現(xiàn)效果為