【vue中利用canvas实现签名并保存】

vue中实现签名

封装的组件

<template>
  <div class="canvas">
    <!-- <div class="rotate canvas_header">手写签名</div> -->
    <div class="canvas_canvas">
      <canvas id="myCanvas"></canvas>
    </div>
    <!-- <div @click="clearArea()" class="rotate canvas_footer_tip">*请手写签字,确认后提交。一旦提交,将无法再次对面试情况进行评价!</div> -->
    <div class="rotate canvas_footer">
      <div @click="back()" class=" canvas_footer_back">返回打分页面</div>
      <div @click="clearArea()" class=" canvas_footer_re">重写</div>
      <div @click="saveImageInfo()" class="canvas_footer_su">提交</div>
    </div>
    <!-- 颜色选择器 -->
    <div class="canvas_color" v-show="showColor == 2">
      <div>
        <input v-model="strokeStyle" type="color" />
      </div>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    isWeichat: {
      type: Boolean,
      default:()=>true
    },
    isWeixinBrowser: {
      type: String,
      default:()=>'true'
    }
  },
  data() {
    return {
      touchPressed: false,
      ctx: null,
      strokeStyle: "#000000", //书写颜色
      lineWidth: 4, //线条宽度
      lastX: null,
      lastY: null,
      canvas: null,
      showColor: 1, //显示颜色
      sColor: 0, //显示颜色
    };
  },
  mounted() {
    this.$nextTick(() => {
      let canvas = document.getElementById("myCanvas");
      let canvasBox = document.getElementsByClassName("canvas_canvas")[0];
      this.canvas = canvas;
      this.ctx = canvas.getContext("2d");
      let winW = window.innerWidth - 85;
      let winH = window.innerHeight - 40;
      if(this.isWeixinBrowser === 'false') {
        winH = window.innerHeight - document.getElementsByClassName('nav-bar')[0].clientHeight -40
      }
      if(window.innerHeight/window.innerWidth < 1.5) {
        // pad端
        winW = window.innerWidth - 135;
        canvasBox.style.marginLeft = '3.5rem'
      }
      this.canvas.width = winW;
      this.canvas.height = winH;
      canvasBox.style.width = winW + 'px'
      canvasBox.style.height = winH + 'px'
      this.Init();
    });
  },
  methods: {
    Init() {
      // 移动前
      this.canvas.addEventListener(
        "touchstart",
        (event) => {
          if (event.targetTouches.length == 1) {
            event.preventDefault(); // 阻止浏览器默认事件,重要
            let touch = event.targetTouches[0];
            this.touchPressed = true;
            this.draw(
              touch.pageX - this.canvas.offsetLeft,
              touch.pageY - this.canvas.offsetTop,
              false
            );
          }
        },
        false
      );
      // 移动中
      this.canvas.addEventListener(
        "touchmove",
        (event) => {
          if (event.targetTouches.length == 1) {
            event.preventDefault(); // 阻止浏览器默认事件,重要
            let touch = event.targetTouches[0];
            if (this.touchPressed) {
              this.draw(
                touch.pageX - this.canvas.offsetLeft,
                touch.pageY - this.canvas.offsetTop,
                true
              );
            }
          }
        },
        false
      );
      // 移动结束
      this.canvas.addEventListener(
        "touchend",
        (event) => {
          if (event.targetTouches.length == 1) {
            event.preventDefault(); // 阻止浏览器默认事件,防止手写的时候拖动屏幕,重要
            this.touchPressed = false;
          }
        },
        false
      );
    },
    draw(x, y, isDown) {
      let ctx = this.ctx;
      if (isDown) {
        ctx.beginPath();
        ctx.strokeStyle = this.strokeStyle;
        ctx.lineWidth = this.lineWidth;
        ctx.lineJoin = "round";
        ctx.moveTo(this.lastX, this.lastY);
        ctx.lineTo(x, y);
        ctx.closePath();
        ctx.stroke();
      }
      this.lastX = x;
      this.lastY = y;
    },
    // 重写
    clearArea() {
      this.ctx.setTransform(1, 0, 0, 1, 0, 0);
      this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
    },
    // 保存本地
    saveImage() {
      // let a = document.createElement("a");
      this.rotateBase64Img(this.canvas.toDataURL(), -90, this.download)
      // a.href = this.canvas.toDataURL();
      // a.download = "sign";
      // a.click(); //保存
    },
    download(data) {
      let a = document.createElement("a");
      a.href = data;
      a.download = "sign";
      a.click(); //保存
    },
    //验证canvas画布是否为空函数
    isCanvasBlank(canvas) {
      let blank = document.createElement('canvas');//系统获取一个空canvas对象
      blank.width = canvas.width;
      blank.height = canvas.height;
      return canvas.toDataURL() == blank.toDataURL();//比较值相等则为空
    },
    // 保存服务器
    saveImageInfo() {
      if(this.isCanvasBlank(this.canvas)) {
        this.$toast('请签名后提交')
        return
      }
      this.rotateBase64Img(this.canvas.toDataURL(), -90, this.dataURLtoFile)
    },
    dataURLtoFile(dataurl) {//将base64转换为文件,dataurl为base64字符串,filename为文件名(必须带后缀名,如.jpg,.png)
      let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
      while(n--){
          u8arr[n] = bstr.charCodeAt(n);
      }
      let fileList = new File([u8arr], 'signature.png', {type:mime});
      // this.update(fileList)//图片上传
      this.$emit('upload', fileList)
      return fileList;
    },
    
    //签完名的图片旋转处理
    // src为你、base64编码;edg为角度,0-360;callback回调
    rotateBase64Img(src, edg, callback) {
      let canvas = document.createElement("canvas");
      let ctx = canvas.getContext("2d");
      let imgW;//图片宽度
      let imgH;//图片高度
      let 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}; //裁剪坐标
      let 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);
          let 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())
      };
    },
    back() {
      this.$router.go(-1)
    }
    //end
  },
  watch: {
    strokeStyle(newColor,oldColor) {
      console.log(newColor)
    },
  },
};
</script>
<style>
.canvas {
  width: 100%;
  /* height: 100%; */
}
.canvas_header {
  width: 100%;
  text-align: center;
  line-height: 50px;
  font-weight: 800;
  font-size: 20px;
}
.canvas_canvas {
  width: 100%;
  height: 100%;
  margin: 0 auto;
  margin-top: .625rem;
  margin-left: 4.0625rem;
  border: .125rem solid #aaa;
}
.canvas_info {
  margin: 10px 8%;
  font-size: 12px;
}
.canvas_footer {
  width: 100%;
  /* max-width: 140%; */
  margin: 0;
  display: flex;
  justify-content: space-between;
  line-height: 40px;
  text-align: center;
  color: white;
  /* transform: translate(10px, 10px); */
  font-weight: 800;
  position: fixed;
  left: -42%;
  top: 50%;
}
.canvas_footer_back {
  width: 8.25rem;
  margin-right: .625rem;
  font-weight: 400;
  background: #ccc;
  /* background: #c9b2b2; */
  border-radius: 10px;
}
.canvas_footer_re {
  width: 5.625rem;
  margin-right: .625rem;
  background: #ccc;
  /* background: #c9b2b2; */
  border-radius: 10px;
}
.canvas_footer_tip {
  /* width: 15.625rem; */
  /* background: #1989f1; */
  position: fixed;
  left: -45%;
  top: 37%;
  color: #ccc;
  font-size: .625rem;
  line-height: 1.3;
  border-radius: 10px;
}
.canvas_footer_su {
  width: 5.625rem;
  background: #5471d5;
  border-radius: 10px;
}
.rotate  {
  transform: rotate(90deg);
  transform-origin: 50% 50%;
}
</style>

其他组件调用

<template>
  <div class="signature-box">
    <nav-bar v-if="isWeixinBrowser=='false'"></nav-bar>
    <Signature :style="getContentStyle" :isWeixinBrowser="isWeixinBrowser" @upload="upload"/>
  </div>
</template>
<script>
import NavBar from "@/components/NavBar.vue";
import Signature from '@/components/Signature.vue'
import { Dialog } from 'vant'
export default {
  components: { NavBar, Signature },
  data() {
    return {
      isWeixinBrowser: true,
      votingId: '',
      voterId: '',
    };
  },
  created() {
    this.voterId = this.$route.query.voterId
    this.votingId = this.$route.query.votingId
    this.isWeixinBrowser =  sessionStorage.getItem("isWeixinBrowser")
    if (this.isWeixinBrowser) {
      var $body = $('body');
      document.title = sessionStorage.getItem('mobileTitle')
      var $iframe = $('<iframe src="/favicon.ico"></iframe>');
      $iframe.on('load',function() {
      setTimeout(function() {
        $iframe.off('load').remove();
      }, 0);
      }).appendTo($body);
    }  else {
      this.isWeichat()
    }
  },
  mounted() {},
  computed: {
    getContentStyle() {
      this.$nextTick(() => {
        let height = '100%'
        if(this.isWeixinBrowser === 'false') {
          height = `height: calc(100% - ${document.getElementsByClassName('nav-bar')[0].clientHeight}px)`
        }
        return `width: 100%;height: ${height}`
      })
      return ''
    }
  },
  methods: {
    /**
     * 判断是否微信登录
     */
    isWeichat() {
      var ua = window.navigator.userAgent.toLowerCase();
      if(ua.match(/MicroMessenger/i) == 'micromessenger'){
        this.isWeixinBrowser = 'true';
      }else{
        this.isWeixinBrowser = 'false';
      }
    },
    /**
     * 上传
     */
    upload(file){
      Dialog.confirm({
        title: '签字确认',
        message: '签字一旦提交,将无法再次对面试情况进行评价!确认是否继续提交签字',
      })
        .then(() => {
        let param = new FormData(); //创建form对象
        param.append('file',file,file.name);//通过append向form对象添加数据
        param.append('voterId',this.voterId);//通过append向form对象添加数据
        this.$api
          .uploadImg(param)
          .then((res) => {
            this.$toast('签名提交成功')
            this.$router.replace({ name: 'success',query:{
              voterId: this.voterId,
              votingId: this.votingId,
            }})
          })
          .catch((err) => {
            this.$toast('签名文件上传失败,请重试')
          })
        })
        .catch(() => {})
      
    },
  }
};
</script>

<style scoped>
.signature-box {
  width: 100%;
  height: 100%;
}
</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值