uni-app自定义tabs Posted on 2023-08-27 | In uni-app uni-app自定义tabs 123456789101112131415161718192021222324252627282930313233343536373839<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> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146export 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; } }}; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<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>