Newer
Older
xinJiangMiniProgranm / components / card.vue
<template>  
	<view class="flipcard" id="flipcard" ref="flipcard">  
		<view class="flipcard-item" v-for="(item, index) in pages" :key="index"  
			:style="[transformIndex(index),transform(index)]" @touchstart="touchstart" @touchmove="touchmove"  
			@touchend="touchend" @touchcancel="touchend" @transitionend="onTransitionEnd(index,'transitionend')">  
			<view class="flipcard-item-stack-info">  
				<view style="display: flex;justify-content: space-between;">
					<view class="t3">一起来识谣</view>
					<view class="t4">已投票</view> 
				</view>
				
				<!-- 谣言 -->
				<view class="yy-content">
					<view class="text">谣言:</view>
					<view class="title">
						支持公众投票,来区分某条信息是否是谣言,支持公众投票,来区分某条众投票,言,支持公众投票,
					</view>
					<view class="source">
						来源:新疆网络举报
					</view>
				</view>
				
				<!-- 投票 -->
				<view class="tp">
					<view class="real">
						<text>真</text>
						<text>1212312</text>
					</view>
					<view class="fail">
						<text>142353245</text>
						<text>假</text>
					</view>
				</view>
			</view>  
		</view>  
	</view>  
</template>  
  
<script>  
    import { getTouchPoint, getElSize } from '@/common/utils.js'  
    export default {  
			props: {  
				pages: {  
						type: Array,  
						default: []  
				},  
				stackInit: {  
						type: Object,  
						default: {}  
				},  
			},  
			data() {  
				return {  
					el: {},  
					basicdata: {  
						start: {},  
						end: {}  
					},  
					temporaryData: {  
						isStackClick: true, // 多了一个参数  
						offsetY: '',  
						poswidth: 0,  
						posheight: 0,  
						lastPosWidth: '',  
						lastPosHeight: '',  
						lastZindex: '',  
						rotate: 0,  
						lastRotate: 0,  
						visible: this.stackInit.visible || 3, // 展示几个  
						tracking: false,  
						animation: false,  
						currentPage: this.stackInit.currentPage || 0, // 当前第几个  
						opacity: 1,  
						lastOpacity: 0,  
						swipe: false,  
						zIndex: 10  
					}  
				}  
			},  
			computed: {  
				// 划出面积比例  
				offsetRatio(e) {  
					let width = this.el.width  
					let height = this.el.height  
					let offsetWidth = width - Math.abs(this.temporaryData.poswidth)  
					let offsetHeight = height - Math.abs(this.temporaryData.posheight)  
					let ratio = 1 - (offsetWidth * offsetHeight) / (width * height) || 0  
					return ratio > 1 ? 1 : ratio  
				},  
				// 划出宽度比例  
				offsetWidthRatio() {  
					let width = this.el.offsetWidth  
					let offsetWidth = width - Math.abs(this.temporaryData.poswidth)  
					let ratio = 1 - offsetWidth / width || 0  
					return ratio  
				}  
			},  
			created() {  
				this.$nextTick(async e => {  
					this.el = await getElSize('flipcard', this)  
				})  
			},  
			methods: {  
				touchstart(e) {  
					if (this.temporaryData.tracking) {  
						return  
					}  
					// 是否为touch  
					if (e.type === 'touchstart') {  
						if (e.touches.length > 1) {  
							this.temporaryData.tracking = false  
							return  
						} else {  
							// 记录起始位置  
							const point = getTouchPoint(e)  

							// console.log("记录起始位置",point);  
							this.basicdata.start.t = new Date().getTime()  
							this.basicdata.start.x = point.x  
							this.basicdata.start.y = point.y  
							this.basicdata.end.x = point.x  
							this.basicdata.end.y = point.y  
							// offsetY在touch事件中没有,只能自己计算  
							this.temporaryData.offsetY = point.y - this.el.top  
							// console.log(this.temporaryData.offsetY)  
						}  
							// pc操作  
					} else {  
						console.log("pc记录起始位置");  
						uni.showToast({  
								title: '无效操作',  
								icon: 'none'  
						});  
						// this.basicdata.start.t = new Date().getTime()  
						// this.basicdata.start.x = e.clientX  
						// this.basicdata.start.y = e.clientY  
						// this.basicdata.end.x = e.clientX  
						// this.basicdata.end.y = e.clientY  
						// this.temporaryData.offsetY = e.offsetY  
					}  
					this.temporaryData.isStackClick = true // 多了一个参数  
					this.temporaryData.tracking = true  
					this.temporaryData.animation = false  
				},  
				touchmove(e) {  
					this.temporaryData.isStackClick = false // 多了一个参数  
					// 记录滑动位置  
					if (this.temporaryData.tracking && !this.temporaryData.animation) {  
						const point = getTouchPoint(e)  
						if (e.type === 'touchmove') {  
								e.preventDefault()  
								this.basicdata.end.x = point.x  
								this.basicdata.end.y = point.y  
						} else {  
								e.preventDefault()  
								this.basicdata.end.x = point.x  
								this.basicdata.end.y = point.y  
						}  
						// 计算滑动值  
						this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x  
						this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y  
						let rotateDirection = this.rotateDirection()  
						let angleRatio = this.angleRatio()  
						this.temporaryData.rotate = rotateDirection * this.offsetWidthRatio * 15 * angleRatio  
					}  
				},  
				touchend(e, index) {  
					console.log(this.temporaryData.isStackClick)  
					if(this.temporaryData.isStackClick) {  
							this.$emit('click', index) // 多了一个参数,触发上层传递事件  
							this.temporaryData.isStackClick = false  
					}  
					this.temporaryData.isStackClick = true // 多了一个参数  
					this.temporaryData.tracking = false  
					this.temporaryData.animation = true  
					// 滑动结束,触发判断  
					// 判断划出面积是否大于0.4  
					if (this.offsetRatio >= 0.4) {  
						// 计算划出后最终位置  
						let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth)  
						this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 :  
								this.temporaryData.poswidth - 200  
						this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData  
								.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio)  
						this.temporaryData.opacity = 0  
						this.temporaryData.swipe = true  
						this.nextTick()  
						// 不满足条件则滑入  
					} else {  
						this.temporaryData.poswidth = 0  
						this.temporaryData.posheight = 0  
						this.temporaryData.swipe = false  
						this.temporaryData.rotate = 0  
					}  
				},  
				nextTick() {  
					// 记录最终滑动距离  
					this.temporaryData.lastPosWidth = this.temporaryData.poswidth  
					this.temporaryData.lastPosHeight = this.temporaryData.posheight  
					this.temporaryData.lastRotate = this.temporaryData.rotate  
					this.temporaryData.lastZindex = 20  
					// 循环currentPage  
					this.temporaryData.currentPage = this.temporaryData.currentPage === this.pages.length - 1 ? 0 : this  
							.temporaryData.currentPage + 1  
					// currentPage切换,整体dom进行变化,把第一层滑动置最低  
					this.$nextTick(() => {  
						this.temporaryData.poswidth = 0  
						this.temporaryData.posheight = 0  
						this.temporaryData.opacity = 1  
						this.temporaryData.rotate = 0  
					})  
				},  
				onTransitionEnd(index, log) {  
					let lastPage = this.temporaryData.currentPage === 0 ? this.pages.length - 1 : this.temporaryData  
							.currentPage - 1  
					// dom发生变化正在执行的动画滑动序列已经变为上一层  
					if (this.temporaryData.swipe && index === lastPage) {  
						this.temporaryData.animation = true  
						this.temporaryData.lastPosWidth = 0  
						this.temporaryData.lastPosHeight = 0  
						this.temporaryData.lastOpacity = 0  
						this.temporaryData.lastRotate = 0  
						this.temporaryData.swipe = false  
						this.temporaryData.lastZindex = -1  
					}  
				},  
				prev() {  
					this.temporaryData.tracking = false  
					this.temporaryData.animation = true  
					// 计算划出后最终位置  
					let width = this.el.width  
					this.temporaryData.poswidth = -width  
					this.temporaryData.posheight = 0  
					this.temporaryData.opacity = 0  
					this.temporaryData.rotate = '-3'  
					this.temporaryData.swipe = true  
					this.nextTick()  
				},  
				next() {  
					this.temporaryData.tracking = false  
					this.temporaryData.animation = true  
					// 计算划出后最终位置  
					let width = this.el.width  
					this.temporaryData.poswidth = width  
					this.temporaryData.posheight = 0  
					this.temporaryData.opacity = 0  
					this.temporaryData.rotate = '3'  
					this.temporaryData.swipe = true  
					this.nextTick()  
				},  
				rotateDirection() {  
					if (this.temporaryData.poswidth <= 0) {  
							return -1  
					} else {  
							return 1  
					}  
				},  
				angleRatio() {  
					let height = this.el.height  
					let offsetY = this.temporaryData.offsetY  
					let ratio = -1 * (2 * offsetY / height - 1)  
					return ratio || 0  
				},  
				inStack(index, currentPage) {  
					let stack = []  
					let visible = this.temporaryData.visible  
					let length = this.pages.length  
					for (let i = 0; i < visible; i++) {  
							if (currentPage + i < length) {  
									stack.push(currentPage + i)  
							} else {  
									stack.push(currentPage + i - length)  
							}  
					}  
					return stack.indexOf(index) >= 0  
				},  
				// 非首页样式切换  
				transform(index) {  
					let currentPage = this.temporaryData.currentPage  
					let length = this.pages.length  
					let lastPage = currentPage === 0 ? this.pages.length - 1 : currentPage - 1  
					let style = {}  
					let visible = this.temporaryData.visible  
					if (index === this.temporaryData.currentPage) {  
							return  
					}  
					if (this.inStack(index, currentPage)) {  
							let perIndex = index - currentPage > 0 ? index - currentPage : index - currentPage + length  
							style['opacity'] = '1'  
							style['transform'] = 'translate3D(0,0,' + -1 * 60 * (perIndex - this.offsetRatio) + 'px' + ')'  
							style['zIndex'] = visible - perIndex  
								
							if (!this.temporaryData.tracking) {    
									style['transitionTimingFunction'] = 'ease'    
									style['transitionDuration'] = 300 + 'ms'    
							}  
					} else if (index === lastPage) {  
							style['transform'] = 'translate3D(' + this.temporaryData.lastPosWidth + 'px' + ',' + this.temporaryData  
									.lastPosHeight + 'px' + ',0px) ' + 'rotate(' + this.temporaryData.lastRotate + 'deg)'  
							style['opacity'] = this.temporaryData.lastOpacity  
							style['zIndex'] = this.temporaryData.lastZindex  
							style['transitionTimingFunction'] = 'ease'    
							style['transitionDuration'] = 300 + 'ms'  
					} else {  
							style['zIndex'] = '-1'  
							style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')'  
					}  
					return style  
				},  
				// 首页样式切换  
				transformIndex(index) {  
					if (index === this.temporaryData.currentPage) {  
						let style = {}  
						style['transform'] = 'translate3D(' + this.temporaryData.poswidth + 'px' + ',' + this.temporaryData  
								.posheight + 'px' + ',0px) ' + 'rotate(' + this.temporaryData.rotate + 'deg)'  
						style['opacity'] = this.temporaryData.opacity  
						style['zIndex'] = 10  
						if (this.temporaryData.animation) {  
							style['transitionTimingFunction'] = 'ease'    
							style['transitionDuration'] = (this.temporaryData.animation ? 300 : 0) + 'ms'    
						}  
						return style  
					}  
				},  
			}  
    }  
</script>  
  
<style lang="scss" scoped>  
		@import '../common/variables.scss';
    .flipcard {  
			width: 100%;  
			// height: 100%;  
			height: 600rpx;  
			position: relative;  
			// 层叠效果
			perspective: 2000rpx;  
			perspective-origin: 50% 150%;  
			-webkit-perspective: 1000px;  
			-webkit-perspective-origin: 50% 150%;  
			margin: 0;  
			padding: 0;  
  
			&-item {  
				background: linear-gradient(#bed5fe 0%, #eef1fd 92%, #fff 100%);
				height: 100%;  
				width: 100%;  
				border-radius: 30rpx;  
				overflow: hidden;  
				position: absolute;  
				opacity: 0;  
				display: -webkit-flex;  
				display: flex;  
				-webkit-flex-direction: column;  
				flex-direction: column;  
				-webkit-touch-callout: none;  
				-webkit-user-select: none;  
				-khtml-user-select: none;  
				-moz-user-select: none;  
				-ms-user-select: none;  
				user-select: none;  
				pointer-events: auto;  
				padding: 30rpx;  
				/* #ifndef APP-NVUE */  
				box-sizing: border-box;  
				/* #endif */  
				box-shadow: 0px 2rpx 30rpx 0px rgba(94, 114, 153, 0.3);  
				&-stack-info {  
					.t3 {  
						padding: 10rpx 30rpx;  
						background-color: #fff;
						color: #0043ac;
						font-weight: 600;
						width: fit-content;
						border-radius: 16rpx;
						margin-bottom: 32rpx;
					}  
					.t4 {
						padding: 10rpx 30rpx;
						background-color: #ccc;
						color: #0043ac;
						font-weight: 600;
						width: fit-content;
						border-radius: 16rpx;
						margin-bottom: 32rpx;
					}
					.yy-content {
						background-color: red;
						padding: 10rpx 30rpx;
						border-radius: 16rpx;
						background-color: #fff;
						.text {
							color: #0043ac;
							font-weight: 600;
						}
						.title {
							color: #000;
							font-size: 36rpx;
							font-weight: 600;
							margin-top: 32rpx;
							@include exceeding-line-overflow(3);
						}
						.source {
							margin-top: 20rpx;
							font-size: 26rpx;
							color: #c1c1c1;
							margin-bottom: 20rpx;
						}
					}
					
					// 投票
					.tp {
						display: flex;
						width: 100%;
						margin-top: 32rpx;
						border-radius: 16rpx;
						overflow: hidden;
						color: #fff;
						background-color: #fff;
						.real {
							display: flex;
							justify-content: space-between;
							flex: 1;
							padding: 0 10rpx;
							// border: 2rpx solid green;
							border-right: none;
							background-color: #21a366;
						}
						.fail {
							display: flex;
							justify-content: space-between;
							flex: 1;
							text-align: right;
							background-color: #cc463d;
							padding: 0 10rpx;
							// border: 1rpx solid red;
							border-left: none;
						}
					}
					.problem {  
						height: 180rpx;  
						overflow: hidden;  
						text-overflow: ellipsis;  

						.problem-text {  
							font-size: 32rpx;  
							color: #333333;  
							line-height: 46rpx;  
						}  
					}  

					.desc {  
						margin-top: 36rpx;  

						.desc-text {  
								font-size: 26rpx;  
								color: #999999;  
						}  
					}  

					.bottom-box {  
						bottom: 30rpx;  
						left: 30rpx;  

						.avatar {  
								width: 80rpx;  
								height: 80rpx;  
								border-radius: 50%;  
								margin-right: 20rpx;  
						}  

						.name {  
							font-size: 28rpx;  
							color: #AAAAAA;  
						}  
					}  
				}  
			}  
    }  
</style>