1.组件部分
<template>
<view :id="'tab'+id">
<scroll-view class="scroll-view_H" scroll-x="true" :scroll-left="scrollLeft">
<view :id="'tab-item'+index" :class="['scroll-view-item',current==index?'active':'']" :style="[tabItemStyle(index)]" v-for="(item,index) in list" @click="itemClick(item,index)">
<view class="flex-display flex-column a-i-c">
<image class="u-icon__img" :src="item.img" mode="widthFix"></image>
<view class="flex-display fs24 a-i-c j-c-c mar-top-20 fs-bold" :class="[current==index?'active-check':'']">{{item.name}}</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
function guid(len = 32, firstU = true, radix = null) {
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
let uuid = [];
radix = radix || chars.length;
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
} else {
let r;
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift();
return 'u' + uuid.join('');
} else {
return uuid.join('');
}
}
export default {
props: {
current: { //当前选中的tab
type: [Number, String],
default: 0
},
list: { //tab列表数据
type: Array,
default () {
return []
}
},
barStyle: { //自定义底部横条样式
type: Object,
default () {
return {}
}
},
itemStyle: { //自定义选中项tab的样式
type: Object,
default () {
return {}
}
},
activeColor: { //tab选中字体颜色
type: String,
default: 'rgb(41, 121, 255)'
},
inactiveColor: { //tab未选中字体颜色
type: String,
default: 'rgb(48, 49, 51)'
},
showBar: { //是否显示底部的横条
type: Boolean,
default: true
},
// 用于显示图片小图标时,图片的宽度
iconWidth: {
type: [String, Number],
default: ''
},
// 用于显示图片小图标时,图片的高度
iconHeight: {
type: [String, Number],
default: ''
},
// 字体大小单位rpx
fontSize: {
type: [String, Number],
default: 30
}
},
data() {
return {
scrollLeft: 0,
animation: '',
id: guid(10),
parentLeft: '',
componentWidth: '',
tabQueryInfo: []
};
},
watch: {
current() {
setTimeout(() => {
this.animation = ''
}, 500)
this.scrollByIndex()
}
},
computed: {
tabItemStyle() {
return (index) => {
let style = {
fontSize: this.addUnit(this.fontSize)
}
if (index == this.current) {
style.color = this.activeColor
Object.assign(style, this.itemStyle);
} else {
style.color = this.inactiveColor
}
return style
}
},
isImg() {
return (name) => {
if (typeof name == 'undefined' || !name) {
return false
}
return name.indexOf('/') !== -1;
}
},
imgStyle() {
let style = {};
// 如果设置width和height属性,则优先使用,否则使用size属性
style.width = this.iconWidth ? this.addUnit(this.width) : this.addUnit(this.fontSize);
style.height = this.iconHeight ? this.addUnit(this.height) : this.addUnit(this.fontSize);
return style;
}
},
mounted() {
this.init()
},
methods: {
// 设置一个init方法,方便多处调用
init() {
// 获取tabs组件的尺寸信息
uni.createSelectorQuery().
in(this)['select']('#tab' + this.id)
.boundingClientRect(tabRect => {
// tabs组件距离屏幕左边的宽度
this.parentLeft = tabRect.left;
// tabs组件的宽度
this.componentWidth = tabRect.width;
this.getTabRect();
})
.exec()
},
// 查询tab的布局信息
getTabRect() {
// 创建节点查询
let query = uni.createSelectorQuery().in(this);
// 历遍所有tab,这里是执行了查询,最终使用exec()会一次性返回查询的数组结果
for (let i = 0; i < this.list.length; i++) {
// 只要size和rect两个参数
// 只要size和rect两个参数
query.select(`#tab-item${i}`).fields({
size: true,
rect: true
});
}
// 执行查询,一次性获取多个结果
query.exec(
function(res) {
this.tabQueryInfo = res;
// 初始化滚动条和移动bar的位置
this.scrollByIndex();
}.bind(this)
);
},
// 滚动scroll-view,让活动的tab处于屏幕的中间位置
scrollByIndex() {
// 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
let tabInfo = this.tabQueryInfo[this.current];
if (!tabInfo) return;
// 活动tab的宽度
let tabWidth = tabInfo.width;
// 活动item的左边到tabs组件左边的距离,用item的left减去tabs的left
let offsetLeft = tabInfo.left - this.parentLeft;
// 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动
let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2;
this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
},
addUnit(value = 'auto', unit = 'rpx') {
value = String(value);
// 用uView内置验证规则中的number判断是否为数值
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value) ? `${value}${unit}` : value;
},
itemClick(item, index) {
this.$emit('click', index)
if (this.current == index) return
if (this.current > index) {
this.animation = 'animate__animated animate__slideInRight'
} else {
this.animation = 'animate__animated animate__slideInLeft'
}
this.$emit('change', index)
}
}
}
</script>
<style lang="scss">
.active-check{
width: 134upx;
height: 43upx;
font-family: PingFang SC;
color: #FFFFFF;
background: #FCAC39;
border-radius: 22px;
}
.scroll-view_H {
white-space: nowrap;
}
.scroll-view-item {
width: 150upx;
display: inline-block;
padding: 20rpx 0rpx;
transition: all 0.2s ease;
}
.active {
position: relative;
.bar {
position: absolute;
left: 0;
bottom: 0;
right: 0;
width: 40%;
margin: 0 auto;
height: 4rpx;
background: #007AFF;
animation-duration: 0.1s;
}
}
.u-icon__img {
width: 88upx;
height: 88upx;
}
</style>
引用组件
<template>
<view style="height:101vh;" class=" flex-display flex-column">
<nav-tab :list="list1" :current="current" :showBar="true" :fontSize="30" @click="tabClick" @change="change">
</nav-tab>
</view>
</view>
</template>
<script>
import navTab from '../../components/goods-public/nav-tabs.vue'
export default {
components: {
navTab,
},
data() {
return {
current: 0,
list1: [],
}
},
mounted() {
},
onLoad(option) {
this.navList()
},
onReachBottom(e) {
},
methods: {
navList() {
this.list1 = [{
name: '全部',
img: '../../static/img/goods/shop-all.png'
}, {
name: '苹果',
img: '../../static/img/goods/sg-pg.png'
}, {
name: '橘子',
img: '../../static/img/goods/sg-cz.png'
}, {
name: '石榴',
img: '../../static/img/goods/sg-sl.png'
},
{
name: '梨子',
img: '../../static/img/goods/sg-l.png'
},
]
},
tabClick(index) {
// console.log(index)
},
change(index) {
this.current = index
},
}
}
</script>
<style lang="scss" scoped>
page {
background-color: rgba(246, 244, 247, 1);
}
.content-c {
display: flex;
flex-direction: column;
flex: 1;
border-radius: 0rpx 0rpx 0rpx 0rpx;
}
.list-scroll-content {
height: 100%;
width: 100%;
border-radius: 38upx 38upx 0upx 0upx;
}
.swiper-item {
height: 100%;
width: 100%;
border-radius: 38upx 38upx 0upx 0upx;
}
.swiper-box {
height: 100%;
width: 100%;
border-radius: 38upx 38upx 0upx 0upx;
}
</style>