jiuyiUniapp/jiuyi/components/public/jy-shop-navigation/index.vue

280 lines
7.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="tab" :style="{ background }">
<scroll-view class="scroll-view-content" scroll-x scroll-with-animation :show-scrollbar="false"
:scroll-left="scrollLeft">
<view class="scroll-item-view" :class="scroll ? '' : 'flex-row-left'" id="tab">
<view class="scroll-item" :class="[index == list.length - 1 ? 'no-right' : '']"
:style="{ color: currentIndex == index ? activeColor : '', height: tabHeight, 'line-height': tabHeight, 'font-size': fontSize, width: !scroll ? (100 / list.length) + '%' : '', 'margin-right': scroll ? marright : '', 'font-weight': currentIndex == index ? activeWeight : '' }"
:id="'tab-item-' + index" v-for="(item, index) in list" :key="index" @click="clickTab(item, index)">
{{ item[titalName] }}
</view>
<view class="scroll-tab-bar" :style="[tabBarStyle]"></view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => {
return []
}
},
// 当前活动tab的索引
current: {
type: [Number, String],
default: 0
},
titalName: {
type: String,
default: 'name'
},
// 菜单底部移动的bar的宽度单位px
barWidth: {
type: [String, Number],
default: 16
},
// 移动bar的高度
barHeight: {
type: [String, Number],
default: 2
},
barStyle: {
type: Object,
default() {
return {}
}
},
// 移动bar选中颜色
activeBarColor: {
type: String,
default: '#4990F9'
},
// 文字选中颜色
activeColor: {
type: String,
default: '#4990F9'
},
// 过渡动画时长, 单位ms
duration: {
type: [String, Number],
default: 0.5
},
// tab高度
height: {
type: Number,
default: 34
},
// 字体
fontSize: {
type: String,
default: '16px'
},
// 是否滚动,默认是,否则是不滚动,按个数等分宽度
scroll: {
type: Boolean,
default: true
},
// margin-right
marright: {
type: String,
default: '38px'
},
// font-weight
activeWeight: {
type: String,
default: 'normal'
},
background: {
type: String,
default: ''
}
},
watch: {
list(n, o) {
// list变动时重制内部索引否则可能导致超出数组边界的情况
if (n.length !== o.length) this.currentIndex = 0;
this.$nextTick(() => {
this.init();
});
},
current: {
immediate: true,
handler(nVal, oVal) {
// 视图更新后再执行移动操作
this.$nextTick(() => {
this.currentIndex = nVal;
this.scrollByIndex();
});
}
}
},
computed: {
tabBarStyle: function () {
let style = {
width: this.barWidth + 'px',
height: this.barHeight + 'px',
transform: `translateX(${this.scrollBarLeft}px)`,
'transition-duration': `${this.barFirstTimeMove ? 0 : this.duration}s`,
'border-radius': `${this.barHeight / 2}px`,
'background-color': this.activeBarColor
}
Object.assign(style, this.barStyle)
return style
},
tabHeight() {
return this.height + 'px'
}
},
data() {
return {
scrollLeft: 0,
componentWidth: 0, // tabs组件宽度单位为px
parentLeft: 0, // 父元素(tabs组件)到屏幕左边的距离
scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
tabItemRectInfo: [],
currentIndex: 0,
barFirstTimeMove: true
};
},
mounted() {
this.init()
},
methods: {
init() {
this.$nextTick(() => {
uni.createSelectorQuery().in(this).select(`#tab`).fields({
size: true,
rect: true
}, data => {
this.parentLeft = data.left;
// tabs组件的宽度
this.componentWidth = data.width;
}).exec();
this.getTabItemRect()
})
},
getTabItemRect() {
let query = uni.createSelectorQuery().in(this);
for (let i = 0; i < this.list.length; i++) {
// 只要size和rect两个参数
query.select(`#tab-item-${i}`).fields({
size: true,
rect: true
});
}
query.exec((data) => {
this.tabItemRectInfo = data
this.scrollByIndex()
})
},
scrollByIndex() {
let tabInfo = this.tabItemRectInfo[this.currentIndex]
if (!tabInfo) return
// 活动tab的宽度
let tabWidth = tabInfo.width
let offsetLeft = tabInfo.left - this.parentLeft
// 将活动的tabs-item移动到屏幕正中间实际上是对scroll-view的移动
let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2
this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
// 计算当前活跃item到组件左边的距离
let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft
this.scrollBarLeft = left - this.barWidth / 2;
if (this.barFirstTimeMove == true) {
setTimeout(() => {
this.barFirstTimeMove = false;
}, 100)
}
},
clickTab(item, index) {
// 点击当前活动tab不触发事件
if (index == this.currentIndex) return
this.currentIndex = index
this.$emit('tabItemClick', item, index)
this.scrollByIndex()
}
}
}
</script>
<style lang="scss" scoped>
.tab {
position: sticky;
top: 88rpx;
left: 0;
width: 100%;
z-index: 9;
height: 88rpx;
}
scroll-view {
box-sizing: border-box;
height: 100%;
}
::v-deep ::-webkit-scrollbar {
display: none
}
/* #ifndef APP-NVUE */
::-webkit-scrollbar,
::-webkit-scrollbar,
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
/* #ifdef H5 */
// 通过样式穿透隐藏H5下scroll-view下的滚动条
scroll-view ::v-deep ::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
.scroll-view-content {
white-space: nowrap;
width: 100%;
position: relative;
.scroll-item-view {
width: 100%;
white-space: nowrap;
position: relative;
margin-bottom: 20rpx;
}
.scroll-item {
position: relative;
padding: 0 20rpx;
display: inline-block;
text-align: center;
color: #8C8C8C;
}
.mar-r-38 {
margin-right: 76rpx;
}
.no-right {
margin: 0;
}
.scroll-tab-bar {
position: absolute;
bottom: 0;
z-index: 10;
}
}
</style>