uni-app自定义tabs

uni-app自定义tabs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<view class="tab-container">
<view class="tab-box">
<scroll-view
id="_scroll"
scroll-x
class="scroll-view"
scroll-with-animation
:scroll-left="scrollLeft"
>
<view class="scroll-content">
<view class="tab-item-box">
<block v-for="(item, index) in tabList" :key="index">
<view
class="tab-item"
:id="'_tab_' + index"
:class="{ 'tab-item-active': activeIndex === index }"
@click="tabClick(index)"
:style="{
color:
activeIndex === index ? defaultConfig.activeTextColor : defaultConfig.textColor
}"
>{{ item.label || item }}</view
>
</block>
</view>
<!-- 滑块 -->
<view
class="underLine"
:style="{
transform: 'translateX(' + slider.left + 'px)',
width: defaultConfig.underLineWidth + 'px',
height: defaultConfig.underLineHeight + 'px',
backgroundColor: defaultConfig.underLineColor
}"
/>
</view>
</scroll-view>
</view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
export default {
name: 'my-tabs',
props: {
// 父组件传入的 tabs 数据
tabData: {
type: Array,
default: () => []
},
// 默认激活项
defaultIndex: {
type: Number,
default: 0
},
// 配置对象
config: {
type: Object,
default: () => {
return {};
}
}
},
data: () => {
return {
// 内部维护的数据对象,为每个 item 单独额外维护一个 slider 的滑块对象
tabList: [],
// 当前激活项的 index
activeIndex: -1,
// 滑块对象
slider: {
// 距离左侧的距离
left: 0
},
// scrollView 的横向滚动条位置
scrollLeft: 0,
// 默认配置
defaultConfig: {
// 默认的字体颜色
textColor: '#333333',
// 高亮字体颜色
activeTextColor: '#f94d2a',
// 下划线宽度 px
underLineWidth: 24,
// 下划线高度 px
underLineHeight: 2,
// 下划线颜色
underLineColor: '#f94d2a'
}
};
},
// 侦听器
watch: {
// 侦听数据的变化
tabData: {
handler(val) {
this.tabList = val;
setTimeout(() => {
this.updateTabWidth();
}, 0);
},
// 该回调将会在侦听开始之后被立即调用
immediate: true
},
// 监听激活项目的变化
defaultIndex: {
handler(val) {
this.activeIndex = val;
// 定义滑块的位置
this.tabToIndex();
},
// 该回调将会在侦听开始之后被立即调用
immediate: true
},
// 监听 config
config: {
handler(val) {
this.defaultConfig = { ...this.defaultConfig, ...val };
},
// 该回调将会在侦听开始之后被立即调用
immediate: true
}
},
methods: {
/**
* 更新 tab item 的宽度
*/
updateTabWidth() {
/**
* 为 tabList 的每个 item 单独额外维护一个 slider 的滑块对象
*/
let data = this.tabList;
if (data.length == 0) return false;

// 获取 dom 的固定写法
const query = uni.createSelectorQuery().in(this);
// 循环数据源
data.forEach((item, index) => {
// 获取 dom 的固定写法
query
.select('#_tab_' + index)
.boundingClientRect((res) => {
// 为数据对象中每一个 item 都维护一个 _slider(滑动条) 对象
item._slider = {
// 当前的 tab 距离左侧的距离
left: res.left + (res.width - this.defaultConfig.underLineWidth) / 2
};
// 运算完成之后,执行一次 【滑块】位置运算
if (data.length - 1 === index) {
this.tabToIndex();
}
})
.exec();
});
},
/**
* tab 的点击事件处理
*/
tabClick(index) {
this.activeIndex = index;
// 定义滑块的位置
this.tabToIndex();
// 发送通知
this.$emit('tabClick', index);
},
/**
* 根据当前的 activeIndex 下标,计算 【滑块】 滚动位置
*/
tabToIndex() {
if (this.tabList.length === 0) return;
// 获取当前的 activeIndex
const activeIndex = this.activeIndex;
// 滑块的宽度
const underLineWidth = this.defaultConfig.underLineWidth;
// 配置 滚动条 的数据
this.slider = {
// TODO:left 如何定义呢?
// 1. 维护一个单独的数据对象 `tabList`
// 2. 在 `tabList` 的 `item` 中为一个 `_slider` 属性
// 3. 该属性保存了 【当前 `item` 下 的滑块位置】
// 3.1. 计算公式:`滑块左侧位置 = item.left + (item.width - slider.width) / 2`
left: this.tabList[activeIndex]._slider.left
};
// 为 scrollView 设置滚动位置
this.scrollLeft = this.activeIndex * this.defaultConfig.underLineWidth;
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<style lang="scss" scoped>
.tab-container {
font-size: $uni-font-size-base;
height: 45px;
line-height: 45px;
background-color: $uni-bg-color;
.tab-box {
width: 100%;
height: 45px;
display: flex;
position: relative;
.scroll-view {
white-space: nowrap;
width: 100%;
height: 100%;
box-sizing: border-box;
.scroll-content {
width: 100%;
height: 100%;
position: relative;

.tab-item-box {
height: 100%;
.tab-item {
height: 100%;
display: inline-block;
text-align: center;
padding: 0 15px;
position: relative;
text-align: center;
color: $uni-text-color;

&-active {
color: $uni-text-color-hot;
}
}
}
.underLine {
height: 2px;
width: 25px;
background-color: #f01414;
border-radius: 3px;
transition: 0.5s;
position: absolute;
bottom: 0;
}
}
}
}

/* #ifdef H5 */
/deep/.uni-scroll-view::-webkit-scrollbar {
display: none;
}

/deep/.uni-scroll-view {
scrollbar-width: none;
}
/* #endif */
}
</style>