让van-tabs组件的下划线宽度比文字内容略宽一些

1,078 阅读1分钟

vue3,移动端项目使用 vant 框架

UI 想要的效果是 van-tabs 组件的下划线比内容略长,可组件本身只能通过 line-width 设置

a.png

静态内容还好说,可如图例,标题的宽度是随着内容动态变化的,因此我二次封装了 LocalTabs 组件,使用的时候只需要将 van-tabs 替换为 LocalTabs 就可以了

<script setup>
let tabsRef = ref()

const dealLineBar = () => {
    const tabDomList = tabsRef.value.$el.querySelectorAll('.van-tab')

    const activeTabWidth = tabDomList[tabsRef.value.active].querySelector('span').clientWidth
    const lineDom = tabsRef.value.$el.querySelector('.van-tabs__line')

    lineDom.style.width = activeTabWidth + 4 + 'px'
}
const onChange = () => {
    setTimeout(() => {
        dealLineBar()
    }, 20)
}
onMounted(() => {
    setTimeout(() => {
        dealLineBar()
    }, 100)
    tabsRef.value.$el.querySelectorAll('.van-tab span').forEach((dom, index) => {
        const observer = new MutationObserver(() => {
            if (tabsRef.value.active == index) {
                setTimeout(() => {
                    dealLineBar()
                }, 0)
            }
        })
        observer.observe(dom, {
            characterData: true,
            subtree: true,
        })
    })
})
</script>

<template>
    <div :class="['local-tabs-wrap']">
        <van-tabs ref="tabsRef" v-bind="$attrs" @change="onChange">
            <slot></slot>
            <template #nav-right>
                <slot name="nav-right"> </slot>
            </template>
        </van-tabs>
    </div>
</template>

<style lang="scss" scoped>
.local-tabs-wrap {
}
</style>

vue3 中使用 v-bind="$attrs"就可以绑定外面的属性和方法,这样就和使用 van-tabs 的传参一致了,onChange 是点击的时候自动计算,onMounted 处理已经选中 tab,但是数据发生变化导致标题长度发生变化的情况

        observer.observe(dom, {
            characterData: true,
            subtree: true,
        })

这段代码我搞了好久,本质上文字是文本节点,也属于后代节点,characterData 表示监听文本变化,subtree 表示将监听扩展到子节点,因此二者必须同时具备才能有效果。