何も言わずにコードに直接アクセスしてください
<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.canvas
this.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);
}
//end
this.obj.isWrite = false;
},
/**
* 清空画板
*/
canvasClear() {
this.obj.context.strokeStyle = '#fff';
this.obj.context.clearRect(0, 0, this.obj.canvasWidth, this.obj.canvasHeight);
this.step = -1
this.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 = false
this.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;
}
}
}