<template>
	<div class="canvas-main">
		<canvas :ref="domId" class="app-sign-canvas" :id="domId" @mousedown.prevent.stop="handleMousedown"
		 @mousemove.prevent.stop="handleMousemove" @mouseup.prevent.stop="handleMouseup" @mouseleave.prevent.stop="handleMouseleave"
		 @touchstart.prevent.stop="handleTouchstart" @touchmove.prevent.stop="handleTouchmove" @touchend.prevent.stop="handleTouchend">
			您的浏览器不支持canvas技术,请升级浏览器!
		</canvas>
		<div class="float-text" @mousedown.prevent.stop="move">
			{{positionX}}
			        {{positionY}}
		</div>
	</div>

</template>
<script>
	export default {
		name: 'BaseCanvas',
		model: {
			value: 'image',
			event: 'confirm'
		},
		props: {
			image: {
				required: false,
				type: [String],
				default: null
			},

			options: { //配置项
				required: false,
				type: [Object],
				default: () => null
			},

		},
		data() {
			return {
				positionX:0,
				positionY:0,
				value: null, //base64
				domId: `sign-canvas-${Math.random().toString(36).substr(2)}`, //生成唯一dom标识
				canvas: null, //canvas dom对象
				context: null, //canvas 画布对象
				dpr: 1,
				config: {
					isDpr: false, //是否使用dpr兼容高分屏 [Boolean] 可选
					lastWriteSpeed: 1, //书写速度 [Number] 可选
					lastWriteWidth: 2, //下笔的宽度 [Number] 可选
					lineCap: 'round', //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square]	正方形线帽
					lineJoin: 'round', //线条交汇时边角的类型  [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
					canvasWidth: 600, //canvas宽高 [Number] 可选
					canvasHeight: 600, //高度  [Number] 可选
					isShowBorder: true, //是否显示边框 [可选]
					bgColor: '#fcc', //背景色 [String] 可选 注:这背景色仅仅只是canvas背景,保存的图片仍然是透明的
					borderWidth: 1, // 网格线宽度  [Number] 可选
					borderColor: "#ff787f", //网格颜色  [String] 可选
					writeWidth: 5, //基础轨迹宽度  [Number] 可选
					maxWriteWidth: 30, // 写字模式最大线宽  [Number] 可选
					minWriteWidth: 5, // 写字模式最小线宽  [Number] 可选
					writeColor: '#101010', // 轨迹颜色  [String] 可选
					isSign: false, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
					imgType: 'png' //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
				},
			};
		},
		methods: {
			init() {
				const options = this.options;
				if (options) {
					for (const key in options) {
						this.config[key] = options[key];
					}
				}
				this.dpr = typeof window !== 'undefined' && this.config.isDpr ? window.devicePixelRatio || window.webkitDevicePixelRatio ||
					window.mozDevicePixelRatio || 1 : 1;
				console.log(this.domId)
				this.$nextTick(() => {
					this.canvas = document.getElementById(this.domId);
					console.log(this.canvas)
					this.context = this.canvas.getContext("2d");
					this.canvas.style.background = this.config.bgColor;
					this.canvas.height = this.config.canvasWidth;
					this.canvas.width = this.config.canvasHeight;
					this.canvasInit();
					this.canvasClear();
				})

			},

			move(e) {
				let odiv = e.target; //获取目标元素

				//算出鼠标相对元素的位置
				let disX = e.clientX - odiv.offsetLeft;
				let disY = e.clientY - odiv.offsetTop;
				document.onmousemove = (e) => { //鼠标按下并移动的事件
					//用鼠标的位置减去鼠标相对元素的位置，得到元素的位置
					let left = e.clientX - disX;
					let top = e.clientY - disY;

					//绑定元素位置到positionX和positionY上面
					this.positionX = top;
					this.positionY = left;

					//移动当前元素
					odiv.style.left = left + 'px';
					odiv.style.top = top + 'px';
				};
				document.onmouseup = (e) => {
					document.onmousemove = null;
					document.onmouseup = null;
				};
			},

			/* 绘制输入的文本 */
			doAddText(val) {
				let context = this.context
				context.fillStyle = "red";
				context.textAlign = 'left';
				context.font = '40px Helvetica';
				context.fillText(val, 40, 40);
			},

			/**
			 * 轨迹宽度
			 */
			setLineWidth() {
				const nowTime = new Date().getTime();
				const diffTime = nowTime - this.config.lastWriteTime;
				this.config.lastWriteTime = nowTime;
				let returnNum = this.config.minWriteWidth + (this.config.maxWriteWidth - this.config.minWriteWidth) * diffTime / 30;
				if (returnNum < this.config.minWriteWidth) {
					returnNum = this.config.minWriteWidth;
				} else if (returnNum > this.config.maxWriteWidth) {
					returnNum = this.config.maxWriteWidth;
				}
				returnNum = returnNum.toFixed(2);
				//写字模式和签名模式
				if (this.config.isSign) {
					this.context.lineWidth = this.config.writeWidth * this.dpr;
				} else {
					const lineWidth = this.config.lastWriteWidth = this.config.lastWriteWidth / 4 * 3 + returnNum / 4
					this.context.lineWidth = lineWidth * this.dpr;
				}
			},

			/**
			 * 写开始
			 */
			writeBegin(point) {
				this.config.isWrite = true;
				this.config.lastWriteTime = new Date().getTime();
				this.config.lastPoint = point;
				this.writeContextStyle();
			},

			/**
			 * 绘制轨迹
			 */
			writing(point) {
				this.context.beginPath();
				this.context.moveTo(this.config.lastPoint.x * this.dpr, this.config.lastPoint.y * this.dpr);
				this.context.lineTo(point.x * this.dpr, point.y * this.dpr);
				this.setLineWidth();
				this.context.stroke();
				this.config.lastPoint = point;
				this.context.closePath();
			},

			/**
			 * 写结束
			 */
			writeEnd(point) {
				this.config.isWrite = false;
				this.config.lastPoint = point;
			},

			/**
			 * 轨迹样式
			 */
			writeContextStyle() {
				this.context.beginPath();
				this.context.strokeStyle = this.config.writeColor;
				this.context.lineCap = this.config.lineCap;
				this.context.lineJoin = this.config.lineJoin;
			},


			/**
			 * 清空画板
			 */
			canvasClear() {
				this.context.save();
				this.context.strokeStyle = this.config.writeColor;
				this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
				this.context.beginPath();
				this.context.lineWidth = this.config.borderWidth * this.dpr;
				this.context.strokeStyle = this.config.borderColor;
				let size = this.config.borderWidth / 2 * this.dpr;
				if (this.config.isShowBorder) {
					//画外面的框
					this.context.moveTo(size, size);
					this.context.lineTo(this.canvas.width - size, size);
					this.context.lineTo(this.canvas.width - size, this.canvas.height - size);
					this.context.lineTo(size, this.canvas.height - size);
					this.context.closePath();
					this.context.stroke();
				}
				if (this.config.isShowBorder && !this.config.isSign) {
					//画里面的框
					this.context.moveTo(0, 0);
					this.context.lineTo(this.canvas.width, this.canvas.height);
					this.context.lineTo(this.canvas.width, this.canvas.height / 2);
					this.context.lineTo(0, this.canvas.height / 2);
					this.context.lineTo(0, this.canvas.height);
					this.context.lineTo(this.canvas.width, 0);
					this.context.lineTo(this.canvas.width / 2, 0);
					this.context.lineTo(this.canvas.width / 2, this.canvas.height);
					this.context.stroke();
				}
				this.$emit('confirm', null);
				this.context.restore();
			},

			/**
			 *  保存图片 格式base64
			 */
			async saveAsImg() {
				return new Promise(async (r, j) => {
					const image = new Image();
					let compressImg = await this.$editorTools.compressImg(this.canvas.toDataURL(`image/${this.config.imgType}`),
						this.canvas.width, this.canvas.height)
					r({
						base64: compressImg,
						width: this.canvas.width,
						height: this.canvas.height
					})
				})

			},

			/**
			 * 初始化画板
			 */
			canvasInit() {
				this.canvas.width = this.config.canvasWidth;
				this.canvas.height = this.config.canvasHeight;
				this.canvas.style.width = `${this.config.canvasWidth}px`;
				this.canvas.style.height = `${this.config.canvasHeight}px`;
				this.config.emptyCanvas = this.canvas.toDataURL(`image/${this.config.imgType}`);
				console.log(this.canvas.width, this.canvas.height)
			},

			/**
			 * 鼠标按下 => 下笔
			 */
			handleMousedown(e) {
				console.log(e)
				this.writeBegin({
					x: e.offsetX || e.clientX,
					y: e.offsetY || e.clientY
				});
			},

			/**
			 * 书写过程 => 下笔书写
			 */
			handleMousemove(e) {
				this.config.isWrite && this.writing({
					x: e.offsetX,
					y: e.offsetY
				});
			},

			/**
			 * 鼠标松开 => 提笔
			 */
			handleMouseup(e) {
				this.writeEnd({
					x: e.offsetX,
					y: e.offsetY
				});
			},

			/**
			 * 离开书写区域 => 提笔离开
			 */
			handleMouseleave(e) {
				this.config.isWrite = false;
				this.config.lastPoint = {
					x: e.offsetX,
					y: e.offsetY
				};
			},

			/* ==========================移动端兼容=Start================================ */

			/**
			 * 手指按下 => 下笔
			 */
			handleTouchstart(e) {
				const touch = e.targetTouches[0];
				const x = touch.clientX ? touch.clientX - this.getRect().left : touch.pageX - this.offset(touch.target, 'left');
				const y = touch.clientY ? touch.clientY - this.getRect().top : touch.pageY - this.offset(touch.target, 'top');
				this.writeBegin({
					x,
					y
				});
			},

			/**
			 * 手指移动 => 下笔书写
			 */
			handleTouchmove(e) {
				const touch = e.targetTouches[0];
				const x = touch.clientX ? touch.clientX - this.getRect().left : touch.pageX - this.offset(touch.target, 'left');
				const y = touch.clientY ? touch.clientY - this.getRect().top : touch.pageY - this.offset(touch.target, 'top');
				this.config.isWrite && this.writing({
					x,
					y
				});
			},

			/**
			 * 手指移动结束 => 提笔离开
			 */
			handleTouchend(e) {
				const tcs = e.targetTouches;
				const ccs = e.changedTouches;
				const touch = tcs && tcs.length && tcs[0] || ccs && ccs.length && ccs[0];
				const x = touch.clientX ? touch.clientX - this.getRect().left : touch.pageX - this.offset(touch.target, 'left');
				const y = touch.clientY ? touch.clientY - this.getRect().top : touch.pageY - this.offset(touch.target, 'top');
				this.writeEnd({
					x,
					y
				});
			},

			/* ==========================移动端兼容=End================================== */

			/**
			 * 下载二维码到本地
			 */
			downloadSignImg(name) {
				const c = document.getElementById(this.domId);
				const dataURL = c.toDataURL('image/png');
				this.saveFile(dataURL, name ? `${name}.${this.config.imgType}` : `${Date.parse(new Date())}.${this.config.imgType}`);
			},

			/**
			 * 保存文件
			 */
			saveFile(data, filename) {
				const saveLink = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
				saveLink.href = data;
				saveLink.download = filename;
				const event = document.createEvent('MouseEvents');
				event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
				saveLink.dispatchEvent(event);
			},

			/**
			 * 获取画布的元素的大小及其相对于视口的位置
			 * @return {}
			 */
			getRect() {
				return this.$refs[this.domId].getBoundingClientRect();
			},

			/**
			 * 获取dom对象的偏移量 可以获取解决position定位的问题
			 * @returns number
			 */
			offset(obj, direction) {
				//将top,left首字母大写,并拼接成offsetTop,offsetLeft
				const offsetDir = 'offset' + direction[0].toUpperCase() + direction.substring(1);
				let realNum = obj[offsetDir];
				let positionParent = obj.offsetParent; //获取上一级定位元素对象
				while (positionParent != null) {
					realNum += positionParent[offsetDir];
					positionParent = positionParent.offsetParent;
				}
				return realNum;
			}
		},
		mounted() {
			this.init();
		},
		
		watch: {
			'options.writeWidth': {
				handler(n) {
					this.context.lineWidth = n
					this.config.writeWidth = n
				},
				deep: true
			},
			'options.writeColor': {
				handler(n) {
					this.context.strokeStyle = n
					this.config.writeColor = n
				},
				deep: true
			},
			'options.canvasWidth': {
				handler(n) {
					this.canvas.width = n
					this.config.canvasWidth = n
					this.canvasInit();
				},
			},
			'options.canvasHeight': {
				handler(n) {
					this.canvas.height = n
					this.config.canvasHeight = n
					this.canvasInit();
				},
			},
		},
	};
</script>
<style lang="less" scoped>
	.canvas-main {
		position: relative;

		.float-text {
			position: absolute;
			font-size: 20px;
			color: red;
			cursor: pointer;
			user-select: none;
		}
	}
</style>
