VUE使用Canvas元素实现手绘签名(支持重新签名+撤回一笔)
效果:

代码实现:
(vue2.0语法)
<template> <div class="wrap"> <div ref="signatureArea" class="signature" @mousedown="startDrawing" @mousemove="drawing" @mouseup="stopDrawing"></div> <div class="btn_group"> <button class="btn" style="background: #1e9fff" @click="handleSave">确认完成</button> <button class="btn" style="background: #009688" @click="handleClearUp">撤回一笔</button> <button class="btn" style="background: #ff5722" @click="handleClear">重新签名</button> <button class="btn" style="background: #009688" @click="handleSave1">手动上传</button> </div> </div> </template> <script> export default { name: "signPage", data() { return { drawingCanvas: "", backgroundCanvas: "", drawingCtx: "", backgroundCtx: "", isDrawing: false, lastX: null, lastY: null, penstyle: { w: 5, color: "black" }, //画笔样式 w:画笔粗细,像素 color:颜色 undoList: [], // 用于保存所有操作,用于撤销和重做 //redoList: [], // 用于保存所有撤销的操作,用于重做 currentPath: [] //当次绘画操作记录 }; }, created() { // 创建两个canvas元素 this.drawingCanvas = document.createElement("canvas"); // 用于绘制签名 this.backgroundCanvas = document.createElement("canvas"); // 用于保存带有白色背景的签名图像 }, mounted() { this.$nextTick(() => { this.init(); //解决初次打开页面后触发后未正常显示线条问题(dom未加载完毕,canvas未获取到宽高导致) }); }, methods: { init() { this.undoList = []; this.drawingCtx = this.drawingCanvas.getContext("2d"); // 获取绘制画布的上下文对象 this.backgroundCtx = this.backgroundCanvas.getContext("2d"); // 获取背景画布的上下文对象 this.drawingCanvas.width = this.$refs.signatureArea.offsetWidth; //画布宽 this.drawingCanvas.height = parseInt((this.$refs.signatureArea.offsetWidth / 16.0) * 9); //画布高 16:9 this.$refs.signatureArea.style.background = "white"; //签名区域背景色 this.backgroundCanvas.width = this.drawingCanvas.width; this.backgroundCanvas.height = this.drawingCanvas.height; this.$refs.signatureArea.appendChild(this.drawingCanvas); }, //鼠标按下 startDrawing(e) { e.preventDefault(); // 阻止默认事件 var touch = e; // 获取触摸点坐标 var rect = this.$refs.signatureArea.getBoundingClientRect(); // 获取签名区域的位置和大小 this.lastX = touch.clientX - rect.left; this.lastY = touch.clientY - rect.top; this.isDrawing = true; this.currentPath = { index: this.undoList.length + 1, points: [{ x: this.lastX, y: this.lastY }] }; //存储当前次的绘制信息(此时存储的points可以看做是此次笔的起始落脚点位置) }, //开始绘画 drawing(e) { if (!this.isDrawing) return; var touch = e; var rect = this.$refs.signatureArea.getBoundingClientRect(); var x = touch.clientX - rect.left; var y = touch.clientY - rect.top; this.currentPath.points.push({ x: x, y: y }); //鼠标不停地滑动,不停地记录此次滑动过的位置 this.drawLine(this.lastX, this.lastY, x, y); //鼠标滑过位置,绘制显示出实线 this.lastX = x; //不停滑动,此值要不停更新,旨在每一次移动都需要更新起点,即下一笔的起点是上一笔画的终点位置 this.lastY = y; }, // 绘制实线 drawLine(x1, y1, x2, y2) { this.drawingCtx.beginPath(); // 开始一条新的路径 this.drawingCtx.moveTo(x1, y1); // 将画笔移动到起点 this.drawingCtx.lineTo(x2, y2); // 绘制一条直线到终点 this.drawingCtx.lineWidth = this.penstyle.w; // 设置线条的宽度为1像素 this.drawingCtx.strokeStyle = this.penstyle.color; // 设置线条颜色为黑色 this.drawingCtx.stroke(); // 绘制线条 }, //停止绘画 (鼠标松开事件) stopDrawing(e) { this.isDrawing = false; this.undoList.push(this.currentPath); }, //重新签名 handleClear() { // 清除签名 this.drawingCtx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height); // 清除绘制画布的内容 this.backgroundCtx.clearRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height); // 清除背景画布的内容 }, //撤销 handleClearUp() { // 从栈中弹出最后一个状态,并重置 canvas if (this.undoList.length > 0) { const lastPath = this.undoList.pop(); //删除最后一笔画 //this.redoList.push(lastPath); //记录下来撤销了哪些内容 this.drawingCtx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height); //清空画布 this.drawPaths(this.undoList);//将记录的笔画记录重新显示画出来 } }, // 画所有的路径 drawPaths(paths) { paths.forEach(path => { this.drawingCtx.beginPath(); this.drawingCtx.strokeStyle = this.penstyle.color; this.drawingCtx.lineWidth = this.penstyle.w; this.drawingCtx.moveTo(path.points[0].x, path.points[0].y); path.points.slice(1).forEach(point => { this.drawingCtx.lineTo(point.x, point.y); //console.error(point.x, point.y); }); this.drawingCtx.stroke(); }); }, //保存签名 handleSave() { // 绘制白色背景 this.backgroundCtx.fillStyle = "white"; this.backgroundCtx.fillRect(0, 0, this.backgroundCanvas.width, this.backgroundCanvas.height); // 复制绘制的签名到带有白色背景的画布 this.backgroundCtx.drawImage(this.drawingCanvas, 0, 0); // 将带有白色背景的画布内容转为PNG格式的DataURL var dataURL = this.backgroundCanvas.toDataURL("image/png"); //这里的dataURL是一个base64字符串,也可以直接放到img的src里面展示 let file = this.base64ImgtoFile(dataURL, "手写签名" + this.randomString(12)); //base64转file //手写签名图片文件传递给父组件 this.$emit("signatruefile", { raw: file, size: file.size, name: "手写签名" + this.randomString(12) + ".png", uid: this.randomString(12) }); // 创建一个链接元素并设置下载属性 // var link = document.createElement("a"); // var time = new Date().getTime(); // link.href = dataURL; // link.download = "签名" + time + ".png"; // 设置下载文件的名称 // link.style.display = "none"; // document.body.appendChild(link); // link.click(); // document.body.removeChild(link); },
//手动上传签名图片回到给父组件事件 handleSave1() { this.$emit("handleSave"); }, //base64转file base64ImgtoFile(dataurl, filename = "file") { let arr = dataurl.split(","); let mime = arr[0].match(/:(.*?);/)[1]; let suffix = mime.split("/")[1]; let bstr = atob(arr[1]); let n = bstr.length; let u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], `${filename}.${suffix}`, { type: mime }); }, randomString(len) { len = len || 32; let timestamp = new Date().getTime(); /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/ let $chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"; let maxPos = $chars.length; let randomStr = ""; for (let i = 0; i < len; i++) { randomStr += $chars.charAt(Math.floor(Math.random() * maxPos)); } return randomStr + timestamp; } } }; </script> <style scoped> .wrap { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; } .btn_group { margin-top: 16px; } .btn { width: 140px; line-height: 38px; text-align: center; font-weight: bold; letter-spacing: 2px; color: #fff; text-shadow: 1px 1px 1px #333; border-radius: 5px; margin: 0 20px 20px 20px; /* border: 1px solid #b42323; */ background-color: rgb(207, 41, 41); cursor: pointer; } .signature { width: 100%; border: 1px solid #868080; } </style>
本文来自博客园,作者:じ逐梦,转载请注明原文链接:https://www.cnblogs.com/ZhuMeng-Chao/p/18599924

浙公网安备 33010602011771号