<template>
<div class="popmask">
<div class="signName">
<div class="close" @click="close">
<van-icon size="24" name="cross" />
</div>
<div class="autographBox">
<canvas :width="differW*0.9+'px'" :height="differH+'px'" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" ref="canvasF"></canvas>
<p v-if="!isDraw">请本人签名</p>
</div>
</div>
<div class="autographBtn" :style="{ top: 0, left: differ*0.1 + 'px' }">
<div @click="overwrite">重签</div>
<div @click="seaveImages">确定</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from "vue";
import { Toast } from "vant"
let differW = ref<any>(document.documentElement.clientWidth);
let differH = ref<any>(document.documentElement.clientHeight);
let canvasF = ref<any>(null);
let canvasTxt: any = null; //画布
let isDraw = ref<boolean>(false); //签名标记
let startX = 0; //开始坐标x
let startY = 0;//开始坐标y
let strDataURI = '' // 保存的canvas图像
const emit = defineEmits(['bindCloseFlag']);
onMounted(() => {
initalHandle();
})
const initalHandle = () => {
nextTick(() => {
let canvas = canvasF.value;
canvas.width = canvasF.value.offsetWidth;
canvas.height = canvasF.value.offsetHeight;
canvasTxt = canvas.getContext("2d");
canvasTxt.strokeStyle = '#333';
canvasTxt.lineWidth = '3';
})
}
const touchStart = (ev: any) => {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
isDraw.value = true; //签名标记
let obj = {
x: ev.targetTouches[0].clientX - differW.value * 0.1,
y: ev.targetTouches[0].pageY - window.scrollY
};
startX = obj.x;
startY = obj.y;
canvasTxt.beginPath();//开始作画
}
}
const touchMove = (ev: any) => {
ev = ev || event;
ev.preventDefault();
console.log()
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientX - differW.value * 0.1,
y: ev.targetTouches[0].pageY - window.scrollY
};
canvasTxt.moveTo(startX, startY);//移动画笔
canvasTxt.lineTo(obj.x, obj.y);//创建线条
canvasTxt.stroke();//画线
startY = obj.y;
startX = obj.x;
}
}
const touchEnd = (ev: any) => {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
canvasTxt.closePath();//收笔
canvasTxt.fill();
}
}
// 重新签名
const overwrite = () => {
canvasTxt.clearRect(0, 0, canvasF.value.width, canvasF.value.height);
isDraw.value = false; //签名标记
}
const seaveImages = () => {
if (isDraw.value) {
Toast.loading({
message: '加载中...',
forbidClick: true,
overlay: true,
duration: 0
});
rotateBase64Img(canvasF.value.toDataURL("image/png"), 270, (base64data: any) => {
console.log(base64data);
Toast.clear();
});
} else {
Toast('您还没有签名')
}
}
const rotateBase64Img = (src: string, edg: number, callback: any) => {
let canvas = document.createElement("canvas");
let ctx: any = canvas.getContext("2d");
let imgW;//图片宽度
let imgH;//图片高度
let size;//canvas初始大小
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);
//drawImage向画布上绘制图片
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;
}
//putImageData() 将图像数据放回画布
ctx.putImageData(imgData, 0, 0);
callback(canvas.toDataURL())
};
}
const close = () => {
emit("bindCloseFlag", '');
}
</script>
<style scoped>
.popmask {
position: fixed;
width: 100%;
height: 100vh;
z-index: 1005;
top: 0;
background: #fff;
}
.signName {
position: absolute;
height: 100vh;
width: 90vw;
background-color: #fff;
z-index: 1;
right: 0;
}
.close {
position: absolute;
right: 10px;
top: 20px;
z-index: 1;
}
.autographBox {
width: 100%;
height: 100%;
position: relative;
}
.autographBox div {
width: 100%;
height: 100%;
}
.autographBox canvas {
width: 100%;
height: 100%;
touch-action: none;
/* 当触控事件发生在元素上时,不进行任何操作。 */
}
.signName p {
position: absolute;
width: 100vh;
height: 90vw;
transform: rotate(90deg);
/* transform: translate(-50%, -50%); */
font-size: 4rem;
font-weight: bolder;
color: #436CDF;
opacity: 0.1;
transform: rotate(90deg);
transform-origin: left top;
-webkit-transform: rotate(90deg);
-webkit-transform-origin: left top;
-moz-transform: rotate(90deg);
-moz-transform-origin: left top;
-ms-transform: rotate(90deg);
-ms-transform-origin: left top;
top: 0;
text-align: center;
left: 100%;
line-height: 90vw;
z-index: -1;
}
.autographBtn {
position: absolute;
width: 100vh;
height: 10vw;
display: flex;
justify-content: center;
align-items: center;
transform: rotate(90deg);
transform-origin: left top;
-webkit-transform: rotate(90deg);
-webkit-transform-origin: left top;
-moz-transform: rotate(90deg);
-moz-transform-origin: left top;
-ms-transform: rotate(90deg);
-ms-transform-origin: left top;
z-index: 2;
}
.autographBtn div {
width: 50%;
height: 100%;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.3rem;
}
.autographBtn div:first-child {
opacity: 0.4;
background: -webkit-linear-gradient(top, #728CFD 0%, #5C7EFE 100%);
}
.autographBtn div:last-child {
background: -webkit-linear-gradient(top, #728CFD 0%, #5C7EFE 100%);
}
</style>
vue3 电子签名
于 2022-09-22 17:04:10 首次发布