天天看點

Canvas實作H5簽名

<template>
    <div class="position-fixed sign-page">
        <div>簽名</div>
        <div class="no-sign" @click="goSign">
          <img :src="signature" v-if="signature" />
        </div>
        
        <!--  畫布  -->
         <div v-show="showCanvas" class="canvas-area position-fixed">
             <span class="cancel rotate" v-show="showBtn" @click="handleCancel">取消   	</span>
             <span class="finish rotate" v-show="showBtn" @click="handleSave">完成</span>
             <van-icon v-show="showBtn" name="revoke" size="24" color="#ff9900" class="revoke rotate" @click="handleRevoke" />
             <canvas id="myCanvas"></canvas>
         </div>
     </div>
 </template>
 
 <script>
 export default {
   data() {
    return {
      canvas: null, // canvas對象
      showCanvas: false, // 顯示畫布
      showBtn: true, // 顯示畫布上的各種按鈕
      ctx: null, // 畫筆
      pointList: [], // 筆觸
      cacheData: [], // 緩存每一步的圖像,用于回退
      signature: '', // 簽名圖像
    };
  },
  mounted() {
    this.canvas = document.getElementById('myCanvas');
    this.canvas.width = document.documentElement.clientWidth;
    this.canvas.height = document.documentElement.clientHeight;
    this.ctx = this.canvas.getContext('2d');
    this.setBackground();
    this.bindEvent();
  },
  methods: {
    // 畫布背景設為白色
    setBackground() {
      this.ctx.fillStyle = '#ffffff';
      this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    },
     // 去設定簽名
    goSign() {
      this.showCanvas = true;
    },
     // 監聽觸摸事件
    bindEvent() {
      this.handleTouchStart();
      this.handleTouchMove();
      this.handleTouchEnd();
    },
    // 觸摸開始
    handleTouchStart() {
      const that = this;
      this.canvas.ontouchstart = function (e) {
        that.showBtn = false;
        that.pointList = [];
        that.drawSign(e);
      };
    },
    // 觸摸移動
    handleTouchMove() {
      const that = this;
      this.canvas.ontouchmove = function (e) {
        that.drawSign(e);
      };
    },
    // 繪制簽名
    drawSign(e) {
      const ctx = this.ctx;
      e = e || event;
      const x = e.touches[0].clientX;
      const y = e.touches[0].clientY;
      this.pointList.push([x, y]);
      ctx.strokeStyle = '#000000';
      ctx.beginPath();
      ctx.lineWidth = 1;

      this.pointList.forEach((p, idx) => {
        if (idx === 0) {
          ctx.moveTo(...p);
        } else {
          ctx.lineTo(...p);
        }
      });
      ctx.stroke();
    },
    // 觸摸結束
    handleTouchEnd() {
      const that = this;
      this.canvas.ontouchend = function () {
        that.cacheData.push(that.ctx.getImageData(0, 0, that.canvas.width, that.canvas.height));
        that.showBtn = true;
      };
    },
     // 撤回
    handleRevoke() {
      if (this.cacheData.length > 1) {
        this.cacheData.pop();
        const img = this.cacheData[this.cacheData.length - 1];
        this.ctx.putImageData(img, 0, 0);
      } else if (this.cacheData.length === 1) {
        this.cacheData.pop();
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.setBackground();
      }
    },
    // 取消
    handleCancel() {
      this.showBtn = true;
      this.pointList = [];
      this.cacheData = [];
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.setBackground();
      this.showCanvas = false;
    },
        // 儲存
    handleSave() {
      // 未繪制圖像
      if (this.cacheData.length === 0) {
        this.handleCancel();
        return;
      }
      const data = this.canvas.toDataURL('image/png');
      let img = new Image();
      img.src = data;
      const that = this;
      img.onload = function () {
        // 順時針旋轉 270 度
        const width = that.canvas.width,
          height = that.canvas.height;
        that.canvas.width = height;
        that.canvas.height = width;
        that.ctx.rotate((Math.PI / 2) * 3);
        that.ctx.drawImage(img, -width, 0);
        that.signature = that.canvas.toDataURL('image/png');
        // 還原寬高
        that.canvas.width = width;
        that.canvas.height = height;
        that.handleCancel();
      };
    },
  }
 }
</script>

 <style scoped >
.position-fixed {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 10;
}
.sign-page {
  background: #ffffff;
  padding: 12px 16px;
  text-align: left;
  .no-sign {
    height: 160px;
    background: #f2f2f2;
    border-radius: 6px;
    margin: 12px 0;
    img {
      width: 100%;
      height: 160px;
      background: #ffffff;
    }
  }
  .canvas-area {
    z-index: 14;
    color: #ff9900;
    background: #ffffff;
    .cancel {
      display: inline-block;
      position: absolute;
      right: 16px;
      top: 20px;
    }
    .rotate {
      transform: rotate(90deg);
      -ms-transform: rotate(90deg);
      -moz-transform: rotate(90deg);
      -webkit-transform: rotate(90deg);
      -o-transform: rotate(90deg);
    }
    .finish {
      display: inline-block;
      position: absolute;
      right: 16px;
      bottom: 20px;
    }
    .revoke {
      position: absolute;
      top: 50%;
      left: 16px;
    }
  }
}
</style>