vue3 电子签名

<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>

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值