使用vue3.0实现一个移动端电子签名组件

时间:2025-03-28 11:33:25

因业务需求,前段时间写了一个电子签名组件,在这里记录一下,绘图需求,首先肯定需要使用

canvas标签,考虑到在移动端使用,所以选择使用touch事件。

首先奉上html结构(该组件为横屏展示):

<div class="signName" :style="{top:0,left:differ+'px'}">
    <div class="close" @click="close"><img src="../assets/images/" 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()
   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 =;
       =  - 10;
       =  - 10;
      canvasTxt = ("2d");
       = '#333';
       = '3';
    }) 

主要事件方法如下:

  1. touchstart

    const touchStart = (ev) => {
          ev = ev || event;
          ();
          if ( == 1) {
             = true; //签名标记
            let obj = {
              x: [0].clientY,
              y: - [0].clientX - (*0.1)
            }; //y的计算值中:*0.5代表的是除了整个画板signatureBox剩余的高,this.$*0.1是画板中标题的高
            startX = ;
            startY = ;
            ();//开始作画
            (obj);//记录点
          }
        }

  2. touchmove

    const touchMove = (ev)=> {
          ev = ev || event;
          ();
          if ( == 1) {
            let obj = {
              x: [0].clientY,
              y: - [0].clientX - (*0.1)
            };
            moveY = ;
            moveX = ;
            (startX, startY);//移动画笔
            (, );//创建线条
            ();//画线
            startY = ;
            startX = ;
            (obj);//记录点
          }
        }

  3. touchend

    const touchEnd = (ev)=> {
          ev = ev || event;
          ();
          if ( == 1) {
            let obj = {
              x: [0].clientY,
              y: - [0].clientX - (*0.1)
            };
            (obj);//记录点
            ({ x: -1, y: -1 });//记录点
            ();//收笔
            ();
          }
        }

  4. 重写

    const overwrite = ()=> {
          (
            0,
            0,
            ,
            
          );
          points = [];
           = false; //签名标记
        }

  5. 保存图片

    function seaveImages() {
        if(){
           strDataURI = ("image/png");
           (strDataURI)
           ("autographConfirm", {
            baseCode:strDataURI,
          });
        }else{
          (Toast);
          Toast('您还没有签名')
        }
      }

最后附上完整代码:

<template>
  <div class="signName" :style="{top:0,left:differ+'px'}">
    <div class="close" @click="close"><img src="../assets/images/" 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()
   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 =;
       =  - 10;
       =  - 10;
      canvasTxt = ("2d");
       = '#333';
       = '3';
    }) 
    const touchStart = (ev) => {
      ev = ev || event;
      ();
      if ( == 1) {
         = true; //签名标记
        let obj = {
          x: [0].clientY,
          y: - [0].clientX - (*0.1)
        }; //y的计算值中:*0.5代表的是除了整个画板signatureBox剩余的高,this.$*0.1是画板中标题的高
        startX = ;
        startY = ;
        ();//开始作画
        (obj);//记录点
      }
    }
   const touchMove = (ev)=> {
      ev = ev || event;
      ();
      if ( == 1) {
        let obj = {
          x: [0].clientY,
          y: - [0].clientX - (*0.1)
        };
        moveY = ;
        moveX = ;
        (startX, startY);//移动画笔
        (, );//创建线条
        ();//画线
        startY = ;
        startX = ;
        (obj);//记录点
      }
    }
    const touchEnd = (ev)=> {
      ev = ev || event;
      ();
      if ( == 1) {
        let obj = {
          x: [0].clientY,
          y: - [0].clientX - (*0.1)
        };
        (obj);//记录点
        ({ x: -1, y: -1 });//记录点
        ();//收笔
        ();
      }
    }
    const overwrite = ()=> {
      (
        0,
        0,
        ,
        
      );
      points = [];
       = false; //签名标记
    }
  function seaveImages() {
    if(){
       strDataURI = ("image/png");
       (strDataURI)
       ("autographConfirm", {
        baseCode:strDataURI,
      });
    }else{
      (Toast);
      Toast('您还没有签名')
    }
  }
  function close(){
    ("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>