Vue 简单自定义标签

Vue 简单自定义标签

思路:

1、计算每个项离父级左侧宽 left

2、计算当前滑块的宽,绝对定位

3、下一个项的宽/2-滑块的宽/2+下一项离父级左侧的宽 left

4、使用定位left(性能较差一点) 或 translate 移动距离

html 复制代码
<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>
js 复制代码
export default {
    name: "oil-tabs",
	props: {
	    list: { // 菜单
	        type: Array,
	        default: () => []
	    },
	    duration: { // 滑动所需时间 单位ms
	        type: Number,
	        default: () => 300
	    },
	    activeColor: { // 选中项颜色
	        type: String,
	        default: () => ''
	    }
	},
	data() {
	    return {
	        currentIndex: 0,// 当前的下标
	        itemObj: {} // 菜单项的位置
	    }
	},
	watch: {
	    list: {
	        handler(val) {
	            this.itemObj = {}
	            this.list = val
	            if (val && val.length > 0) {
	                this.$nextTick(() => {
	                    this.handleSlider()
	                })
	            }
	        }, immediate: true
	    }
	},
	methods: {
	    /**
	     * 点击tab项
	     * */
	    changeItem(index) {
	        if (this.currentIndex === index) {
	            return;
	        }
	        this.currentIndex = index
	        this.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();
			// 下一个菜单的宽及距父级左侧left
			let { offsetLeft, width } = currentTab;
			// 滑块位置
			// lineSlider.style.left = width / 2 - currentWidth / 2 + offsetLeft + 'px';
			lineSlider.style.transform = `translateX(${width / 2 - currentWidth / 2 + offsetLeft}px)`;
	    }
	}
}
css 复制代码
.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;
        }
    }
}
相关推荐
万少1 小时前
DeepSeek 昨晚刚开源了 Harness:附万少的2 万字保姆级教程
前端·后端·架构
程序员黑豆3 小时前
Java字符串详解
java·前端·ai编程
无我Code4 小时前
开发者-2026年中总结
前端·面试·程序员
To_OC4 小时前
LC 560 和为 K 的子数组:前缀和配哈希表,这对组合我是真的服了
javascript·算法·程序员
浮生望5 小时前
React 受控与非受控组件:从表单状态管理到实时校验的完整实践
前端
明朝百晓生6 小时前
Deep RL learning[2026/8]
开发语言·javascript·人工智能
糖墨夕6 小时前
第二章:AI Agent 到底是个啥?—— 用生活场景把概念讲明白
前端
Csvn6 小时前
⚡ content-visibility: auto —— 长页面渲染提速的隐藏神器,白捡的性能优化
前端
用户938515635076 小时前
ESLint 代码规范完全指南——从 AST 原理到 flat config 逐行解析
javascript·后端·代码规范
用户938515635076 小时前
深入理解 Next.js:从 SPA 的 SEO 问题到约定式路由与 SSR 原理
前端·后端·全栈