vue-signature-pad插件实现移动端签字功能(css,js)+将签名照片旋转90度之后的base64码传给后端
效果图
代码
<template>
<div class="sign-finish">
<NavBar class="navBar" title="电子签名"></NavBar>
<div class="wrap">
<div class="actionsWrap">
<div class="actions">
<van-button plain @click="cancel" type="info">取消</van-button>
<van-button
class="action-button"
@click="submit"
type="info"
:loading="isLoading"
>提交</van-button
>
</div>
</div>
<div></div>
<div class="topbtn">
<img
src="@/assets/images/workApplyImg/cancel.png"
alt="Logo"
class="logo"
@click="undo"
/><a @click="undo">撤销</a
><img
src="@/assets/images/workApplyImg/write.png"
alt="Logo"
class="logo"
@click="clearSignature"
/><a @click="clearSignature">重写</a>
</div>
<div class="content">请在空白区域内手写签名确认</div>
<VueSignaturePad
class="canvas"
ref="signaturePad"
:options="options"
@begin="handleBegin"
@end="handleEnd"
></VueSignaturePad>
</div>
</div>
</template>
<script>
import { VueSignaturePad } from "vue-signature-pad";
import NavBar from "@/components/NavBar";
import { signatureApi } from "@/api/signature";
import { Toast, Dialog } from "vant";
import { useUserStore } from "@/store/user";
import dayjs from "dayjs";
export default {
components: {
VueSignaturePad,
NavBar,
},
data() {
return {
isLoading: false,
imgSrc: "",
searchData: {},
options: {
// 这里可以配置signature_pad的选项,例如宽高、背景色等
penColor: "rgb(0, 0, 0)", // 笔的颜色
penWidth: 2, // 笔的宽度
backgroundColor: "#F4F6FA", // 背景颜色
canvasWidth: 600, // canvas的宽度
canvasHeight: 300, // canvas的高度
rotate: -90,
},
};
},
created() {
const { userInfo } = useUserStore();
this.searchData.loginAccount = userInfo.empCode;
this.searchData.loginName = userInfo.empName;
},
methods: {
handleBegin() {
console.log("开始签名");
},
handleEnd() {
console.log("结束签名");
},
// 撤销
undo() {
this.$refs.signaturePad.undoSignature();
},
// 取消
cancel() {
this.$router.push({
path: "/main/home",
});
},
// 清除
clearSignature() {
this.$refs.signaturePad.clearSignature();
},
saveSignature() {
const data = this.$refs.signaturePad.save(); // 保存签名数据到dataURL或base64字符串中
},
clsoePage(res) {
Dialog.confirm({
message: res.message,
confirmButtonText: "关闭页面",
type: "success",
center: true,
showCancelButton: false,
}).then(() => {
this.cancel();
});
},
// 提交
async submit() {
this.isLoading = true;
const { isEmpty, data } = this.$refs.signaturePad.saveSignature();
if (isEmpty) {
this.isLoading = false;
Toast("签名为空,请书写后再进行添加");
return;
}
await this.rotateBase64Img(data, -90, (res) => {
this.imgSrc = res.split(",");
const parasms = {
signId: this.$route.query.fileid,
signPhoto: this.imgSrc[1],
jackdaw_id: this.$route.query.jackdaw_id,
qsdt: dayjs().format("YYYY-MM-DD"),
...this.searchData,
};
let res = signatureApi.signatureSub(parasms);
if (res.statusCode === 200) {
this.clsoePage(res);
this.isLoading = false;
} else {
this.$notify({ type: "danger", message: res });
this.isLoading = false;
}
});
},
// 将写完的签名图片顺时针旋转90度,拿到旋转之后base64码传给后端
rotateBase64Img(src, edg, callback) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var imgW; //图片宽度
var imgH; //图片高度
var size; //canvas初始大小
if (edg % 90 != 0) {
console.error("旋转角度必须是90的倍数!");
throw "旋转角度必须是90的倍数!";
}
edg < 0 && (edg = (edg % 360) + 360);
const quadrant = (edg / 90) % 4; //旋转象限
const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 }; //裁剪坐标
var image = new Image();
image.crossOrigin = "anonymous";
image.src = src;
image.onload = function () {
imgW = image.width;
imgH = image.height;
size = imgW > imgH ? imgW : imgH;
canvas.width = size * 2;
canvas.height = size * 2;
switch (quadrant) {
case 0:
cutCoor.sx = size;
cutCoor.sy = size;
cutCoor.ex = size + imgW;
cutCoor.ey = size + imgH;
break;
case 1:
cutCoor.sx = size - imgH;
cutCoor.sy = size;
cutCoor.ex = size;
cutCoor.ey = size + imgW;
break;
case 2:
cutCoor.sx = size - imgW;
cutCoor.sy = size - imgH;
cutCoor.ex = size;
cutCoor.ey = size;
break;
case 3:
cutCoor.sx = size;
cutCoor.sy = size - imgW;
cutCoor.ex = size + imgH;
cutCoor.ey = size + imgW;
break;
}
ctx.translate(size, size);
ctx.rotate((edg * Math.PI) / 180);
ctx.drawImage(image, 0, 0);
var imgData = ctx.getImageData(
cutCoor.sx,
cutCoor.sy,
cutCoor.ex,
cutCoor.ey
);
if (quadrant % 2 == 0) {
canvas.width = imgW;
canvas.height = imgH;
} else {
canvas.width = imgH;
canvas.height = imgW;
}
ctx.putImageData(imgData, 0, 0);
callback(canvas.toDataURL());
};
},
},
};
</script>
<style lang="less" scoped>
.sign-finish {
width: 100vw;
height: 100vh;
background-color: #fff;
overflow: hidden;
.topbtn {
position: absolute;
display: flex;
transform: rotate(90deg);
bottom: 14vh;
right: -20px;
a {
color: #358cf1;
font-size: 15px;
}
img {
transform: rotate(-90deg);
margin-left: 15px;
margin-right: 5px;
}
}
.content {
position: absolute;
transform: rotate(90deg);
top: 50%;
font-size: 20px;
right: 5vw;
color: #999999;
}
.wrap {
padding: 15px;
height: 92.5vh;
display: flex;
justify-content: center;
overflow: hidden;
.actionsWrap {
width: 50px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
top: 37%;
}
.canvas {
flex: 1;
}
.actions {
margin-right: 10px;
white-space: nowrap;
transform: rotate(90deg);
.action-button {
margin-left: 15px;
}
}
}
}
</style>