Files
medicine_app/components/direction-swiper/direction-swiper.vue
2024-05-22 13:42:15 +08:00

447 lines
15 KiB
Vue

<template>
<view class="direction_swiper" ref="directionSwiper" @touchstart="onSwiperTouchstart" @touchmove="onSwiperTouchmove" @touchcancel="onSwiperTouchcancel" @touchend="onSwiperTouchend">
<view class="swiper_content_box" ref="swiperContent" :style="{width: (screenWidth * tabData.length) + 'px', height: screenHeight + 'px' ,transform: 'translateX(' + translateX + 'px)', transition: 'transform ' + animationTime + 'ms ease'}">
<view class="swiper_container" v-for="(item,index) of tabData" :key="index" :ref="'swiperContainer' + item.key" :style="{ width: screenWidth + 'px', height: (screenHeight * (item.list.length || 1)) + 'px', transform: 'translateY(' + swiperData[index].translateY + 'px)', transition: 'transform ' + animationTime + 'ms ease'}">
<view v-if="item.list && item.list.length > 0" :style="{ width: screenWidth + 'px', height: (screenHeight * (item.list.length || 1)) + 'px'}">
<view v-for="(childItem,childIndex) of item.list" :key="childIndex" class="swiper_item " v-if="Math.abs(swiperData[swiperIndex].swiperItemIndex - childIndex) < 3" :style="{ top: (childIndex * screenHeight) + 'px', width: screenWidth + 'px', height: screenHeight + 'px' }">
<slot :item="item" :childItem="childItem" :index="index" :childIndex="childIndex"></slot>
</view>
</view>
<view class="swiper_empty" v-else :style="{ width: screenWidth + 'px', height: screenHeight + 'px' }">
<image class="swiper_empty_image" src="http://qn.kemean.cn/upload/202104/12/1618198729835lgteuvym.png" mode="aspectFit"></image>
<text class="swiper_empty_text">{{ item.emptyText || '暂无数据' }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
let clearTime = null;
let lastTime = 0;
// #ifdef APP-NVUE
const animation = weex.requireModule('animation');
const dom = weex.requireModule('dom');
// #endif
export default {
props: {
list: {
type: Array,
default () {
return []
}
},
current: {
type: Number,
default () {
return 0
}
},
},
data() {
return {
//开始触摸时间
startTime: 0,
//开始触摸距离
touchStartY: 0,
touchStartX: 0,
//上次的位置
currentY: 0,
currentX: 0,
contentTranslateX: 0,
tabData: [],
swiperData: [],
animationTime: 0,
animationDirection: "Y",
screenHeight: 0,
screenWidth: 0,
// 是否允许滑动
canSlide: true,
// 手指数量
fingersNumber: 0,
swiperIndex: 0,
translateX: 0,
};
},
//第一次加载
created() {
let systemInfo = uni.getSystemInfoSync();
this.screenWidth = systemInfo.screenWidth;
this.screenHeight = systemInfo.screenHeight;
this.$nextTick(() => {
this.getContainerWidthHeight();
});
let swiperData = [];
if(this.list && this.list.length > 0){
this.list.forEach(item => {
if(item.key && item.list){
let objectData = Object.assign({
list: [],
}, item);
this.tabData.push(objectData);
swiperData.push({
//滑动距离
translateY: 0,
swiperItemIndex: 0,
length: item.list.length || 0,
key: item.key
});
} else {
uni.showToast({
title: "参数没有key或list",
icon: "none"
});
}
});
}
this.swiperData = swiperData;
},
watch: {
list(val){
let tabData = [];
if(val && val.length > 0){
val.forEach((item,index) => {
if(item.key){
tabData.push(item);
this.swiperData[index].length = item.list.length || 0;
} else {
uni.showToast({
title: "【list】参数没有key",
icon: "none"
});
}
});
}
this.tabData = tabData;
this.$forceUpdate();
},
current(val){
this.onType(val);
}
},
//方法
methods: {
// 获取内容的高度
getContainerWidthHeight(){
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['directionSwiper'], (data) => {
this.screenWidth = data.size.width;
this.screenHeight = data.size.height;
});
// #endif
// #ifndef APP-NVUE
uni.createSelectorQuery().in(this).selectAll('.direction_swiper')
.boundingClientRect(data => {
this.screenWidth = data[0].width;
this.screenHeight = data[0].height;
}).exec()
// #endif
},
onType(index){
this.swiperIndex = index;
this.onChangeY();
this.contentTranslateX = -(index * this.screenWidth);
// #ifdef APP-NVUE
let swiperContent = this.$refs.swiperContent;
if(swiperContent){
if(Array.isArray(swiperContent)){
swiperContent = swiperContent[0];
}
animation.transition(this.$refs.swiperContent, {
styles: {
transform: 'translate('+ this.contentTranslateX +'px, 0px)',
transformOrigin: 'center center'
},
duration: 360, //ms
timingFunction: 'ease',
delay: 0 //ms
}, function () {
});
}
// #endif
// #ifndef APP-NVUE
this.translateX = this.contentTranslateX;
this.animationTime = 360;
// #endif
},
onChangeY() {
this.$emit("changeY", {
...this.tabData[this.swiperIndex],
...this.swiperData[this.swiperIndex],
});
},
setAnimationY(translateY, animationTime, indexChange){
this.animationTime = animationTime;
let swiperItem = this.swiperData[this.swiperIndex];
swiperItem.translateY = translateY;
if(indexChange){
if(indexChange == "less" && swiperItem.swiperItemIndex > 0){
swiperItem.swiperItemIndex -= 1;
} else if(indexChange == "plus" && swiperItem.swiperItemIndex < swiperItem.length - 1){
swiperItem.swiperItemIndex += 1;
}
}
this.$set(this.swiperData, this.swiperIndex, swiperItem);
// #ifdef APP-NVUE
let swiperContainer = this.$refs['swiperContainer' + swiperItem.key];
if(swiperContainer){
if(Array.isArray(swiperContainer)){
swiperContainer = swiperContainer[0];
}
animation.transition(swiperContainer, {
styles: {
transform: 'translate(0px, ' + translateY + 'px)',
transformOrigin: 'center center'
},
duration: animationTime, //ms
timingFunction: 'ease',
delay: 0 //ms
}, function () { });
}
// #endif
setTimeout(() => {
this.canSlide = true;
}, animationTime);
},
setAnimationX(translateX, animationTime, indexChange){
if(indexChange){
if(indexChange == "less" && this.swiperIndex > 0){
this.swiperIndex -= 1;
this.contentTranslateX = translateX;
this.$emit("changeX", {
swiperIndex: this.swiperIndex,
...this.swiperData[this.swiperIndex]
});
this.onChangeY();
} else if(indexChange == "plus" && this.swiperIndex < this.swiperData.length - 1){
this.swiperIndex += 1;
this.contentTranslateX = translateX;
this.$emit("changeX", {
swiperIndex: this.swiperIndex,
...this.swiperData[this.swiperIndex]
});
this.onChangeY();
}
}
// #ifdef APP-NVUE
let swiperContent = this.$refs.swiperContent;
if(swiperContent){
if(Array.isArray(swiperContent)){
swiperContent = swiperContent[0];
}
animation.transition(swiperContent, {
styles: {
transform: 'translate('+ translateX +'px, 0px)',
transformOrigin: 'center center'
},
duration: animationTime, //ms
timingFunction: 'ease',
delay: 0 //ms
}, function () {
});
}
// #endif
// #ifndef APP-NVUE
this.translateX = translateX;
this.animationTime = animationTime;
// #endif
setTimeout(() => {
this.canSlide = true;
},animationTime);
},
// 手指触摸动作开始
onSwiperTouchstart(e) {
this.fingersNumber += 1;
if(this.canSlide && this.fingersNumber == 1){
//储存手指触摸坐标,当前时间戳,当前坐标
// #ifdef APP-NVUE
this.touchStartY = e.changedTouches[0].screenY;
this.touchStartX = e.changedTouches[0].screenX;
// #endif
// #ifndef APP-NVUE
this.touchStartY = e.changedTouches[0].clientY;
this.touchStartX = e.changedTouches[0].clientX;
// #endif
let startTime = new Date().getTime();
this.startTime = startTime;
lastTime = startTime;
if(this.swiperData[this.swiperIndex]){
this.currentY = this.swiperData[this.swiperIndex].translateY;
} else {
this.currentY = 0;
}
this.currentX = this.contentTranslateX;
this.animationDirection = "";
}
},
// 手指触摸后移动
onSwiperTouchmove(e) {
if(this.canSlide && this.fingersNumber == 1){
//手指当前坐标
// #ifdef APP-NVUE
const clientY = e.changedTouches[0].screenY;
const clientX = e.changedTouches[0].screenX;
// #endif
// #ifndef APP-NVUE
const clientY = e.changedTouches[0].clientY;
const clientX = e.changedTouches[0].clientX;
// #endif
//计算滑动距离
const differenceY = this.touchStartY - clientY;
const differenceX = this.touchStartX - clientX;
let currentTime = new Date().getTime();
if(Math.abs(differenceY) > Math.abs(differenceX)){
let item = this.swiperData[this.swiperIndex];
//判断最终滑动方向Y轴
if (differenceY < 0) {
if(item.swiperItemIndex > 0){
this.animationDirection = "Y";
this.setAnimationY(this.currentY + Math.abs(differenceY), 0);
}
} else {
if(item.swiperItemIndex < (item.length - 1)){
this.animationDirection = "Y";
this.setAnimationY(this.currentY - differenceY, 0);
}
}
} else {
//判断最终滑动方向X轴
if (differenceX < 0) {
if(this.swiperIndex > 0){
this.animationDirection = "X";
this.setAnimationX(this.currentX + Math.abs(differenceX), 0);
}
} else {
if(this.swiperIndex < this.swiperData.length - 1){
this.animationDirection = "X";
this.setAnimationX(this.currentX - Math.abs(differenceX), 0);
}
}
}
lastTime = currentTime;
}
},
// 手指触摸动作被打断,如来电提醒,弹窗
onSwiperTouchcancel(e) {
// #ifdef APP-NVUE
this.finallySlide(e.changedTouches[0].screenX, e.changedTouches[0].screenY);
// #endif
// #ifndef APP-NVUE
this.finallySlide(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
// #endif
this.fingersNumber = 0;
},
// 手指触摸动作结束
onSwiperTouchend(e) {
// #ifdef APP-NVUE
this.finallySlide(e.changedTouches[0].screenX, e.changedTouches[0].screenY);
// #endif
// #ifndef APP-NVUE
this.finallySlide(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
// #endif
this.fingersNumber -= 1;
},
//最终判断滑动
finallySlide(finallyX, finallyY) {
if(this.canSlide && this.fingersNumber == 1){
//手指离开的时间
const endTime = new Date().getTime();
//手机滑动屏幕的总花费时间
const timeDifference = endTime - this.startTime;
this.canSlide = false;
//手指触摸总滑动距离
const distanceDifferenceY = this.touchStartY - finallyY;
if(this.animationDirection == "X"){
const distanceDifferenceX = this.touchStartX - finallyX;
//判断是否滑动到左边 滑动距离超过3分之一 或者 滑动时间在300毫秒并且距离在4分之一
if (Math.abs(distanceDifferenceX) > this.screenWidth / 3 || timeDifference < 500 && Math.abs(distanceDifferenceX) > (this.screenWidth / 5)) {
//判断最终滑动方向
let animationTime = 360;
if(timeDifference > 400 && timeDifference > 200){
animationTime = timeDifference;
}
if (distanceDifferenceX < 0) {
this.setAnimationX(0, animationTime, "less");
} else {
this.setAnimationX(-this.screenWidth, animationTime, "plus");
}
} else {
this.setAnimationX(this.currentX, 360);
}
} else if(this.animationDirection == "Y"){
//判断是否滑动到左边 滑动距离超过3分之一 或者 滑动时间在300毫秒并且距离在4分之一
if (Math.abs(distanceDifferenceY) > this.screenHeight / 5 || timeDifference < 500 && Math.abs(distanceDifferenceY) > (this.screenHeight / 10)) {
//判断最终滑动方向
let animationTime = 360;
if(timeDifference < 500 && timeDifference > 200){
animationTime = timeDifference;
}
if(Math.abs(distanceDifferenceY) < this.screenHeight){
let remainingRatio = (this.screenHeight - Math.abs(distanceDifferenceY)) / this.screenHeight;
animationTime = animationTime * remainingRatio;
if(animationTime < 150){
animationTime = 150;
}
}
if (distanceDifferenceY < 0) {
this.setAnimationY(this.currentY + this.screenHeight, animationTime, "less");
} else {
this.setAnimationY(this.currentY - this.screenHeight, animationTime, "plus");
}
this.onChangeY();
} else {
let animationTime = 360;
let remainingRatio = Math.abs(distanceDifferenceY) / this.screenHeight;
animationTime = animationTime * remainingRatio;
if(animationTime < 150){
animationTime = 150;
}
this.setAnimationY(this.currentY, animationTime);
}
} else {
this.canSlide = true;
}
}
},
}
};
</script>
<style lang="scss" scoped>
@import '@/style/mixin.scss';
.direction_swiper {
width: 750rpx;
overflow: hidden;
}
.swiper_content_box {
flex-direction: row;
}
.swiper_container {
position: relative;
}
.swiper_item {
width: 750rpx;
position: absolute;
top: 1504rpx;
left: 0rpx;
}
.swiper_empty {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
width: 750rpx;
position: absolute;
top: 0rpx;
left: 0rpx;
}
.swiper_empty_image {
width: 360rpx;
height: 360rpx;
}
.swiper_empty_text {
font-size: 28rpx;
color: #FFF;
}
</style>