vue 簽名組件

臨時幫朋友寫個移動端的項目, 發現 它的項目中有個 pdf 文檔的 簽名功能, 也是折騰了一下簽名組件

簽名組件原理: 利用原生canvas 配合 移動端的事件, touchstart, touchend, touchmove, 進行 在canvas 畫布上畫線, 最後,把生成的簽名, 通過 toDataURL()方法 保存 爲 base64 圖片,就拿到簽名了,實現效果如下:

清空畫布, 選擇簽名字體顏色, 保存, 功能比較簡單

github上的 簽名組件

上代碼啊 

注: 本項目中使用了 vant-ui, 看演示報錯的小夥伴 ,記得刪除掉 vant-ui 的組件,然而,你可能看不到 有些效果了,比如字體顏色,清空, 確認等

1. template 模板

<template>
  <div class="signHandle">
    <canvas ref="signHandle" class="canvas" />
    <div class="btn_container van-hairline--top" :style="{height:height + 'px'}">
      <van-radio-group v-model="radio" class="radio_container" @change="radioHandle">
        <van-radio v-for="item in liColors" :key="item" :checked-color="item" :name="item" />
      </van-radio-group>
      <div>
        <van-button size="mini" @touchstart="clearHandle">清空</van-button>
        <van-button type="info" size="mini" @touchstart="saveImg">確認</van-button>
      </div>
    </div>
  </div>
</template>

2. js, 都是 canvas 的 一些 api , 就不解釋了

<script>
// 解構設備的寬度, 和 高度
const { clientWidth, clientHeight } = document.documentElement
export default {
  data() {
    return {
      radio: '#000',
      height: 50,
      direction: false, // true 代表橫屏, false 代表'豎屏'
      el: '', // canvas dom
      ctx: '', // canvas context
      background: '#fff', // canvas background-color
      color: '#000', // 繪製時線條的顏色
      linewidth: 3, // 線條的寬度
      liColors: ['#ee0a24', '#000', '#1890ff']
    }
  },
  created() {
    this.color = this.radio
    window.addEventListener(
      'onorientationchange' in window ? 'orientationchange' : 'resize',
      () => {
        if (window.orientation === 180 || window.orientation === 0) {
          this.direction = false
          this.draw()
        }
        if (window.orientation === 90 || window.orientation === -90) {
          this.direction = true
          this.draw()
        }
      },
      false
    )
  },
  mounted() {
    this.draw()
  },
  methods: {
    radioHandle(value) {
      this.color = value
      this.setCanvas()
    },
    // 添加繪製 line
    draw() {
      document.addEventListener('touchmove', e => e.preventDefault(), {
        passive: false
      })
      this.el = this.$refs.signHandle
      this.initCanvas()
    },
    // 初始化canvas配置
    initCanvas() {
      const { height, direction, el } = this
      if (direction) {
        el.width = clientHeight
        el.height = clientWidth - height
      } else {
        el.width = clientWidth
        el.height = clientHeight - height
      }
      this.ctx = el.getContext('2d')
      this.setCanvas()
      this.drawStart()
      this.drawing()
      this.drawEnd()
    },
    // 配置 canvas
    setCanvas() {
      const { ctx, height, direction } = this
      console.log(direction)
      // 設置背景色
      ctx.fillStyle = this.background
      // 繪製矩形
      if (direction) {
        ctx.fillRect(0, 0, clientHeight, clientWidth - height)
      } else {
        ctx.fillRect(0, 0, clientWidth, clientHeight - height)
      }

      // 設置線條顏色
      ctx.strokeStyle = this.color
      // 設置線寬
      ctx.lineWidth = this.linewidth
      // 設置線條兩頭的結束點和開始點是圓形的
      ctx.lineCap = 'round'
    },
    // 開始繪製
    drawStart() {
      const { el, ctx } = this
      el.addEventListener(
        'touchstart',
        e => {
          ctx.beginPath()
          ctx.moveTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY)
        },
        false
      )
    },
    // 繪製中
    drawing() {
      const { el, ctx } = this
      el.addEventListener(
        'touchmove',
        e => {
          ctx.lineTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY)
          ctx.stroke()
        },
        false
      )
    },
    // 繪製結束
    drawEnd() {
      const { el, ctx } = this
      el.addEventListener('touchend', () => ctx.closePath(), false)
    },
    // 清空
    clearHandle() {
      this.initCanvas()
    },
    // 保存信息
    saveImg() {
      const imgBase64 = this.el.toDataURL()
      console.log('保存簽名成功' + imgBase64)
    }
  }
}
</script>

3. css

.signHandle {
  position: relative;
  background-color: #666;
  .canvas {
    position: absolute;
    top: 0;
    left: 0;
  }
  .btn_container {
    width: 100%;
    position: fixed;
    bottom: 0;
    left: 0;
    background-color: #fff;
    display: flex;
    align-items: center;
    padding: 0 15px;
    box-sizing: border-box;
    justify-content: space-between;
    .radio_container {
      display: flex;
      /deep/ .van-radio {
        margin-right: 10px;
        &:nth-child(1) {
          /deep/ .van-icon {
            background-color: #ee0a24;
          }
        }
        &:nth-child(2) {
          /deep/ .van-icon {
            background-color: #000;
          }
        }
        &:nth-child(3) {
          /deep/ .van-icon {
            background-color: #1890ff;
          }
        }
      }
    }
  }
}
</style>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章