**【组件】vue实现自定义tab栏下划线滚动过渡效果以及点击当前tab自动居中**
vue实现自定义tab栏下划线滚动过渡效果以及点击当前tab自动居中小程序、前端交流群:609690978先上效果图组件代码:<template><div v-if="list.length > 0" class="tabBlock"><div @scroll="scroll" class="tabContent"><div ref="tabCont
·
vue实现自定义tab栏下划线滚动过渡效果以及点击当前tab自动居中
小程序、前端交流群:609690978
先上效果图
组件代码:
<template>
<div v-if="list.length > 0" class="tabBlock">
<div @scroll="scroll" class="tabContent">
<div ref="tabContentScroll" class="tabContentScroll">
<div id="tab_list" class="tab">
<div
ref="tab_item"
v-for="(item, index) in list"
:key="index"
:class="['tab__item', { 'tab__item--active': currentIndex === index }]"
:style="{ color: currentIndex === index ? `${itemColor}` : '' }"
@click="select(item, index)"
>
<div class="tab__item-title">
<slot :title="item" name="title"></slot>
</div>
<div v-if="!showTitleSlot" class="tab__item-title">{{ item.title }}</div>
</div>
</div>
<div :style="{ background: lineColor, width: lineStyle.width, transform: lineStyle.transform, transitionDuration: lineStyle.transitionDuration }" class="tab__line"></div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: [Number, String],
default: null
},
list: {
type: Array,
default: () => {
return []
}
},
itemColor: {
type: Array,
default: () => {
return []
}
},
lineColor: {
type: Array,
default: () => {
return []
}
},
lineAnimated: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
currentIndex: 0,
lineStyle: {},
scrollLeft: 0,
tabsScrollLeft: 0,
duration: 0.3
}
},
computed: {
showTitleSlot() {
return this.$scopedSlots.title
}
},
watch: {
list() {
this.setTabList()
},
value() {
this.currentIndex = this.value
this.setTabList()
},
scrollLeft(val) {
console.log(val)
}
},
mounted() {
this.currentIndex = this.value
this.setTabList()
if (!this.lineAnimated) {
this.duration = 0
}
// console.log(this.$scopedSlots)
},
methods: {
select(item, index) {
this.$emit('input', index)
this.scroll()
},
setTabList() {
this.$nextTick(() => {
if (this.list.length > 0) {
this.setLine()
this.scrollIntodiv()
}
})
},
setLine() {
let lineWidth = 0
let lineLeft = 0
this.getElementData(`#tab_item`, data => {
const el = data[this.currentIndex]
lineWidth = el.width
// lineLeft = el.width * (this.currentIndex + 0.5) // 此种只能针对每个item长度一致的
lineLeft = el.width / 2 + -data[0].left + el.left
this.lineStyle = {
width: `${lineWidth}px`,
transform: `translateX(${lineLeft}px) translateX(-50%)`,
transitionDuration: `${this.duration}s`
}
})
},
// item滚动计算
scrollIntodiv() {
this.getElementData('#tab_list', data => {
// const list = data[0]
this.getElementData('#tab_item', data => {
const item = data[this.currentIndex]
// 滚动父级元素宽度
// const tabContentWidth = this.$refs.tabContentScroll.scrollWidth
// 父级元素可视区域宽度
const screenWdith = this.$refs.tabContentScroll.offsetWidth
// item的宽度
const itemWidth = item.width
// item距离最左侧的距离
const itemOffsetLeft = item.left
// item中间值 = (父级可视区域宽度 - item的宽度) / 2
const itemCenter = (screenWdith - itemWidth) / 2
// item目标值 = 当前item左移值 - 中间值
const itemTarget = itemOffsetLeft - itemCenter
// 屏幕宽度
const winWidth = window.innerWidth
console.log(winWidth)
// 目标值小于0时,滚动值设置为0
if (itemTarget < 0) {
this.$refs.tabContentScroll.scrollLeft = 0
return
}
// 目标值小于屏幕宽减去父级可是区域宽时,滚动值设置为 父级可是区域宽-屏幕宽+元素宽
if (itemTarget < winWidth - screenWdith) {
this.$refs.tabContentScroll.scrollLeft = screenWdith - winWidth + itemWidth
return
}
this.$refs.tabContentScroll.scrollLeft = itemTarget
})
})
},
getElementData(el, callback) {
const domArr = this.$refs.tab_item
const arr = []
domArr.forEach(e => {
arr.push({ id: 'tab_item', left: e.offsetLeft, width: e.offsetWidth })
})
callback(arr)
},
scroll() {
this.scrollLeft = this.$refs.tabContentScroll.scrollLeft
}
}
}
</script>
<style lang="scss">
.tabBlock {
position: relative;
margin-bottom: 30px;
.tabContent {
.tabContentScroll {
margin: 0 15px;
overflow: auto;
}
.tabContentScroll::-webkit-scrollbar-thumb {
display: none;
}
}
.tabContent::before {
content: '';
height: 1px;
width: 100%;
position: absolute;
bottom: 1px;
left: 0;
background: #cccccc;
}
.tab {
position: relative;
display: flex;
font-size: 28px;
padding-bottom: 15px;
white-space: nowrap;
&__item {
// flex: 1;
// text-align: center;
line-height: 90px;
color: #999999;
margin-right: 40px;
position: relative;
&--active {
color: black;
}
&-title {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.tab__line {
display: block;
height: 1px;
position: absolute;
bottom: 0px;
left: 0;
z-index: 1;
border-radius: 3px;
position: relative;
background: black;
}
}
</style>
父级页面调用方法
1.自定义标题样式,如上述效果图
<filters-tab :list="filtersList" v-model="active">
<template v-slot:title="{ title }">
<div class="tab-block">
<div class="tab-block-title">{{ title.title }}</div>
<div v-if="title.info != 0" class="tab-block-info">
<p class="tab-block-info-text">{{ title.info }}</p>
</div>
</div>
</template>
</filters-tab>
data() {
return {
filtersList: [
{ title: '所有订单1', info: 0 },
{ title: '待支付1', info: 0 },
{ title: '待发货1', info: 0 },
{ title: '已发货1', info: 0 },
{ title: '售后中1', info: 0 },
{ title: '已完成1', info: 0 },
{ title: '所有订单2', info: 0 },
{ title: '待支付2', info: 0 },
{ title: '待发货2', info: 0 },
{ title: '已发货2', info: 0 },
{ title: '售后中2', info: 0 },
{ title: '已完成2', info: 0 }
],
active: 0
}
},
.tabContent {
margin-top: 20px;
.tab-block {
&-info {
width: 16px;
height: 16px;
border-radius: 50%;
background: #ccc;
position: absolute;
right: -18px;
top: 40%;
transform: translate(0, -50%);
&-text {
font-size: 11px;
font-weight: 600;
color: #999999;
line-height: 16px;
text-align: center;
}
}
}
}
2.直接调用
<filters-tab :list="list" v-model="active" itemColor="#03648f" lineColor="#03648f"></filters-tab>
<div>{{active}}</div>
小程序、前端交流群:609690978
更多推荐
已为社区贡献3条内容
所有评论(0)