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;
        }
    }
}
相关推荐
the局外人4 分钟前
用 TRAE 把 Markdown文档 搬到微信公众号
前端
Autumn_ing5 分钟前
AI原型设计工具怎么选?可编辑原型与可运行前端适合什么场景
前端·小程序·产品经理·代码·codex·墨刀ai客户端·ai原型
夕阳_醉了37 分钟前
前端通用组件开发全指南|从 0 到 1 打造稳定可复用的业务组件
前端
Amos_Web42 分钟前
Rspack 源码解析(三十):SourceMapDevToolPlugin 如何生成并关联 source map
前端
console.log('npc')1 小时前
从0到1实现任务评价统计功能:React + TypeScript 实战指南(小白注释版)
前端·react.js·typescript
IT_陈寒1 小时前
为什么你应该学习React?
前端·人工智能·后端
ZoeLandia1 小时前
React04:事件处理与受控表单
开发语言·前端·react.js·typescript
亿元程序员1 小时前
用AI做了一款微信小游戏,却发现上传不了
前端
Ai-_Man1 小时前
我需要批量导出Poe左边栏所有对话,而不是一条具体的对话内容,你们AI导出鸭可以吗?
开发语言·前端·javascript·人工智能·小程序
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - Web Storage 本地存储 & 文件 API & 拖放 API
前端·html·html5