啥也不说了直接上代码
<template><div><div class="block-panel"><canvas ref="canvas" class="canvas" @mousedown="beginningDraw" @mouseup="endingDraw"@mouseleave="drawLeave" @mousemove="drawing" v-show="!isSaved"></canvas><div class="picture" v-show="isSaved"><img :src="picture" alt="签名异常"></div><div class="btns-box"><div style="text-align: center"><el-button type="plain" @click="canvasUndo">撤销</el-button><el-button type="plain" @click="canvasRedo">反撤销</el-button></div><div style="text-align: center"><el-button type="primary" @click="canvasClear">清空</el-button><el-button type="primary" @click="signAgain">重新签</el-button><el-button type="primary" @click="saveAsImg">保存</el-button></div></div></div></div>
</template>
import axios from "axios"
export default {data(){return{// 图片路径picture: '',// 画布emptyCanvas: '',// 画布操作记录step: -1,canvas: '',// 画布操作历史canvasHistory: [],// 画布参数obj: {},// 是否已保存生成图片isSaved: false}},mounted(){this.init()this.canvasInit()},methods: {/*** 初始化画布数据*/init(){this.canvas = this.$refs.canvasthis.obj = {canvas: this.canvas,context: this.canvas.getContext("2d"),isWrite: false, //是否开始lastWriteTime: -1, lastWriteSpeed: 0, lastWriteWidth: 0,canvasWidth: 800, // canvas宽canvasHeight: 400, // canvase高bgColor: '#fff', // 背景色borderWidth: 2, // 网格线宽度borderColor: "#fff", //网格颜色 lastPoint: {}, //writeWidth: 2, //基础轨迹宽度maxWriteWidth: 30, // 写字模式最大线宽minWriteWidth: 1, // 写字模式最小线宽writeColor: '#000', // 轨迹颜色isWriteName: true // 签名模式}},/*** 设置画布线条宽度*/setLineWidth(){let nowTime = new Date().getTime();let diffTime = nowTime - this.obj.lastWriteTime;this.obj.lastWriteTime = nowTime;let returnNum = this.obj.minWriteWidth + (this.obj.maxWriteWidth - this.obj.minWriteWidth) * diffTime / 30;if(returnNum < this.obj.minWriteWidth) {returnNum = this.obj.minWriteWidth;} else if(returnNum > this.obj.maxWriteWidth) {returnNum = this.obj.maxWriteWidth;}returnNum = returnNum.toFixed(2);//写字模式和签名模式if(this.obj.isWriteName){this.obj.context.lineWidth = this.obj.writeWidth;}else{this.obj.context.lineWidth = this.obj.lastWriteWidth = this.obj.lastWriteWidth / 4 * 3 + returnNum / 4;}},/*** 画布中书写*/writing(point){this.obj.context.beginPath();this.obj.context.moveTo(this.obj.lastPoint.x, this.obj.lastPoint.y);this.obj.context.lineTo(point.x, point.y);this.setLineWidth();this.obj.context.stroke();this.obj.lastPoint = point;this.obj.context.closePath();},/*** 书写样式*/writeContextStyle() {this.obj.context.beginPath();this.obj.context.strokeStyle = this.obj.writeColor;this.obj.context.lineCap = 'round';this.obj.context.lineJoin = "round";},/*** 开始书写*/writeBegin(point) {this.obj.isWrite = true;this.obj.lastWriteTime = new Date().getTime();this.obj.lastPoint = point;this.writeContextStyle();},/*** 结束书写*/writeEnd(type = 0) {//新增记录if (type == 1) {this.step++;if (this.step < this.canvasHistory.length) {this.canvasHistory.length = this.step; // 截断数组}this.canvasHistory.push(this.canvas.toDataURL("image/png")); // 添加新的绘制到历史记录console.log(this.step);}//endthis.obj.isWrite = false;},/*** 清空画板*/canvasClear() {this.obj.context.strokeStyle = '#fff';this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);this.step = -1this.canvasHistory = []if(!this.obj.isWriteName) {this.obj.context.beginPath();var size = this.obj.borderWidth / 2;//画外面的框this.obj.context.moveTo(size, size);this.obj.context.lineTo(this.obj.canvasWidth - size, size);this.obj.context.lineTo(this.obj.canvasWidth - size, this.obj.canvasHeight - size);this.obj.context.lineTo(size, this.obj.canvasHeight - size);this.obj.context.closePath();this.obj.context.lineWidth = this.obj.borderWidth;this.obj.context.strokeStyle = this.obj.borderColor;this.obj.context.stroke();//画里面的框this.obj.context.moveTo(0, 0);this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight);this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight / 2);this.obj.context.lineTo(this.obj.canvasWidth, this.obj.canvasHeight / 2);this.obj.context.lineTo(0, this.obj.canvasHeight / 2);this.obj.context.lineTo(0, this.obj.canvasHeight);this.obj.context.lineTo(this.obj.canvasWidth, 0);this.obj.context.lineTo(this.obj.canvasWidth / 2, 0);this.obj.context.lineTo(this.obj.canvasWidth / 2, this.obj.canvasHeight);this.obj.context.stroke();}},/*** 保存图片 格式base64*/saveAsImg() {// 将cavase生成base64文件let imageSrc = this.canvas.toDataURL("image/png");if(imageSrc == this.emptyCanvas) {alert('请先书写')} else {this.isSaved = true// console.log('提交的内容===>', imageSrc)// 将base64转成file格式let blob = this.dataURLtoBlob(imageSrc);let file = this.blobToFile(blob, "imgName");// 将blob图片转化路径图片this.picture = window.URL.createObjectURL(file)console.log('this.picture', this.picture)let formData = new FormData()formData.append('file', this.picture)axios({method: 'post',url: '/user/12345',data: formData})}},/*** 初始化画布并备份*/canvasInit() {this.canvas.width = this.obj.canvasWidth;this.canvas.height = this.obj.canvasHeight;this.emptyCanvas = this.canvas.toDataURL("image/png");},/*** 撤销*/canvasUndo() {console.log(this.step)if (this.step >= 0) {this.step--;this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);if (this.step < 0) {return false}let canvasPic = new Image();canvasPic.src = this.canvasHistory[this.step];canvasPic.addEventListener('load', () => {this.obj.context.drawImage(canvasPic, 0, 0);});} else {console.log('不能再继续撤销了');}},/*** 反撤销*/canvasRedo() {if (this.step < this.canvasHistory.length - 1) {this.step++;let canvasPic = new Image();canvasPic.src = this.canvasHistory[this.step];canvasPic.addEventListener('load', () => {this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);this.obj.context.drawImage(canvasPic, 0, 0);});} else {console.log('已经是最新的记录了');}},setWidth(){//宽度铺满width = document.body.clientWidth;console.log(width);this.obj.canvasWidth =width;},beginningDraw(e){let point = {x: e.offsetX || e.clientX,y: e.offsetY || e.clientY};this.writeBegin(point);},endingDraw(e){let point = {x: e.offsetX,y: e.offsetY};this.writeEnd(1);},drawLeave(e){let point = {x: e.offsetX,y: e.offsetY};this.writeEnd();},drawing(e){if(this.obj.isWrite) {let point = {x: e.offsetX,y: e.offsetY};this.writing(point);}},signAgain(){this.isSaved = falsethis.picture = ''this.canvasClear()},dataURLtoBlob(dataurl){let arr = dataurl.split(','),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while(n--) {u8arr[n] = bstr.charCodeAt(n);}return new Blob([u8arr], {type: mime});},blobToFile(theBlob, fileName) {theBlob.lastModifiedDate = new Date().toLocaleDateString();theBlob.name = fileName;return theBlob;}}
}
.block-panel {display: flex;align-items: center;justify-content: space-between;&>.canvas {border: 1px solid #303133;}&>.picture{width: 800px;height: 400px;}&>.btns-box {padding-right: 20px;&>div{margin-bottom: 10px;}}
}