朱丽叶

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

canvas实现签字功能

<template>
	<view class="container">
		<view class="sigh-btns">
			<button class="btn" @tap="handleReset">重写</button>
			<button class="btn" @tap="handleConfirm">确认</button>
		</view>
		<view class="sign-box">
			<canvas class="mycanvas" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove"
				@touchend="touchend"></canvas>
		</view>
		<image :src="tesImg" mode=""></image>
	</view>
</template>

<script>
	export default {
		onLoad() {
			this.ctx = uni.createCanvasContext("mycanvas", this); //创建绘图对象
			//设置画笔样式
			this.ctx.lineWidth = 4;
			this.ctx.lineCap = "round"
			this.ctx.lineJoin = "round"
		},
		data() {
			return {
				ctx: '', //绘图图像
				points: [], //路径点集合
				tempPoint: [], //判断是否书写过
				tesImg: ""
			};
		},
		methods: {
			//触摸开始,获取到起点
			touchstart(e) {
				console.log(e);
				let startX = e.changedTouches[0].x;
				let startY = e.changedTouches[0].y;
				let startPoint = {
					X: startX,
					Y: startY
				};
				/* **************************************************
				    #由于uni对canvas的实现有所不同,这里需要把起点存起来
				 * **************************************************/
				this.points.push(startPoint);
				//每次触摸开始,开启新的路径
				this.ctx.beginPath();
			},
			//触摸移动,获取到路径点
			touchmove(e) {
				let moveX = e.changedTouches[0].x;
				let moveY = e.changedTouches[0].y;
				let movePoint = {
					X: moveX,
					Y: moveY
				};
				this.points.push(movePoint); //存点
				let len = this.points.length;
				if (len >= 2) {
					this.draw(); //绘制路径
				}
				this.tempPoint.push(movePoint);
			},
			// 触摸结束,将未绘制的点清空防止对后续路径产生干扰
			touchend() {
				this.points = [];
			},
			/* ***********************************************
			#   绘制笔迹
			#   1.为保证笔迹实时显示,必须在移动的同时绘制笔迹
			#   2.为保证笔迹连续,每次从路径集合中区两个点作为起点(moveTo)和终点(lineTo)
			#   3.将上一次的终点作为下一次绘制的起点(即清除第一个点)
			************************************************ */
			draw() {
				let point1 = this.points[0]
				let point2 = this.points[1]
				this.points.shift()
				this.ctx.moveTo(point1.X, point1.Y)
				this.ctx.lineTo(point2.X, point2.Y)
				this.ctx.stroke()
				this.ctx.draw(true)
			},
			//清空画布
			handleReset() {
				uni.getSystemInfo({
					success: (res) => {
						let canvasw = res.windowWidth;
						let canvash = res.windowHeight;
						this.ctx.clearRect(0, 0, canvasw, canvash);
						this.ctx.draw(true);
					},
				})
				this.tempPoint = [];
			},
			//将签名笔迹存到本地
			handleConfirm() {
				if (this.tempPoint.length == 0) {
					uni.showToast({
						title: '请先签名',
						icon: 'none',
						duration: 2000
					});
					return;
				}
				uni.canvasToTempFilePath({
					canvasId: 'mycanvas',
					success: (res) => {
						console.log("保存的信息", res);
						this.clearImageEdgeBlank(res.tempFilePath)
						.then(path => {
							this.tesImg = path
						})
					}
				})
			},
			// 裁剪生成的图片
			/**
			 * 清楚图片周围空白区域
			 * @param {string} url - 图片地址或base64
			 * @param {number} [padding=0] - 内边距
			 * @return {string} base64 - 裁剪后的图片字符串
			 */
			clearImageEdgeBlank(url, padding = 0) {
				return new Promise((resolve, reject) => {
					// create canvas
					const canvas = document.createElement("canvas");
					const ctx = canvas.getContext("2d");

					// create image
					const image = new Image();
					image.onload = draw;
					image.src = url;
					image.crossOrigin = "Anonymous";

					function draw() {
						canvas.width = image.width;
						canvas.height = image.height;

						ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
						const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
						const {
							data,
							width,
							height
						} = imageData;

						// 裁剪需要的起点和终点,初始值为画布左上和右下点互换设置成极限值。
						let startX = width,
							startY = height,
							endX = 0,
							endY = 0;

						/*
						col为列,row为行,两层循环构造每一个网格,
						便利所有网格的像素,如果有色彩则设置裁剪的起点和终点
						*/
						for (let col = 0; col < width; col++) {
							for (let row = 0; row < height; row++) {
								// 网格索引
								const pxStartIndex = (row * width + col) * 4;

								// 网格的实际像素RGBA
								const pxData = {
									r: data[pxStartIndex],
									g: data[pxStartIndex + 1],
									b: data[pxStartIndex + 2],
									a: data[pxStartIndex + 3]
								};

								// 存在色彩:不透明
								const colorExist = pxData.a !== 0;

								/*
								如果当前像素点有色彩
								startX坐标取当前col和startX的最小值
								endX坐标取当前col和endX的最大值
								startY坐标取当前row和startY的最小值
								endY坐标取当前row和endY的最大值
								*/
								if (colorExist) {
									startX = Math.min(col, startX);
									endX = Math.max(col, startX);
									startY = Math.min(row, startY);
									endY = Math.max(row, endY);
								}
							}
						}

						// 右下坐标需要扩展1px,才能完整的截取到图像
						endX += 1;
						endY += 1;

						// 加上padding
						startX -= padding;
						startY -= padding;
						endX += padding;
						endY += padding;

						// 根据计算的起点终点进行裁剪
						const cropCanvas = document.createElement("canvas");
						const cropCtx = cropCanvas.getContext("2d");
						cropCanvas.width = endX - startX;
						cropCanvas.height = endY - startY;
						cropCtx.drawImage(
							image,
							startX,
							startY,
							cropCanvas.width,
							cropCanvas.height,
							0,
							0,
							cropCanvas.width,
							cropCanvas.height
						);
						// rosolve裁剪后的图像字符串
						resolve(cropCanvas.toDataURL());
					}
				});
			}

		}
	}
</script>

<style lang="scss" scoped>
	.mycanvas {
		height: 600rpx;
		width: 100%;
		background-color: #fff;
	}
</style>

posted on   朱丽叶  阅读(347)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
点击右上角即可分享
微信分享提示