Vue 简单自定义标签
思路:
1、计算每个项离父级左侧宽 left
2、计算当前滑块的宽,绝对定位
3、下一个项的宽/2-滑块的宽/2+下一项离父级左侧的宽 left
4、使用定位left(性能较差一点) 或 translate 移动距离
<template><div class="tab-list"><!--菜单--><div class="tab-list-main"><div class="tab-list-main-item"v-for="(item,index) in list":key="item.component":class="{ 'tab-list-main-active': index === currentIndex }"@click="changeItem(index)">{{item.title}}</div><!--滑块--><div class="tab-list-main-slider" id="lineSlider"></div></div></div>
</template>
export default {name: "oil-tabs",props: {list: { // 菜单type: Array,default: () => []},duration: { // 滑动所需时间 单位mstype: Number,default: () => 300},activeColor: { // 选中项颜色type: String,default: () => ''}},data() {return {currentIndex: 0,// 当前的下标itemObj: {} // 菜单项的位置}},watch: {list: {handler(val) {this.itemObj = {}this.list = valif (val && val.length > 0) {this.$nextTick(() => {this.handleSlider()})}}, immediate: true}},methods: {/*** 点击tab项* */changeItem(index) {if (this.currentIndex === index) {return;}this.currentIndex = indexthis.handleSlider()let item = this.list[index]this.$emit('change', item)},/*** 计算菜单项的位置* */async handleItem() {let obj = {}this.itemObj = {}let itemList = document.getElementsByClassName('tab-list-main-item');if (itemList.length > 0) {for (let o = 0; o < itemList.length; o++) {obj[o] = itemList[o].getBoundingClientRect()obj[o].offsetLeft = itemList[o].offsetLeft}this.itemObj = obj}},/*** 计算滑块当前位置* */async handleSlider() {if (Object.keys(this.itemObj).length === 0) {await this.handleItem()}let currentTab = this.itemObj[this.currentIndex]if (!currentTab) {return;}// 滑块let lineSlider = document.getElementById('lineSlider');if (!lineSlider) {return;}// 滑块宽let { width: currentWidth } = lineSlider.getBoundingClientRect();// 下一个菜单的宽及距父级左侧leftlet { offsetLeft, width } = currentTab;// 滑块位置// lineSlider.style.left = width / 2 - currentWidth / 2 + offsetLeft + 'px';lineSlider.style.transform = `translateX(${width / 2 - currentWidth / 2 + offsetLeft}px)`;}}
}
.tab-list {width: 100%;min-height: 28px;&-main {display: flex;flex-wrap: nowrap;white-space: nowrap;overflow: auto hidden;color: #ccc;position: relative;padding-bottom: 5px;&-item {margin-right: 24px;font-size: 14px;font-weight: 500;&:last-child {margin-right: 0;}}&-active {color: blue;font-weight: 800;}&-slider {margin-top: 2px;height: 3px;width: 28px;border-radius: 2px;background-color: blue;position: absolute;bottom: 0;left: 0;transform: translateX(0);will-change: left;transition: all .1s linear;}}
}