<template>
	<view :style="{ overflowX: overOnePage ? 'hidden' : 'initial' }">
		<view class="item-wrap" :style="{ height: itemWrapHeight + 'px' }">
			<view
				:class="['item', cur == index ? 'cur' : '', curZ == index ? 'zIndex' : '', itemTransition && index !== cur ? 'itemTransition' : '']"
				v-for="(item, index) in list" :key="index" :id="'item' + index" :data-key="item.key" :data-index="index"
				:style="{ transform: `translate3d(${index === cur ? tranX : item.tranX}px, ${index === cur ? tranY : item.tranY}px, 0px)`, width: `${100 / columns}%` }"
				@tap="itemClick(index,item.data.images)" @longpress="longPress" @touchmove.stop="touchMove"
				@touchend.stop="touchEnd">
				<!-- start:请在该区域编写自己的渲染逻辑 -->
				<view class="info">
					<view class="info__item">
						<image class="image" :src="'http://picture.pangdly.com/'+item.data.images" mode="widthFix"></image>
					</view>
					<view class="info__item_btn" @click.stop="replace(item)">
						更换
					</view>
				</view>
				<!-- end:请在该区域编写自己的渲染逻辑 -->
			</view>
		</view>
		<!-- 更换图片弹窗 -->
		<u-popup v-model="show" mode="bottom" height="50%">
			<view style="display: flex;flex-wrap: wrap;width: 100%;">
				<view style="width: 25%;padding: 10rpx;" v-for="(item,index) in alternateImages" :key="index">
					<image :src="'http://picture.pangdly.com/'+item.images" style="width: 100%;height: 100%;"@click="changeImglist(item.images)" mode="widthFix" ></image>
				</view>
			</view>
		</u-popup>
	</view>
</template>

<script>
	export default {
		props: {
			listData: { // 已选择图片
				type: Array,
				default: () => {
					return []
				}
			},
			columns: { // 列数
				type: Number,
				default: 0
			},
			alternateImages: { // 备选图片
				type: Array,
				default: () => {
					return []
				}
			},
		},
		data() {
			return {
				checkImg: "", //点击更改图片的key
				show: false, //照片图库弹出层
				cur: -1, // 当前激活的元素
				curZ: -1, // 当前激活的元素, 用于控制激活元素z轴显示
				tranX: 0, // 当前激活元素的 X轴 偏移量
				tranY: 0, // 当前激活元素的 Y轴 偏移量
				itemWrapHeight: 0, // 动态计算父级元素高度
				dragging: false, // 是否在拖拽中
				list: [], //数据
				overOnePage: false, // 整个区域是否超过一个屏幕
				itemTransition: false, // item 变换是否需要过渡动画, 首次渲染不需要
				platform: "", //使用平台
			}
		},
		watch: {
			columns(newVal, oldVal) {
				this.columns = newVal
				this.dataChange()
			},
		},
		onReady() {
			this.init()
		},
		methods: {
			changeImglist(src) {//选择更换图片
				this.checkImg.data.images = src
				this.show = false
				let listData = []
				this.list.forEach(item => {
					listData[item.key] = item.data
				})
				this.$emit('change', { //给父组件传递数据
					listData: listData
				})
			},
			replace(item) { //更换图片
				this.checkImg = item
				this.show = true
			},
			init() {
				// 空值判断, 避免获取不到节点信息报错问题
				if (this.listData.length == 0) {
					this.list = []
					return
				}
				// 遍历数据源增加扩展项, 以用作排序使用
				let list = this.listData.map((item, index) => {
					let data = {
						key: index,
						tranX: 0,
						tranY: 0,
						data: item
					}
					return data
				})
				this.list = list
				this.itemTransition = false //阻止初次渲染动画效果
				let {
					windowWidth,
					windowHeight,
					platform
				} = uni.getSystemInfoSync()
				this.windowHeight = windowHeight //获取可使用窗口宽度

				this.platform = platform //获取客户端平台

				setTimeout(() => { //需要等页面渲染完成后执行函数,否则获取不到节点信息
					let queryItem = uni.createSelectorQuery().in(this);
					queryItem.select('.item').boundingClientRect(res => { // 获取每一项的宽高等属性
							let rows = Math.ceil(this.list.length / this.columns)
							this.item = res
							this.getPosition(this.list, false)

							let itemWrapHeight = rows * res.height
							this.itemWrapHeight = itemWrapHeight

							let queryWrap = uni.createSelectorQuery().in(this)
							queryWrap
								.select('.item-wrap')
								.boundingClientRect(res => {
									this.itemWrap = res
									//(元素高度 + 距离顶部距离 > 屏幕高度)  用该公式来计算是否超过一页
									let overOnePage = itemWrapHeight + res.top > this.windowHeight;
									this.overOnePage = overOnePage
								})
								.exec()
						})
						.exec()
				}, 200)
			},
			getPosition(data, vibrate = true) { //根据排序后 list 数据进行位移计算
				let list = data.map((item, index) => {
					item.tranX = this.item.width * (item.key % this.columns)
					item.tranY = Math.floor(item.key / this.columns) * this.item.height
					return item
				})
				this.list = list
				if (!vibrate) return

				if (this.platform != 'devtools') uni.vibrateShort() //判断设备开启震动效果

				let listData = []
				list.forEach(item => {
					listData[item.key] = item.data
				})
				this.$emit('change', { //给父组件传递数据
					listData: listData
				})
			},
			longPress(e) { //长按触发移动排序
				// 防止多指触发 drag 动作, 如果已经在 drag 中则返回, touchstart 事件中有效果
				if (this.dragging) return
				let {
					index
				} = e.currentTarget.dataset
				// 如果是固定 item 则 return
				// if (this.isFixed(index)) return

				// 存储初始化触摸点信息
				this.startTouch = e.changedTouches[0]
				// 如果未获取到触摸点信息 return
				if (!this.startTouch) return
				// 表示 drag 动作开始
				this.dragging = true

				let { // 获取初始化点的 pageX 和 pageY
					pageX: startPageX,
					pageY: startPageY
				} = this.startTouch

				if (this.columns === 1) { // 单列时候X轴初始不做位移
					this.tranX = 0
				} else { // 多列的时候计算X轴初始位移, 使 item 水平中心移动到点击处
					this.tranX = startPageX - this.item.width / 2 - this.itemWrap.left
				}
				// 计算Y轴初始位移, 使 item 垂直中心移动到点击处
				this.tranY = startPageY - this.item.height / 2 - this.itemWrap.top

				this.cur = index
				this.curZ = index

				this.oldX = this.tranX
				this.oldY = this.tranY

				uni.vibrateShort()
			},
			touchMove(e) {
				// 如果不在 drag 中则返回
				if (!this.dragging) return
				let currentTouch = e.changedTouches[0]
				// 如果未获取到触摸点信息 return
				if (!currentTouch) return
				// 获取初始化点的 pageX , pageY 以及标示符 identifier
				let {
					pageX: startPageX,
					pageY: startPageY,
					identifier: startId
				} = this.startTouch
				let {
					pageX: currentPageX,
					pageY: currentPageY,
					identifier: currentId,
					clientY: currentClientY
				} = currentTouch

				// 如果不是同一个触发点则返回
				if (startId != currentId) return

				// 通过 当前坐标点, 初始坐标点, 初始偏移量 来计算当前偏移量
				let tranX = currentPageX - startPageX + this.oldX,
					tranY = currentPageY - startPageY + this.oldY

				// 单列时候X轴初始不做位移
				if (this.columns === 1) tranX = 0

				if (this.overOnePage) { // 判断是否超过一屏幕, 超过则需要判断当前位置动态滚动page的位置
					if (currentClientY > this.windowHeight - this.item.height) {
						uni.pageScrollTo({
							scrollTop: currentPageY + this.item.height - this.windowHeight,
							duration: 300
						});
					} else if (currentClientY < this.item.height) {
						uni.pageScrollTo({
							scrollTop: currentPageY - this.item.height,
							duration: 300
						});
					}
				}

				// 设置当前偏移量
				this.tranX = tranX
				this.tranY = tranY

				// 获取当前的起始坐标
				let originKey = e.currentTarget.dataset.key
				// 根据偏移量计算得出目标坐标
				let endKey = this.calculateMoving(tranX, tranY)

				// 如果是固定 item 则 return
				// if (this.isFixed(endKey)) return
				// 防止拖拽过程中发生乱序问题
				if (originKey == endKey || this.originKey == originKey) return
				this.originKey = originKey
				// 触发排序
				this.insert(originKey, endKey)
			},
			touchEnd() {
				if (!this.dragging) return
				this.clearData()
			},
			//判断是否是固定的 item
			// isFixed(key) {
			// 	let list = this.list
			// 	if (list && list[key] && list[key].fixed) {
			// 		return true
			// 	}
			// 	return false
			// },
			clearData() { //清除参数
				this.originKey = -1
				this.dragging = false
				this.cur = -1
				this.tranX = 0
				this.tranY = 0
				// 延迟清空
				setTimeout(() => {
					this.curZ = -1
				}, 3000)
			},
			l2r(key, origin) { //正序拖动 key 值和固定项判断逻辑
				if (key == origin) return origin
				// if (this.list && this.list[key] && this.list[key].fixed) {
				// 	return this.l2r(key - 1, origin)
				// }else {
				return key
				// }
			},
			r2l(key, origin) { //倒序拖动 key 值和固定项判断逻辑
				if (key == origin) return origin
				// if (this.list && this.list[key] && this.list[key].fixed) {
				// 	return this.r2l(key + 1, origin)
				// } else {
				return key
				// }
			},
			//根据起始key和目标key去重新计算每一项的新的key
			insert(origin, end) {
				this.itemTransition = true
				let list
				if (origin < end) { // 正序拖动
					list = this.list.map(item => {
						// if (item.fixed) return item
						if (item.key > origin && item.key <= end) {
							item.key = this.l2r(item.key - 1, origin)
						} else if (item.key == origin) {
							item.key = end
						}
						return item
					})
					this.getPosition(list)
				} else if (origin > end) { // 倒序拖动
					list = this.list.map(item => {
						// if (item.fixed) return item
						if (item.key >= end && item.key < origin) {
							item.key = this.r2l(item.key + 1, origin)
						} else if (item.key == origin) {
							item.key = end
						}
						return item
					})
					this.getPosition(list)
				}
			},
			//根据当前的手指偏移量计算目标key
			calculateMoving(tranX, tranY) {
				let rows = Math.ceil(this.list.length / this.columns) - 1,
					i = Math.round(tranX / this.item.width),
					j = Math.round(tranY / this.item.height)
				i = i > this.columns - 1 ? this.columns - 1 : i
				i = i < 0 ? 0 : i
				j = j < 0 ? 0 : j
				j = j > rows ? rows : j
				let endKey = i + this.columns * j
				endKey = endKey >= this.list.length ? this.list.length - 1 : endKey
				return endKey
			},
			//监听列数变化, 如果改变重新初始化参数
			dataChange(newVal, oldVal) {
				setTimeout(() => {
					uni.pageScrollTo({
						scrollTop: 0,
						duration: 0
					})
					this.clearData()
					this.init()
				}, 0)
			},
			//点击每一项后触发事件
			itemClick(index, src) {
				// let urls = []
				// urls.push(src)
				// uni.previewImage({//点击查看大图
				// 	urls: 'http://picture.pangdly.com/'+ urls,
				// })
				// let item = this.list[index]
				// this.$emit('click', {
				// 	oldKey: index,
				// 	newKey: item.key,
				// 	data: item.data
				// })
			}
		}
	}
</script>

<style lang="scss" scoped>
	$mainColor: #3F82FD;
	$mainColorActive: rgba($mainColor, 0.7);
	$mainBd: #f7f7f7;
	$hoverBd: #eeeeee;
	$mainBlack1: #333333;
	$mainBlack2: #666666;
	$mainBlack3: #999999;
	$lineColor: #ebedf0;
	$shadow: 0 0 20rpx -5rpx rgba(0, 0, 0, 0.1);

	%clear-float {
		&:after {
			display: block;
			content: "clear";
			height: 0;
			clear: both;
			overflow: hidden;
			visibility: hidden;
		}
	}

	@mixin line($line: 1) {
		display: -webkit-box;
		-webkit-box-orient: vertical;
		-webkit-line-clamp: $line;
		overflow: hidden;
		word-break: break-all;
	}

	.item-wrap {
		position: relative;

		.item {
			position: absolute;
			width: 100%;
			z-index: 1;

			&.itemTransition {
				transition: transform 0.3s;
			}

			&.zIndex {
				z-index: 2;
			}

			&.cur {
				transition: initial;
			}

			&.fixed {
				z-index: 0 !important;
			}
		}
	}

	.info {
		position: relative;
		padding-top: 100%;
		background: #ffffff;

		&__item {
			position: absolute;
			border: 1rpx solid $lineColor;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
			overflow: hidden;
			padding: 10rpx;
			box-sizing: border-box;

			.image {
				width: 100%;
				height: 100%;
			}
		}

		&__item_btn {
			height: 50rpx;
			width: 50rpx;
			position: absolute;
			right: 12rpx;
			bottom: 12rpx;
			background-color: rgba($color: #000000, $alpha: 0.5);
			border-radius: 10rpx;
			color: #fff;
			text-align: center;
			line-height: 50rpx;
			font-size: 18rpx;
		}
	}

	.cell {
		display: flex;
		padding: 20rpx;
		border-bottom: 1rpx solid $lineColor;
		background: #ffffff;

		&__hd {
			font-size: 0;

			.image {
				width: 160rpx;
				height: 160rpx;
				margin-right: 20rpx;
				border-radius: 12rpx;
			}
		}

		&__bd {
			flex: 1;

			.name {
				@include line(2);
				font-size: 28rpx;
				margin-bottom: 12rpx;
			}

			.des {
				@include line(2);
				color: $mainBlack2;
				font-size: 24rpx;
			}
		}
	}
</style>
