Newer
Older
xinJiangMiniProgranm / components / card.vue
lyg on 16 Jan 2024 18 KB 谣言比例替换
<!-- 谣言查证卡片 叠加 -->
<template>  
	<view class="flipcard" id="flipcard" ref="flipcard">  
		<view class="flipcard-item" 
			v-for="(item, index) in pages" 
			:class="index%2 ? 'flipcard-odd' : 'flipcard-even'"
			: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">{{isVoteFlag ? '已投票' : '未投票'}}</view> -->
					 <view></view>
				</view>
				
				<!-- 谣言 -->
				<view class="yy-content">
					<view class="text">谣言:</view>
					<view class="title">
						{{currentShow.title}}
					</view>
					<view class="source">
						来源:{{currentShow.source}}
					</view>
				</view>
				
				<!-- 投票 -->
				<view class="tp">
					<view class="real"  v-if="Number.parseFloat(currentShow.yesNum) > 0" :style="{width: 50 + '%' }" @click="vote(true)">
						<text>真</text>
						<text>{{`${yesRoate}%`}}</text>
					</view>
					<view class="fail" v-if="Number.parseFloat(currentShow.noNum) > 0" :style="{width: 50 + '%' }" @click="vote(false)">
						<text>假</text>
						<text>{{`${noRoate}%`}}</text>
					</view>
				</view>
			</view>  
		</view>  
	</view>  
</template>  
  
<script>  
  import { getTouchPoint, getElSize } from '@/common/utils.js'  
	import { isVote, addVote } from '@/api/rumor.js'
	import { decryption } from '../api/mine.js'
    export default {  
			props: {  
				pages: {  
					type: Array,  
					default: []  
				},  
				stackInit: {  
					type: Object,  
					default: {}  
				},  
			},  
			data() {  
				return {  
					el: {},  
					basicdata: {  // 触摸位置信息
						start: {},  
						end: {}  
					}, 
					isVoteFlag:false, // 是否投票标识
					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  
					},
					id:''
				}  
			},  
			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  
				},
				// 当前展示的谣言信息
				currentShow() {
					return this.pages[this.temporaryData.currentPage]
				},
				// 真比例
				yesRoate() {
					return Math.round(Number(this.currentShow.yesNum) / (Number(this.currentShow.yesNum) + Number(this.currentShow.noNum)) *100) 
				},
				noRoate() {
					return Math.round(Number(this.currentShow.noNum) / (Number(this.currentShow.yesNum) + Number(this.currentShow.noNum)) *100)
				}
			},  
			created() {  
				this.$nextTick(async e => {  
					this.el = await getElSize('flipcard', this)  // 卡片的位置对象,上下左右,宽高
					console.log('----', this.el)
				})  
				const that = this
				// 获取匿名用户唯一标识
				wx.login({
				      success(res){
				        console.log('code====',res.code);
				        wx.request({
				          url: 'https://api.weixin.qq.com/sns/jscode2session',
				          data:{
				            appid: 'wxab7dd0a14272badf',
				            secret: '1ff1d2119715e82db1adda4fe143eedb',
				            js_code: res.code,
				            grant_type:'authorization_code'
				          },
				          method:"GET",
				          success(res){
							that.id = res.data.openid
				            console.log('openid=====',res.data.openid);   // 得到openid
				            console.log('session_key====', res.data.session_key);   // 得到 session_key
				          }
				        })
				      }
				    })
			},  
			watch:{
				'currentShow':{
					immediate:true,
					deep:true,
					async handler(newVal){
						if (this.currentShow) {
							const userinfo = wx.getStorageSync('userInfoxj')
							let phone = ''
							if (userinfo?.phone) {
							   phone = await decryption(userinfo.phone)
							}
							const params = {
								rumorId:this.currentShow.id, // id
								votePerson:phone ? phone  : this.id // 用户唯一标识
							}
							// 查询用户是否投票
							// isVote(params).then(res => {
							// 	console.log(res, '查询用户是否投票')
							// 	this.isVoteFlag = res
							// })
						}
						
					}
				}
			},
			methods: {  
				// 点击真假进行投票
				async vote(flag) {
					// 已经投票
					// if(this.isVoteFlag) {
					// 	uni.$u.toast('您已投过票')
					// 	// this.next()
					// 	return
					// }
					// 未投票
					const userinfo = wx.getStorageSync('userInfoxj')
					let phone = ''
					if (userinfo?.phone) {
					   phone = await decryption(userinfo.phone)
					}
					const params = {
						contentId:this.currentShow.contentId,
						// votePerson:phone ? phone  : this.id, // 用户唯一标识
						flag: flag ? '1' : '2',
						deviceUniqueCode: phone ? phone  : this.id
					}
					addVote(params).then(res => {
						console.log(res, '投票结果')
						uni.$u.toast('投票成功')
						// 投票成功修改 投票状态
						// this.isVoteFlag = true
						// this.next()
					})
				},
				// 触摸开始
				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  
							// 触摸纵坐标-卡片相对于屏幕顶部距离=手指相对于卡片顶部距离
							this.temporaryData.offsetY = point.y - this.el.top  // 手指相对于卡片顶部的距离
						}  
					} 
					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()  // 水平滑动距离是否小于0  左滑< 0  右滑>0
						// let angleRatio = this.angleRatio()  
						// this.temporaryData.rotate = rotateDirection * this.offsetWidthRatio * 15 * angleRatio  
					}  
				},  
				touchend(e, index) {  
					// 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  // 右滑+200 左滑-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 {  // 划出面积比例小于0.4的话
						this.temporaryData.poswidth = 0  // 水平滑动距离置为0
						this.temporaryData.posheight = 0  // 垂直滑动距离置为0
						this.temporaryData.swipe = false  // 是否轮播,是否翻页
						this.temporaryData.rotate = 0  // 翻页角度为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  
					})  
				},  
				// css完成过渡后触发,就是动画结束触发
				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()  
				},  
				// 计算滑动距离是否小于0
				rotateDirection() {  
					if (this.temporaryData.poswidth <= 0) {  // 滑动水平距离<0 -左滑   
						return -1  
					} else {  // >0 右滑
						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;  
            &-odd {
				background: linear-gradient(#bed5fe 0%, #eef1fd 92%, #fff 100%) !important;
			}
			&-even {
				background: linear-gradient(#3c72d2 0%, #eef1fd 92%, #fff 100%) !important;
			}
			&-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>