本文实例为大家分享了vue3.0实现移动端电子签名组件的具体代码,供大家参考,具体内容如下
因业务需求,前段时间写了一个电子签名组件,在这里记录一下,绘图需求,首先肯定需要使用
canvas标签,考虑到在移动端使用,所以选择使用touch事件。
首先奉上html结构(该组件为横屏展示):
<div class="signName" :style="{top:0,left:differ+'px'}">
<div class="close" @click="close"><img src="../assets/images/close.png" alt=""></div>
<div class="autographBox">
<div ref="canvasHW">
<canvas
@touchstart="touchStart($event)"
@touchmove="touchMove($event)"
@touchend="touchEnd($event)"
ref="canvasF"
></canvas>
</div>
<p v-if="!isDraw">请本人签名</p>
</div>
<div class="autographBtn">
<div @click="overwrite">重签</div>
<div @click="seaveImages">确定</div>
</div>
</div>
css样式:
.signName{
position: fixed;
height: 100vw;
width: 100vh;
background-color: #fff;
transform:rotate(90deg);
-webkit-transform:rotate(90deg);
transform-origin: left top;
z-index: 1000;
/* top:0;
left: 0; */
}
.close{
width: 100%;
height: 10%;
padding-left: 2.5rem;
padding-top: 1.2rem;
}
.close img{
width: 2rem;
}
.autographBox{
width: 100%;
height: 80%;
position: relative;
}
.autographBox div{
width: 100%;
height: 100%;
}
.autographBox canvas{
width: 100%;
height: 100%;
}
.signName p{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
font-size: 4rem;
font-weight: bolder;
color:#436CDF;
opacity: 0.1;
}
.autographBtn{
width: 100%;
height: 10%;
display: flex;
justify-content: center;
align-items: center;
}
.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%);
}
其次定义变量,初始化canvas:
var differ = ref(document.documentElement.clientWidth)
var canvasF = ref(null);
var canvasHW = ref(null);
var canvasTxt = null; //画布
var points = []; // 记录点
var isDraw = ref(false); //签名标记
var startX = 0; //开始坐标x
var startY = 0;//开始坐标y
var moveY= 0;
var moveX= 0;
var strDataURI ='' // 保存的canvas图像
onMounted(() =>{
let canvas =canvasF.value;
canvas.height = canvasHW.value.offsetHeight - 10;
canvas.width = canvasHW.value.offsetWidth - 10;
canvasTxt = canvas.getContext("2d");
canvasTxt.strokeStyle = '#333';
canvasTxt.lineWidth = '3';
})
主要事件方法如下:
1、touchstart
const touchStart = (ev) => {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
isDraw.value = true; //签名标记
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
}; //y的计算值中:document.body.offsetHeight*0.5代表的是除了整个画板signatureBox剩余的高,this.$refs.canvasHW.offsetHeight*0.1是画板中标题的高
startX = obj.x;
startY = obj.y;
canvasTxt.beginPath();//开始作画
points.push(obj);//记录点
}
}
2、touchmove
const touchMove = (ev)=> {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
};
moveY = obj.y;
moveX = obj.x;
canvasTxt.moveTo(startX, startY);//移动画笔
canvasTxt.lineTo(obj.x, obj.y);//创建线条
canvasTxt.stroke();//画线
startY = obj.y;
startX = obj.x;
points.push(obj);//记录点
}
}
3、touchend
const touchEnd = (ev)=> {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
};
points.push(obj);//记录点
points.push({ x: -1, y: -1 });//记录点
canvasTxt.closePath();//收笔
canvasTxt.fill();
}
}
4、重写
const overwrite = ()=> {
canvasTxt.clearRect(
0,
0,
canvasF.value.width,
canvasF.value.height
);
points = [];
isDraw.value = false; //签名标记
}
5、保存图片
function seaveImages() {
if(isDraw.value){
strDataURI = canvasF.value.toDataURL("image/png");
console.log(strDataURI)
cxt.emit("autographConfirm", {
baseCode:strDataURI,
});
}else{
console.log(Toast);
Toast('您还没有签名')
}
}
最后附上完整代码:
<template>
<div class="signName" :style="{top:0,left:differ+'px'}">
<div class="close" @click="close"><img src="../assets/images/close.png" alt=""></div>
<div class="autographBox">
<div ref="canvasHW">
<canvas
@touchstart="touchStart($event)"
@touchmove="touchMove($event)"
@touchend="touchEnd($event)"
ref="canvasF"
></canvas>
</div>
<p v-if="!isDraw">请本人签名</p>
</div>
<div class="autographBtn">
<div @click="overwrite">重签</div>
<div @click="seaveImages">确定</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from "vue";
import { Toast } from "vant";
export default {
name: "index",
setup(props,cxt) {
var differ = ref(document.documentElement.clientWidth)
var canvasF = ref(null);
var canvasHW = ref(null);
var canvasTxt = null; //画布
var points = []; // 记录点
var isDraw = ref(false); //签名标记
var startX = 0; //开始坐标x
var startY = 0;//开始坐标y
var moveY= 0;
var moveX= 0;
var strDataURI ='' // 保存的canvas图像
onMounted(() =>{
let canvas =canvasF.value;
canvas.height = canvasHW.value.offsetHeight - 10;
canvas.width = canvasHW.value.offsetWidth - 10;
canvasTxt = canvas.getContext("2d");
canvasTxt.strokeStyle = '#333';
canvasTxt.lineWidth = '3';
})
const touchStart = (ev) => {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
isDraw.value = true; //签名标记
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
}; //y的计算值中:document.body.offsetHeight*0.5代表的是除了整个画板signatureBox剩余的高,this.$refs.canvasHW.offsetHeight*0.1是画板中标题的高
startX = obj.x;
startY = obj.y;
canvasTxt.beginPath();//开始作画
points.push(obj);//记录点
}
}
const touchMove = (ev)=> {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
};
moveY = obj.y;
moveX = obj.x;
canvasTxt.moveTo(startX, startY);//移动画笔
canvasTxt.lineTo(obj.x, obj.y);//创建线条
canvasTxt.stroke();//画线
startY = obj.y;
startX = obj.x;
points.push(obj);//记录点
}
}
const touchEnd = (ev)=> {
ev = ev || event;
ev.preventDefault();
if (ev.touches.length == 1) {
let obj = {
x: ev.targetTouches[0].clientY,
y: differ.value- ev.targetTouches[0].clientX - (differ.value*0.1)
};
points.push(obj);//记录点
points.push({ x: -1, y: -1 });//记录点
canvasTxt.closePath();//收笔
canvasTxt.fill();
}
}
const overwrite = ()=> {
canvasTxt.clearRect(
0,
0,
canvasF.value.width,
canvasF.value.height
);
points = [];
isDraw.value = false; //签名标记
}
function seaveImages() {
if(isDraw.value){
strDataURI = canvasF.value.toDataURL("image/png");
console.log(strDataURI)
cxt.emit("autographConfirm", {
baseCode:strDataURI,
});
}else{
console.log(Toast);
Toast('您还没有签名')
}
}
function close(){
cxt.emit("close", {
closeFlag:false,
});
}
return {
differ,
canvasF,
canvasHW,
isDraw,
touchStart,
touchMove,
touchEnd,
overwrite,
seaveImages,
close
};
},
};
</script>
<style scoped>
.signName{
position: fixed;
height: 100vw;
width: 100vh;
background-color: #fff;
transform:rotate(90deg);
-webkit-transform:rotate(90deg);
transform-origin: left top;
z-index: 1000;
/* top:0;
left: 0; */
}
.close{
width: 100%;
height: 10%;
padding-left: 2.5rem;
padding-top: 1.2rem;
}
.close img{
width: 2rem;
}
.autographBox{
width: 100%;
height: 80%;
position: relative;
}
.autographBox div{
width: 100%;
height: 100%;
}
.autographBox canvas{
width: 100%;
height: 100%;
}
.signName p{
position: absolute;
top:50%;
left: 50%;
transform: translate(-50%,-50%);
font-size: 4rem;
font-weight: bolder;
color:#436CDF;
opacity: 0.1;
}
.autographBtn{
width: 100%;
height: 10%;
display: flex;
justify-content: center;
align-items: center;
}
.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>