app_evaluating/components/QS-tabs/QS-tabs.vue
2023-02-07 19:27:48 +08:00

419 lines
10 KiB
Vue

<template>
<view class="QS-tabs" :style="{
'z-index': zIndex,
'font-size': getFontSize + 'rpx',
'background-color': getBgColor,
'transition-duration': getDuration + 's'
}">
<scroll-view show-scrollbar="false" scroll-x class="QS-tabs-scroll" :scroll-left="left" scroll-with-animation :style="{
'z-index': (Number(zIndex) + 1)
}">
<view class="QS-tabs-scroll-item " :style="{
'height': getHeight + 'rpx',
'line-height': getHeight + 'rpx',
'width': getWidth + 'rpx',
'color': index===getCurrent?getActiveColor:getDefaultColor,
'transition-duration': getDuration + 's',
'z-index': (Number(zIndex) + 2)
}"
v-for="(item, index) in getTabs" :key="index" @tap="emit(index)">
<!-- line1 -->
<view v-if="animationMode==='line1'" class="boxStyle" :style="getDurationStyle +( index===getCurrent?getActiveStyle:getDefaultStyle)"></view>
{{item.name || item}}
</view>
<!-- itemBackground -->
<view v-if="hasItemBackground" class="itemBackgroundBox" :style="getItemBackgroundBoxStyle">
<view class="itemBackground" :style="getItemBackgroundStyle" />
</view>
<!-- line2 -->
<view v-if="animationMode==='line2'" class="boxStyle2" :style="getLinezIndex + getDurationStyle + getBoxStyle2" />
<view v-if="animationMode==='line3'" class="boxStyle2" :style="getLinezIndex + getBoxStyle3" />
</scroll-view>
</view>
</template>
<script>
const {
windowWidth
} = uni.getSystemInfoSync();
export default {
props: {
tabs: { //需循环的标签列表
type: Array,
default () {
return [];
}
},
current: { //当前所在滑块的 index
type: Number,
default: 0
},
height: { //QS-tabs的高度和行高
type: [String, Number],
default: 80
},
width: { //单个tab的宽度
type: [String, Number],
default: 250
},
fontSize: { //字体大小
type: [String, Number],
default: 30
},
duration: { //过渡动画时长, 单位 s
type: [String, Number],
default: .5
},
activeColor: { //选中项的主题颜色
type: String,
default: '#33cc33'
},
defaultColor: { //未选中项的主题颜色
type: String,
default: '#888'
},
animationLineWidth: { //动画线条的宽度
type: [String, Number],
default: 40
},
line2Style: { //line2线条的样式
type: String,
default: 'height: 3rpx;border-radius: 4rpx;'
},
animationMode: { //动画类型
type: String,
default: 'line1'
},
autoCenter: { //是否自动滚动至中心目标
type: Boolean,
default: true
},
autoCenterMode: { //滚动至中心目标类型
type: String,
default: 'component'
},
activeStyle: { //line1模式选中项的样式
type: String,
default: 'bottom:0;left:50%;margin-left: -20%;height: 5rpx;border-radius:4rpx;'
},
defaultStyle: { //line1模式未选中项的样式
type: String,
default: 'bottom:0;left:50%;margin-left: -20%;height: 4rpx;border-radius:4rpx;'
},
backgroundColor: { //统一背景颜色
type: String,
default: 'rgba(255,255,255,0)'
},
hasItemBackground: { //是否开启背景追光
type: Boolean,
default: false
},
itemBackgroundColor: { //统一追光背景颜色
type: String,
default: 'rgba(255,255,255,0)'
},
itemBackgroundStyle: { //追光样式
type: String,
default: ''
},
zIndex: { //css的z-index属性值
type: [String, Number],
default: 99
},
swiperWidth: {
type: [String, Number],
default: 750
}
},
computed: {
getCurrent() {
if (this.current > (this.getTabs.length - 1)) {
return (this.getTabs.length - 1)
}
return this.current;
},
getTabs() {
return this.tabs;
},
getHeight() {
return this.height;
},
getWidth() {
return Number(this.width);
},
getFontSize() {
return this.fontSize;
},
getDuration() {
return this.duration;
},
getBgColor() {
const defaultColor = this.backgroundColor || 'rgba(255,255,255,0)';
if (this.getTabs[this.getCurrent] instanceof Object) {
return this.getTabs[this.getCurrent].backgroundColor || defaultColor;
} else {
return defaultColor;
}
},
getItemBackgroundColor() {
const defaultColor = this.itemBackgroundColor || 'rgba(255,255,255,0)';
if (this.getTabs[this.getCurrent] instanceof Object) {
return this.getTabs[this.getCurrent].itemBackgroundColor || defaultColor;
} else {
return defaultColor;
}
},
getDurationStyle() {
return `transition-duration: ${this.getDuration}s;`
},
getActiveColor() {
let activeColor;
if (this.getTabs[this.getCurrent] instanceof Object) {
if (this.getTabs[this.getCurrent].activeColor) {
activeColor = this.getTabs[this.getCurrent].activeColor;
} else {
activeColor = this.activeColor;
}
} else {
activeColor = this.activeColor;
}
return activeColor;
},
getDefaultColor() {
let defaultColor;
if (this.getTabs[this.getCurrent] instanceof Object) {
if (this.getTabs[this.getCurrent].defaultColor) {
defaultColor = this.getTabs[this.getCurrent].defaultColor;
} else {
defaultColor = this.defaultColor;
}
} else {
defaultColor = this.defaultColor;
}
return defaultColor;
},
getActiveStyle() {
return `width:${this.animationLineWidth}%;background-color:${this.getActiveColor};${this.activeStyle}`;
},
getDefaultStyle() {
return `width:0;background-color:${this.getActiveColor};${this.defaultStyle}`;
},
getLinezIndexNum() {
return Number(this.zIndex) + 2;
},
getLinezIndex() {
return `z-index: ${this.getLinezIndexNum};`;
},
getLw() {
const lineWidth = Number(this.line2Width);
const lw = Number(this.width) * (lineWidth / 100);
return lw;
},
getBoxStyle2() {
const lw = this.getLw;
const x = Number(this.width) * (this.getCurrent + 1) - Number(this.width) / 2 - lw / 2;
return `transform:translate(${x}rpx, -100%);width:${this.getLw}rpx;background-color: ${this.getActiveColor};${this.line2Style}`;
},
getItemBackgroundBoxStyle() {
return `height: ${this.getHeight}rpx;
width: ${this.getWidth}rpx;
z-index: ${(Number(this.zIndex) + 1)};
transition-duration: ${this.getDuration}s;
transform: translateX(${Number(this.width)*(this.getCurrent)}rpx);`
},
getItemBackgroundStyle() {
return `transition-duration: ${this.getDuration}s;
background-color: ${this.getItemBackgroundColor};
box-shadow: 0 0 5rpx 5rpx ${this.getItemBackgroundColor};
${this.itemBackgroundStyle};`
},
getFinishDx() {
const lw = this.getLw;
const d = Number(this.animationFinishCurrent);
const animationFinishCurrent = d + 1;
const a = Number(this.width) * animationFinishCurrent;
const b = Number(this.width) / 2;
const c = lw / 2;
const x = a - b - c;
return x;
},
getDxNum() {
const sw = Number(this.swiperWidth) / 750 * windowWidth;
const b = this.dx / sw;
const c = Number(this.width);
const a = b * c;
return a;
},
getBoxStyle3() {
const x = this.getFinishDx + this.getDxNum;
return `transform:translate(${x}rpx, -100%);width:${this.getLw}rpx;background-color: ${this.getActiveColor};${this.line2Style}`;
}
},
watch: {
current(n, o) {
this.change(n);
}
},
data() {
return {
left: 0,
line2Width: Number(this.animationLineWidth),
setTimeoutFc: null,
componentsWidth: 0,
animationFinishCurrent: this.current,
dx: 0
}
},
// #ifndef H5
onReady() {
this.getQuery(() => {
this.countScrollX();
});
},
// #endif
// #ifdef H5
mounted() {
this.getQuery(() => {
this.countScrollX();
});
},
// #endif
methods: {
emit(index) {
this.$emit('change', index);
},
change() {
this.countScrollX();
if (this.animationMode === 'line2') {
this.line2Width = 2;
if (this.setTimeoutFc) clearTimeout(this.setTimeoutFc);
this.setTimeoutFc = setTimeout(() => {
this.line2Width = this.animationLineWidth;
}, this.getDuration * 1000 * 3 / 5);
}
},
getQuery(cb) {
try {
let view = uni.createSelectorQuery().in(this).select('.QS-tabs');
view.fields({
size: true
}, data => {
if (data) {
this.componentsWidth = data.width;
if (cb && typeof cb === 'function') cb(data);
} else {
this.retryGetQuery(cb);
}
}).exec();
} catch (e) {
//TODO handle the exception
this.componentsWidth = windowWidth;
}
},
retryGetQuery() {
try {
let view = uni.createSelectorQuery().select('.QS-tabs');
view.fields({
size: true
}, data => {
if (data) {
this.componentsWidth = data.width;
} else {
this.componentsWidth = windowWidth;
}
if (cb && typeof cb === 'function') cb(data);
}).exec();
} catch (e) {
//TODO handle the exception
this.componentsWidth = windowWidth;
}
},
countScrollX() {
if (this.autoCenter) {
const width = Number(this.width);
const itemWidth = width / 750 * windowWidth;
let left = itemWidth * (this.getCurrent + 1) - itemWidth / 2;
let fatherWidth;
if (this.autoCenterMode === 'window') {
fatherWidth = windowWidth;
} else {
fatherWidth = this.componentsWidth;
}
this.left = left - fatherWidth / 2;
}
},
setDx(dx) {
this.dx = dx;
},
setFinishCurrent(current) {
this.dx = 0;
this.animationFinishCurrent = current;
}
}
}
</script>
<style scoped>
view,
scroll-view {
box-sizing: border-box;
}
.QS-tabs {
width: 100%;
transition-property: background-color, color;
}
.QS-tabs-scroll {
width: 100%;
white-space: nowrap;
position: relative;
}
.QS-tabs-scroll-item {
position: relative;
display: inline-block;
text-align: center;
transition-property: background-color, color;
padding: 0 20rpx;
}
.content {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.boxStyle {
pointer-events: none;
position: absolute;
transition-property: all;
}
.boxStyle2 {
pointer-events: none;
position: absolute;
bottom: 0;
left: 0;
transition-property: all;
}
.itemBackgroundBox {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
transition-property: transform, background-color;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.itemBackground {
height: 100%;
width: 100%;
transition-property: all;
}
</style>