合同签名

1.首先先画签名的cavans

<template>
  <div class="signature">
    <!--touchstart,touchmove,touchend,touchcancel 这-->
    <div class="signature__header">
      <van-nav-bar
        title="电子签名"
        @click-left="onClickLeft"
      >
        <div slot="left">
          <van-icon name="arrow-left" size="25px" color="#b6b6b6" />
        </div>
      </van-nav-bar>
    </div>
    <div class="signature__zang"></div>
    <div>
      <div>
        <canvas id="canvas" :height="tabListHeight">Canvas画板</canvas>
      </div>
      <div class="signature__button">
        <van-button type="primary"
                    size="small"
                    v-on:click="save">保存, 下一步</van-button>
        <van-button type="default"
                    size="small"
                    v-on:click="clear">清除</van-button>
      </div>
    </div>
  </div>

</template>

<script>

var draw
var preHandler = function (e) {
  e.preventDefault()
}
class Draw {
  constructor (el) {
    this.el = el
    this.canvas = document.getElementById(this.el)
    this.canvas.width = document.documentElement.clientWidth
    this.cxt = this.canvas.getContext('2d')
    this.stage_info = this.canvas.getBoundingClientRect()
    this.path = {
      beginX: 0,
      beginY: 0,
      endX: 0,
      endY: 0
    }
  }
  init (btn) {
    var that = this
    this.canvas.addEventListener('touchstart', function (event) {
      document.addEventListener('touchstart', preHandler, { passive: false })
      that.drawBegin(event)
    })
    this.canvas.addEventListener('touchend', function (event) {
      document.addEventListener('touchend', preHandler, { passive: false })
      that.drawEnd()
    })
    this.clear(btn)
  }
  drawBegin (e) {
    var that = this
    window.getSelection()
      ? window.getSelection().removeAllRanges()
      : document.selection.empty()
    this.cxt.strokeStyle = '#000'
    this.cxt.beginPath()
    this.cxt.moveTo(
      e.changedTouches[0].clientX - this.stage_info.left,
      e.changedTouches[0].clientY - this.stage_info.top
    )
    this.path.beginX = e.changedTouches[0].clientX - this.stage_info.left
    this.path.beginY = e.changedTouches[0].clientY - this.stage_info.top
    this.canvas.addEventListener('touchmove', function () {
      that.drawing(event)
    })
  }
  drawing (e) {
    this.cxt.lineTo(
      e.changedTouches[0].clientX - this.stage_info.left,
      e.changedTouches[0].clientY - this.stage_info.top
    )
    this.path.endX = e.changedTouches[0].clientX - this.stage_info.left
    this.path.endY = e.changedTouches[0].clientY - this.stage_info.top
    this.cxt.stroke()
  }
  drawEnd () {
    document.removeEventListener('touchstart', preHandler, { passive: false })
    document.removeEventListener('touchend', preHandler, { passive: false })
    document.removeEventListener('touchmove', preHandler, { passive: false })
    // canvas.ontouchmove = canvas.ontouchend = null
  }
  clear (btn) {
    this.cxt.clearRect(0, 0, this.canvas.width, 600)
  }
  save () {
    return this.canvas.toDataURL('image/png')
  }
}

export default {
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      val: true,
      screenWidth: 0,
      screenHeight: 0
    }
  },
  mounted () {
    draw = new Draw('canvas')
    draw.init()
    this.screenWidth = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth
    this.screenHeight = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight
  },
  computed: {
    tabListHeight () {
      return this.screenHeight - 100
    }
  },
  methods: {
    onClickLeft () {
      this.$router.go(-1)
    },
    clear: function () {
      draw.clear()
    },
    save: function () {
      var data = draw.save()
      this.$router.push({ name: 'newidentity' })
      sessionStorage.setItem('imgs', data)
      console.log(111)
      console.log(data)
    },

    mutate (word) {
      this.$emit('input', word)
    }
  }
  // data () {
  //   return {
  //     msg: 'Just use canvas to draw',
  //     degree: 0, // 屏幕整体旋转的角度, 可取 -90,90,180等值
  //     scope: [
  //       {
  //         value: 0,
  //         title: '正常',
  //       },
  //       {
  //         value: 90,
  //         title: '顺时针旋转90°',
  //       },
  //       {
  //         value: 180,
  //         title: '顺时针旋转180°',
  //       },
  //       {
  //         value: -90,
  //         title: '逆时针旋转90°',
  //       },
  //     ],
  //   };
  // },
  // components: {
  //   Draw,
  // },
  // mounted () {
  //   this.canvasBox = document.getElementById('canvasBox');
  //   this.initCanvas();
  // },
  // computed: {
  //   getHorizontalStyle () {
  //     console.log(232323)
  //     const d = document;
  //     const w = window.innerWidth || d.documentElement.clientWidth || d.body.clientWidth;
  //     const h = window.innerHeight || d.documentElement.clientHeight || d.body.clientHeight;
  //     let length = (h - w) / 2;
  //     let width = w;
  //     let height = h;

  //     switch (this.degree) {
  //       case -90:
  //         length = -length;
  //       case 90:
  //         width = h;
  //         height = w;
  //         break;
  //       default:
  //         length = 0;
  //     }
  //     if (this.canvasBox) {
  //       this.canvasBox.removeChild(document.querySelector('canvas'));
  //       this.canvasBox.appendChild(document.createElement('canvas'));
  //       setTimeout(() => {
  //         this.initCanvas();
  //       }, 200);
  //     }
  //     return {
  //       transform: `rotate(${this.degree}deg) translate(${length}px,${length}px)`,
  //        `${width}px`,
  //       height: `${height}px`,
  //       transformOrigin: 'center center',
  //     };
  //   },
  // },
  // methods: {
  //   initCanvas () {
  //     const canvas = document.querySelector('canvas');
  //     this.draw = new Draw(canvas, -this.degree);
  //   },
  //   clear () {
  //     this.draw.clear();
  //   },
  //   download () {
  //     this.draw.downloadPNGImage(this.draw.getPNGImage());
  //   },
  //   upload () {
  //     const image = this.draw.getPNGImage();
  //     const blob = this.draw.dataURLtoBlob(image);

  //     const url = '';
  //     const successCallback = (response) => {
  //       console.log(response);
  //     };
  //     const failureCallback = (error) => {
  //       return this.$toast(res.data.statusText);
  //     };
  //     this.draw.upload(blob, url, successCallback, failureCallback);
  //   },
  // },
}
</script>

<style lang="less" scoped>
h1,
h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
#canvasBox {
  display: flex;
  flex-direction: column;
  height: 100%;
}
canvas {
  flex: 1;
  cursor: crosshair;
  background-color: #f2f2f2;
}
.signature {
  height: 100%;
   100%;
  background-color: #ffffff;
  position: relative;
  .signature__header {
     100%;
    height: 46px;
    position: fixed;
    top: 0;
    left: 0;
  }
  .signature__zang {
     100%;
    height: 46px;
  }
  .signature__button {
    height: 50px;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;
  }
}
#keyword-box {
  margin: 10px 0;
}
</style>

  2.合成

<template>
  <div id="identity" class="identity">
    <div class="identity__header">
      <van-nav-bar
        title="签订合同"
        @click-left="onClickLeft"
      >
        <div slot="left">
          <van-icon name="arrow-left" size="25px" color="#b6b6b6" />
        </div>
      </van-nav-bar>
    </div>
    <div class="identity__zang"></div>
    <div class="identity__content" :style="{ height: tabListHeight }">
      <div class="identity__content-item">
        <div class="identity-bnt van-hairline--top">
         <!-- <div class="upphoto"> 上传交款凭证</div> -->
          <!-- 上传图片 -->
            <!-- <div class="pushphoto">
              <img class="pushphoto-add"
                  v-for="(item,index) of photo"
                  :key="index"
                  :src="item"
                  @click="showimg(item)">
              <van-uploader class="pushphoto-add"
                            :after-read="onRead"
                            v-if="falses">
                <img class="pushphoto-add"
                    src="../../assets/img/addphoto.png">
              </van-uploader>
            </div> -->

       <!-- <div class="upphoto"> 上传身份证</div>
      <div class="pushphoto">
        <img class="pushphoto-add"
             v-for="(item,index) of photo1"
             :key="index"
             :src="item"
             @click="showimg1(item)">
        <van-uploader class="pushphoto-add"
                      :after-read="onRead1"
                      v-if="falses1">
          <img class="pushphoto-add"
               src="../../assets/img/addphoto.png">
        </van-uploader>
      </div> -->
      <div class="info-conter"
         ref="infoconter">
//这里是你的合同 <img src="../../assets/img/hetong.png" id="imgCover" ref="imgCover">
//这里是你的签名 <img v-show="zou" :src="ba64" id="imgUploadX"> <canvas v-show="yincang" id="can"></canvas> </div> <!-- <van-button type="primary" @click="pull">签订合同</van-button> --> </div> </div> </div> </div> </template> <script> export default { mounted () { this.$nextTick(() => { this.ba64 = sessionStorage.getItem('imgs'); //this.pull(); this.$toast.loading({ mask: true, message: '合同签名中,请等待...' });
//渲染需要一个过程,需要用到定时器,不然手机合成的签名合同是一片黑 setTimeout(()=>{ // console.log(Timer); this.pull(); },4000); this.screenWidth = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth this.screenHeight = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight }) }, computed: { tabListHeight () { return this.screenHeight / (this.screenWidth / 10) - 2 - 1.226667 + 'rem' } }, data () { return { ba64: '', zou: false, yincang: false, enen: false, frist: '', second: '', files: '', three: '', dataurls: '', threeimg: '', secondimg: '', fristimg: '', text1: true, text2: true, text3: true, imgs1: false, imgs2: false, imgs3: false, screenWidth: 0, screenHeight: 0, falses: true, falses1:true, photo: [], photo1: [], photolist: [], photolist1: [], } }, methods: { // onRead (i) { // this.photo.push(i.content); // //console.log(this.photo); // this.photolist.push(i.file) // if (this.photo.length >= 3) { // this.falses = false // } // console.log(this.photo) // }, // onRead1 (i) { // this.photo1.push(i.content) // this.photolist1.push(i.file) // if (this.photo1.length >= 3) { // this.falses1 = false // } // console.log(this.photo1) // }, // showimg (item) { // this.show = true // this.file = item // }, // showimg1 (item) { // this.show = true // this.file1 = item // }, // 合成图片 pull () { console.log(this.photolist); //console.log(this.photolist1); this.compound(); // setTimeout(()=>{ this.$router.push({ path: '/newesignature' }) // },5000); },
//这里是签名与合同的渲染合成 compound () { this.yincang = true let eleImgUploadX = document.getElementById('imgUploadX') let eleImgCover = document.getElementById('imgCover') let can = document.getElementById('can') let context = can.getContext('2d') let width = this.$refs.infoconter.offsetWidth let heigth = this.$refs.infoconter.offsetHeight let heigths = this.$refs.imgCover.offsetHeight let hei = this.$refs.imgCover.offsetHeight can.width = width can.height = heigths var devicePixelRatio = window.devicePixelRatio || 1, backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1, ratio = devicePixelRatio / backingStoreRatio var oldWidth = can.width; var oldHeight = can.height; can.width = oldWidth * ratio can.height = oldHeight * ratio can.style.width = oldWidth + 'px' can.style.height = oldHeight + 'px' context.scale(ratio, ratio) console.log(width) context.drawImage(eleImgCover, 0, 0, width, heigth) context.drawImage(eleImgUploadX, width * 0.65, hei * 0.8, 95, 80) console.log(can.toDataURL('image/png')) let dataurls = can.toDataURL('image/jpeg') this.dataurls = can.toDataURL('image/jpeg') this.enen = true // 转文件flie let filename = 'canvas.jpg' let feli = dataURLtoFile(dataurls, filename) function dataURLtoFile (dataurl, filename) {//将base64转换为文件 var 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); } return new File([u8arr], filename, { type: mime }) } this.files = feli console.log(this.files) sessionStorage.setItem('files', this.dataurls) }, onClickLeft () { this.$router.push({ name: 'openvip' }) } } } </script> <style lang="less" scoped> .identity { 100%; // height: 100%; overflow: hidden; position: relative; img { 100%; height: 100%; } .identity__header { 100%; height: 46px; position: fixed; top: 0; left: 0; z-index: 999; } .identity__zang { 100%; height: 46px; } .info-conter { 100%; // height: 100%; // background-color: white; // overflow: hidden; overflow-x: auto; overflow-y: auto; position: absolute; right: 0; // bottom: -100%; } #imgCover { opacity: 0; } .info-conter img { 100%; } .identity__content{ 100%; background-color: #ffffff; overflow: scroll; -webkit-overflow-scrolling: touch; .identity__content-item { // height: 100%; 100%; .identity-frist { 100%; height: 180px; background-color: #f2f2f2; margin-top: 10px; margin-bottom: 10px; text-align: center; line-height: 180px; font-size: 18px; .van-uploader { 100%; height: 100%; } } } } .pushphoto { 100%; height: 100px; padding-left: 16px; padding-bottom: 16px; // margin-bottom: 20px; box-sizing: border-box; display: flex; .pushphoto-add { 25%; height: 100%; margin-right: 10px; img { 100%; } } } .identity-bottom { 100%; height: 118px; } .identity-bnt { 100%; // position: fixed; // bottom: 0px; // z-index: 99999; padding: 20px 20px; background-color: #ffffff; box-sizing: border-box; // height: 118px; .identity-reset { margin-bottom: 10px; } .van-button { 100%; } } } </style>

  效果:

 

原文地址:https://www.cnblogs.com/guangzhou11/p/10653830.html