滑动的tabbar
<h2>滑动的tabbar,带有动画的下划线</h2>
<p>效果如图:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/7e43540fd22c80802d51ca616e8ac3ce?showdoc=.jpg" alt="" /></p>
<h3>用法如下:</h3>
<pre><code> <scroll-tab
ref="scrollTab"
showIndicatorLine="true"
indicatorWidth="35"
:tabIndex="tabIndex"
:tabList="tabList"
@changeTab="changeTab" /> </code></pre>
<h4></h4>
<p>1、初始化或者实时刷新下划线,需要每次改变tab索引时,调用 updateIndicatorLine 方法(注意:不要在生命周期马上调用)。
<strong> this.$refs.scrollTab.updateIndicatorLine(tabIndex)</strong></p>
<p>2、@changeTab是tab item点击的事件</p>
<pre><code class="language-javascript">changeTab(tabIndex, first = false) {
this.tabIndex = tabIndex;
this.$refs.scrollTab.updateIndicatorLine(tabIndex);
// 每点击一次tab就刷新下页面
this.refreshList();
},</code></pre>
<h3>scroll-tab属性如下:</h3>
<pre><code class="language-javascript">showIndicatorLine: {
// 是否显示下划线
type: Boolean,
default: true
},
indicatorWidth: {
// 下划线长度
type: Number,
default: 36
},
tabIndex: {
// 当前的索引
type: Number,
default: 0
},
tabList: {
// tab 字符串数组
type: Array,
default: []
},
selectColor: {
type: String,
default: '#108ee9'
}</code></pre>
<h2>scroll-tab的高度是44px,</h2>
<p>源码如下:</p>
<pre><code class="language-javascript"><template>
<scroll-view ref="tabbar" id="tabbar" class="tab-bar" :scroll="true" :scroll-x="true" show-scrollbar="false">
<view>
<view class="tab-container" ref="tabContainer">
<view v-for="(tab, index) in tabList" :key="index" :id="'tab' + index" :ref="'tab' + index" class="tab-item" @click="changeTab(index)">
<text class="tab-item-text" :style="{ color: tabIndex === index ? selectColor : '#666666' }">{{ tab }}</text>
</view>
</view>
<view class="scroll-view-underline-container">
<view
ref="underline"
class="scroll-view-underline scroll-view-animation"
:class="{ 'indicator-line': showBottomLine }"
:style="{ left: indicatorLineLeft + 'px', backgroundColor: selectColor }"
/>
</view>
</view>
</scroll-view>
</template>
<script>
// #ifdef APP-PLUS
const dom = weex.requireModule('dom');
// #endif
/**
* 用法:
* <scroll-tab ref="scrollTab" showIndicatorLine="true" indicatorWidth="35" :tabIndex="tabIndex" :tabList="tabList" @changeTab="changeTab" />
*
* 1、初始化或者实时刷新下划线,需要每次改变tab索引时,调用 updateIndicatorLine 方法(注意:不要在生命周期马上调用)。
*
* 注意,结合swiper使用的时候,swiper的@change、@transition、@animationfinish的方法会多次重复调用,导致点击事件不灵敏的问题
* 可根据tabIndex 过滤重复事件,例如:
* if (this.tabIndex === tabIndex) {
return;
}
*
*/
export default {
props: {
showIndicatorLine: {
// 是否显示下划线
type: Boolean,
default: true
},
indicatorWidth: {
// 下划线长度
type: Number,
default: 36
},
tabIndex: {
// 当前的索引
type: Number,
default: 0
},
tabList: {
// tab 字符串数组
type: Array,
default: []
},
selectColor: {
type: String,
default: '#108ee9'
}
},
data() {
return {
screenWidth: 0, // 屏幕的宽度,单位是px
indicatorLineLeft: 0, // 下划线动态的左边距
tabListSize: {}, // tab数组的布局信息
showBottomLine: false, //显示底部下划线
parentLeft: 0 //tab组件的父组件距离屏幕的左边距离
};
},
mounted() {
this.getScreenInfo();
},
methods: {
getScreenInfo() {
const res = uni.getSystemInfoSync();
if (res) {
this.screenWidth = res.screenWidth;
}
},
scrollToOffset(elRef, offset) {
// #ifdef APP-NVUE
if (dom) {
// console.log('==== scrollToOffset ===== offset:' + offset);
dom.scrollToElement(elRef, {
offset: -offset
});
}
// #endif
},
queryTabElement(tabIndex) {
dom.getComponentRect(this.$refs['tabContainer'], res => {
// console.log('======= getComponentRect =====' + JSON.stringify(res));
if (res.size) {
this.parentLeft = res.size.left;
dom.getComponentRect(this.$refs['tab' + tabIndex][0], chiledRes => {
// console.log('======= getComponentRect tabList =====index:' + tabIndex + ' res: ' + JSON.stringify(res));
this.tabListSize[tabIndex] = chiledRes.size;
this.indicatorLineLeft = chiledRes.size.left + (chiledRes.size.width - this.indicatorWidth) / 2 + Math.abs(this.parentLeft);
// 测试要求下划线一开始不要出现在“全部”位置,所以做了延时处理
this.$nextTick(() => {
setTimeout(() => {
this.showBottomLine = this.showIndicatorLine;
}, 100);
});
});
}
});
},
updateIndicatorLine(tabIndex) {
this.queryTabElement(tabIndex);
// 操作下划线、tab的元素滚动(元素滚动影响了点击事件,暂时注释该功能 )
// const tabElSize = this.tabListSize[tabIndex];
// if (!tabElSize) {
// return;
// }
// if (this.$refs['tab' + tabIndex][0]) {
// // tab元素滚动
// if (tabElSize.left + tabElSize.width > this.screenWidth) {
// const offset = tabElSize.left + tabElSize.width - this.screenWidth;
// this.scrollToOffset(this.$refs['tab' + tabIndex][0], offset);
// } else {
// this.scrollToOffset(this.$refs['tab' + tabIndex][0], tabElSize.left);
// }
// }
},
changeTab(index) {
this.$emit('changeTab', index);
}
}
};
</script>
<style>
.page-view {
flex: 1;
background-color: #f9f9fa;
flex-direction: column;
}
.tab-bar {
width: 750upx;
height: 46px;
margin-bottom: 10px;
flex-direction: row;
flex-wrap: nowrap;
}
.tab-container {
background-color: #ffffff;
height: 44px;
flex-direction: row;
flex-wrap: nowrap;
padding-left: 5px;
padding-right: 5px;
}
.tab-item {
justify-content: center;
padding-left: 10px;
padding-right: 10px;
height: 44px;
}
.tab-item-text {
color: #666666;
font-size: 16px;
text-align: center;
}
.tab-item-select-text {
color: #108ee9;
}
.scroll-view-underline-container {
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
height: 2px;
}
.scroll-view-underline {
position: absolute;
bottom: 0px;
height: 2px;
width: 0px;
background-color: #108ee9;
border-radius: 2px;
}
.scroll-view-animation {
transition-duration: 0.2s;
transition-property: left;
}
.indicator-line {
width: 36px;
}
</style>
</code></pre>