<template>
	<view class="cropper" id="cropper" :class="{ show: show }">
		<view class="cropper-head">
			<!-- <view class="cropper-btn cropper-reset" @tap="resetCrop">重做</view> -->
		</view>
		<view class="cropper-body">
			<image id="image" class="cropper-image" :src="imagePath" mode="aspectFit"></image>
			<view :style="{ width: stageWidth + 'px', height: stageHeight + 'px', left: stageLeft + 'px', top: stageTop + 'px' }" class="cropper-stage" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="touchMove">
				<view id="box" class="cropper-box" :style="{ width: boxWidth + 'px', height: boxHeight + 'px', left: boxLeft + 'px', top: boxTop + 'px' }">
					<view id="lt" class="lt"></view>
					<view id="lb" class="lb"></view>
					<view id="rt" class="rt"></view>
					<view id="rb" class="rb"></view>

					<view class="line-v" style="left:33.3%;"></view>
					<view class="line-v" style="left:66.6%;"></view>
					<view class="line-h" style="top:33.3%;"></view>
					<view class="line-h" style="top:66.6%;"></view>
				</view>
			</view>

			<canvas class="cropper-canvas" canvas-id="canvas" :style="{ height: canvasHeight + 'px', width: canvasWidth + 'px' }"></canvas>
		</view>
		<view class="cropper-bottom">
			<view class="cropper-btn cropper-cancel" @tap="cancelCrop">取消</view>
			<view class="cropper-btn cropper-ok" @tap="completeCrop">裁剪</view>
		</view>
	</view>
</template>
<script>
//无须渲染的变量

let layoutLeft = 0;
let layoutTop = 0;
let layoutWidth = 0;
let layoutHeight = 0;

let stageLeft = 0;
let stageTop = 0;
let stageWidth = 0;
let stageHeight = 0;

let imageWidth = 0;
let imageHeight = 0;

let pixelRatio = 1; //todo设备像素密度//暂不使用//

let imageStageRatio = 1; //图片实际尺寸与剪裁舞台大小的比值，用于尺寸换算。

let minBoxWidth = 0;
let minBoxHeight = 0;

let touchStartBoxLeft = 0;
let touchStartBoxTop = 0;
let touchStartBoxWidth = 0;
let touchStartBoxHeight = 0;

let touchStartX = 0;
let touchStartY = 0;

export default {
	name: 'cropper',
	props: {
		quality: {
			type: Number,
			default: 1
		},

		//目标文件的类型。默认值为jpg，jpg：输出jpg格式图片；png：输出png格式图片
		outputFileType: {
			type: String,
			default: 'jpg'
		},
		//目标图片的宽高比，默认null，即不限制剪裁宽高比。aspectRatio需大于0
		aspectRatio: {
			type: [Number, null],
			default: null
		},
		//最小剪裁尺寸与原图尺寸的比率，默认0.15，即宽度最小剪裁到原图的0.15宽。
		minBoxWidthRatio: {
			type: Number,
			default: 0.15
		},
		//同minBoxWidthRatio，当设置aspectRatio时，minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到。
		minBoxHeightRatio: {
			type: Number,
			default: 0.15
		},
		//剪裁框初始大小比率。默认值0.8，即剪裁框默认宽度为图片宽度的0.8倍。
		initialBoxWidthRatio: {
			type: Number,
			default: 0.8
		},
		//同initialBoxWidthRatio，当设置aspectRatio时，initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到。
		initialBoxHeightRatio: {
			type: Number,
			default: 0.8
		}
	},
	data() {
		return {
			//data
			stageLeft: 0,
			stageTop: 0,
			stageWidth: 0,
			stageHeight: 0,

			boxWidth: 0,
			boxHeight: 0,
			boxLeft: 0,
			boxTop: 0,

			canvasWidth: 0,
			canvasHeight: 0,
			show: false,
			imagePath: ''
		};
	},
	mounted() {
		// setTimeout(() => {
		// 	this.init();
		// }, 150);
	},
	methods: {
		resetCrop() {
			this.$emit('reset');
			this.init(this.imagePath);
		},
		cancelCrop() {
			this.$emit('cancel');
		},
		completeCrop() {
			uni.showLoading({
				mask: true,
				title: '图片处理中'
			});
			let imagePath = this.imagePath;
			let canvasContext = uni.createCanvasContext('canvas', this);

			let boxLeft = this.boxLeft;
			let boxTop = this.boxTop;
			let boxWidth = this.boxWidth;
			let boxHeight = this.boxHeight;

			let sx = Math.ceil(boxLeft * imageStageRatio);
			let sy = Math.ceil(boxTop * imageStageRatio);

			let sWidth = Math.ceil(boxWidth * imageStageRatio);
			let sHeight = Math.ceil(boxHeight * imageStageRatio);
			let dx = 0;
			let dy = 0;

			let dWidth = Math.ceil(sWidth * pixelRatio);
			let dHeight = Math.ceil(sHeight * pixelRatio);
			const param = {
				x: sx,
				y: sy,
				width: dWidth,
				height: dHeight,
				rotate: 0,
				scaleX: 1,
				scaleY: 1
			};

			canvasContext.drawImage(imagePath, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
			canvasContext.draw(false, () => {
				uni.canvasToTempFilePath(
					{
						x: dx,
						y: dy,
						width: dWidth,
						height: dHeight,
						destWidth: sWidth,
						destHeight: sHeight,
						canvasId: 'canvas',
						fileType: this.outputFileType,
						quality: this.quality,
						success: res => {
							this.$emit('complete', { param, path: res.tempFilePath,source:this.imagePath });
						}
					},
					this
				);
			});
		},
		touchMove(e) {
			let targetId = e.target.id;
			let touch = e.touches[0];
			let pageX = touch.pageX;
			let pageY = touch.pageY;

			let offsetX = pageX - touchStartX;
			let offsetY = pageY - touchStartY;

			if (targetId == 'box') {
				let newBoxLeft = touchStartBoxLeft + offsetX;
				let newBoxTop = touchStartBoxTop + offsetY;

				if (newBoxLeft < 0) {
					newBoxLeft = 0;
				}
				if (newBoxTop < 0) {
					newBoxTop = 0;
				}
				if (newBoxLeft + touchStartBoxWidth > stageWidth) {
					newBoxLeft = stageWidth - touchStartBoxWidth;
				}
				if (newBoxTop + touchStartBoxHeight > stageHeight) {
					newBoxTop = stageHeight - touchStartBoxHeight;
				}
				this.boxLeft = newBoxLeft;
				this.boxTop = newBoxTop;
			} else if (targetId == 'lt') {
				if (this.aspectRatio) {
					offsetY = offsetX / this.aspectRatio;
				}

				let newBoxLeft = touchStartBoxLeft + offsetX;
				let newBoxTop = touchStartBoxTop + offsetY;

				if (newBoxLeft < 0) {
					newBoxLeft = 0;
				}
				if (newBoxTop < 0) {
					newBoxTop = 0;
				}

				if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
					newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
				}
				if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
					newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
				}

				let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
				let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);

				//约束比例
				if (newBoxTop == 0 && this.aspectRatio && newBoxLeft != 0) {
					newBoxWidth = newBoxHeight * this.aspectRatio;
					newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
				}
				if (newBoxLeft == 0 && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
					newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
				}

				if (newBoxWidth == minBoxWidth && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
					newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
				}
				this.boxTop = newBoxTop;
				this.boxLeft = newBoxLeft;
				this.boxWidth = newBoxWidth;
				this.boxHeight = newBoxHeight;
			} else if (targetId == 'rt') {
				if (this.aspectRatio) {
					offsetY = -offsetX / this.aspectRatio;
				}

				let newBoxWidth = touchStartBoxWidth + offsetX;
				if (newBoxWidth < minBoxWidth) {
					newBoxWidth = minBoxWidth;
				}
				if (touchStartBoxLeft + newBoxWidth > stageWidth) {
					newBoxWidth = stageWidth - touchStartBoxLeft;
				}

				let newBoxTop = touchStartBoxTop + offsetY;

				if (newBoxTop < 0) {
					newBoxTop = 0;
				}

				if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
					newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
				}
				let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);

				//约束比例
				if (newBoxTop == 0 && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
					newBoxWidth = newBoxHeight * this.aspectRatio;
				}

				if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
					newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
				}

				if (newBoxWidth == minBoxWidth && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
					newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
				}

				this.boxTop = newBoxTop;
				this.boxHeight = newBoxHeight;
				this.boxWidth = newBoxWidth;
			} else if (targetId == 'lb') {
				if (this.aspectRatio) {
					offsetY = -offsetX / this.aspectRatio;
				}
				let newBoxLeft = touchStartBoxLeft + offsetX;

				if (newBoxLeft < 0) {
					newBoxLeft = 0;
				}
				if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
					newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
				}

				let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);

				let newBoxHeight = touchStartBoxHeight + offsetY;
				if (newBoxHeight < minBoxHeight) {
					newBoxHeight = minBoxHeight;
				}
				if (touchStartBoxTop + newBoxHeight > stageHeight) {
					newBoxHeight = stageHeight - touchStartBoxTop;
				}

				//约束比例
				if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxLeft != 0) {
					newBoxWidth = newBoxHeight * this.aspectRatio;
					newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
				}
				if (newBoxLeft == 0 && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
				}

				if (newBoxWidth == minBoxWidth && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
				}

				this.boxLeft = newBoxLeft;
				this.boxWidth = newBoxWidth;
				this.boxHeight = newBoxHeight;
			} else if (targetId == 'rb') {
				if (this.aspectRatio) {
					offsetY = offsetX / this.aspectRatio;
				}
				let newBoxWidth = touchStartBoxWidth + offsetX;
				if (newBoxWidth < minBoxWidth) {
					newBoxWidth = minBoxWidth;
				}
				if (touchStartBoxLeft + newBoxWidth > stageWidth) {
					newBoxWidth = stageWidth - touchStartBoxLeft;
				}

				let newBoxHeight = touchStartBoxHeight + offsetY;
				if (newBoxHeight < minBoxHeight) {
					newBoxHeight = minBoxHeight;
				}
				if (touchStartBoxTop + newBoxHeight > stageHeight) {
					newBoxHeight = stageHeight - touchStartBoxTop;
				}

				//约束比例
				if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
					newBoxWidth = newBoxHeight * this.aspectRatio;
				}

				if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
				}

				if (newBoxWidth == minBoxWidth && this.aspectRatio) {
					newBoxHeight = newBoxWidth / this.aspectRatio;
				}

				this.boxWidth = newBoxWidth;
				this.boxHeight = newBoxHeight;
			}
		},
		touchStart(e) {
			let touch = e.touches[0];
			let pageX = touch.pageX;
			let pageY = touch.pageY;

			touchStartX = pageX;
			touchStartY = pageY;

			touchStartBoxLeft = this.boxLeft;
			touchStartBoxTop = this.boxTop;
			touchStartBoxWidth = this.boxWidth;
			touchStartBoxHeight = this.boxHeight;
		},
		close(force=true) {
			this.show = false;
			if(force){
				this.imagePath = ''
			}
		},
		init(src) {
			if (!src) {
				return '';
			}
			this.imagePath = src;
			uni.showLoading({
				mask: true,
				title: '载入图片中'
			});
			uni.createSelectorQuery()
				.in(this)
				.select('.cropper-body')
				.boundingClientRect(rect => {
					layoutLeft = rect.left;
					layoutTop = rect.top;
					layoutWidth = rect.width;
					layoutHeight = rect.height;

					uni.getImageInfo({
						src: this.imagePath,
						success: imageInfo => {
							imageWidth = imageInfo.width;
							imageHeight = imageInfo.height;
							let imageWH = imageWidth / imageHeight;
							let layoutWH = layoutWidth / layoutHeight;
							if (imageWH >= layoutWH) {
								stageWidth = layoutWidth;
								stageHeight = stageWidth / imageWH;
								imageStageRatio = imageHeight / stageHeight;
							} else {
								stageHeight = layoutHeight;
								stageWidth = layoutHeight * imageWH;
								imageStageRatio = imageWidth / stageWidth;
							}
							stageLeft = (layoutWidth - stageWidth) / 2;
							stageTop = (layoutHeight - stageHeight) / 2;

							minBoxWidth = stageWidth * this.minBoxWidthRatio;
							minBoxHeight = stageHeight * this.minBoxHeightRatio;

							let boxWidth = stageWidth * this.initialBoxWidthRatio;
							let boxHeight = stageHeight * this.initialBoxHeightRatio;

							if (this.aspectRatio) {
								boxHeight = boxWidth / this.aspectRatio;
							}
							if (boxHeight > stageHeight) {
								boxHeight = stageHeight;
								boxWidth = boxHeight * this.aspectRatio;
							}

							let boxLeft = (stageWidth - boxWidth) / 2;
							let boxTop = (stageHeight - boxHeight) / 2;

							this.canvasWidth = imageWidth * pixelRatio;
							this.canvasHeight = imageHeight * pixelRatio;

							this.stageLeft = stageLeft;
							this.stageTop = stageTop;
							this.stageWidth = stageWidth;
							this.stageHeight = stageHeight;

							this.boxWidth = boxWidth;
							this.boxHeight = boxHeight;
							this.boxLeft = boxLeft;
							this.boxTop = boxTop;
							setTimeout(() => {
								uni.hideLoading();
								this.show = true;
							}, 100);
						},
						fail: () => {
							uni.showToast({
								icon: 'none',
								title: '图片载入失败'
							});
						}
					});
				})
				.exec();
		}
	}
};
</script>

<style lang="scss">
.cropper {
	position: fixed;
	left: 0;
	right: 0;
	top: 0;
	bottom: 0;
	background-color: #000;
	z-index: -1000000;
	opacity: 0;
	&.show {
		z-index: 999;
		opacity: 1;
	}
	.cropper-head {
		position: fixed;
		top: 0;
		width: 750rpx;
		z-index: 6;
		height: calc(var(--status-bar-height) + 88rpx);
		padding-top: var(--status-bar-height);
		display: flex;
		justify-content: flex-end;
		align-items: center;
	}
	.cropper-btn {
		height: 64rpx;
		margin: 0 20rpx;
		padding: 0 30rpx;
		line-height: 64rpx;
		color: #fff;
		font-size: 26rpx;
	}
	.cropper-body {
		margin: calc(var(--status-bar-height) + 88rpx) 30rpx 0 30rpx;
		height: calc(100vh - var(--status-bar-height) - 88rpx - 100rpx - var(--safe-area-inset-bottom));
		position: relative;
	}
	.cropper-bottom {
		height: calc(var(--safe-area-inset-bottom) + 100rpx);
		padding-top: var(--safe-area-inset-bottom);
		display: flex;
		align-items: center;
		justify-content: space-between;
		position: fixed;
		z-index: 6;
		width: 750rpx;
		bottom: 0;
	}
	.cropper-ok {
		color: #39f;
	}
	.cropper-image {
		position: absolute;
		width: 100%;
		height: 100%;
	}
	.cropper-stage {
		position: absolute;
		.cropper-box {
			position: absolute;
			border: 4rpx solid #ddd;
			box-sizing: border-box;
			box-shadow: 0 0 0 2000rpx rgba(0, 0, 0, 0.5);
			.lt {
				position: absolute;
				height: 48rpx;
				width: 48rpx;
				left: -6rpx;
				top: -6rpx;
				border-left: 12rpx solid #ffffff;
				border-top: 12rpx solid #ffffff;
			}

			.lb {
				position: absolute;
				height: 48rpx;
				width: 48rpx;
				left: -6rpx;
				bottom: -6rpx;
				border-left: 12rpx solid #ffffff;
				border-bottom: 12rpx solid #ffffff;
			}

			.rt {
				position: absolute;
				height: 48rpx;
				width: 48rpx;
				right: -6rpx;
				top: -6rpx;
				border-right: 12rpx solid #ffffff;
				border-top: 12rpx solid #ffffff;
			}

			.rb {
				position: absolute;
				height: 48rpx;
				width: 48rpx;
				right: -6rpx;
				bottom: -6rpx;
				border-right: 12rpx solid #ffffff;
				border-bottom: 12rpx solid #ffffff;
			}
			.line-v,
			.line-h {
				position: absolute;
				opacity: 0.5;
			}
			.line-v {
				width: 2rpx;
				border-left: 2rpx dashed #fff;
				height: 100%;
			}
			.line-h {
				height: 2rpx;
				border-bottom: 2rpx dashed #fff;
				width: 100%;
			}
		}
	}
	.cropper-canvas {
		position: fixed;
		background-color: red;
		left: 5000rpx;
	}
}

// 安全域兼容样式
// page {
// 	--safe-area-inset-top: 0px;
// 	--safe-area-inset-right: 0px;
// 	--safe-area-inset-bottom: 0px;
// 	--safe-area-inset-left: 0px;

// 	@supports (top: constant(safe-area-inset-top)) {
// 		--safe-area-inset-top: constant(safe-area-inset-top);
// 		--safe-area-inset-right: constant(safe-area-inset-right);
// 		--safe-area-inset-bottom: constant(safe-area-inset-bottom);
// 		--safe-area-inset-left: constant(safe-area-inset-left);
// 	}

// 	@supports (top: env(safe-area-inset-top)) {
// 		--safe-area-inset-top: env(safe-area-inset-top);
// 		--safe-area-inset-right: env(safe-area-inset-right);
// 		//--safe-area-inset-bottom: 12px;
// 		--safe-area-inset-bottom: env(safe-area-inset-bottom);
// 		--safe-area-inset-left: env(safe-area-inset-left);
// 	}
// }
</style>
