微信小程序Canvas实现手写签名

  • Post author:
  • Post category:小程序




微信小程序Canvas实现手写签名



功能描述

点击按钮显示弹窗,弹窗主体为签名板,底部两个按钮为清除和保存。

清除按钮:清空签名板

保存按钮:保存签名为图片,并关闭弹窗



关键点分析

  1. Canvas实现手写签名部分:签名不一定具有连续性;判断是否签名;签名需要明显一些
  2. 微信小程序api实现保存为图片
  3. 因为是弹窗显示签名板,方便以后其他地方调用就封装为小程序组件使用



功能实现

1.signature组件wxml

<view class="signature-modal" hidden="{{hideModal}}">
  <view class="modal-mask" bindtap="closeModal"></view>
  <view class="modal-content-area">
    <view class="modal-title">签名板</view>
    <view class="modal-content">
      <canvas canvas-id="signature" class="modal-canvas" disable-scroll="{{true}}" id="handWriting" bindtouchstart="scaleStart"
        bindtouchmove="scaleMove" bindtouchend="scaleEnd" ></canvas>
      <view class="modal-bottom">
        <view class="modal-btn modal-clear" bindtap="clearCanvas">清除</view>
        <view class="modal-btn modal-confirm" bindtap="saveToImage">上传</view>
      </view>
    </view>
  </view>
</view>

2.signature组件样式

.signature-modal {
 position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-mask{
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 4;
  background-color: rgba(0, 0, 0, .5);
}
.modal-content-area{
  position: relative;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  width: 700rpx;
  min-height: 550rpx;
  background-color: #ffffff;
  border-radius: 18rpx;
}
.modal-content {
  width: 100%;
  height: 100%;
}
.modal-canvas {
  width: 650rpx;
  height: 420rpx;
  margin: 0 auto;
  margin-bottom: 30rpx;
  border-radius: 18rpx;
  box-sizing: border-box;
  overflow: hidden;
  background-color: rgba(0, 0, 0, .1);
}
.modal-bottom {
  display: flex;
  width: 100%;
  height: 70rpx;
  justify-content: space-between;
  align-items: center;
  background-color: #869ee9;
  color: #ffffff;
}
.modal-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 50%;
  height: 100%;
}
.modal-btn:nth-child(1) {
  border-right: 1rpx solid #ffffff;
}

3.signature组件js文件

// component/signature/index.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
    tempFilePath:'',
    hideModal: true,
    hasDraw: false,
    canvasName: '#handWriting',
    ctx: '',
    canvasWidth: 0,
    canvasHeight: 0,
    startPoint: {
      x: 0,
      y: 0,
    },
    selectColor: 'black',
    lineColor: '#1A1A1A', // 颜色
    lineSize: 1, // 笔记倍数
    radius: 5, //画圆的半径
  },
  lifetimes: {
    ready() {
      let that = this
      let query = wx.createSelectorQuery().in(this); //获取自定义组件的SelectQuery对象
      this.canvasCtx = wx.createCanvasContext('signature', that)

      // 设置线的样式
      this.canvasCtx.setLineCap("round");
      this.canvasCtx.setLineJoin("round");
      // 初始化颜色
      this.canvasCtx.setStrokeStyle(that.data.selectColor);
      // 初始化粗细

      query.select('.modal-canvas').boundingClientRect(rect => {
  
        this.setData({
          canvasWidth: rect.width,
          canvasHeight: rect.height,
        });
      }).exec();
    }
  },

  /**
   * 组件的方法列表
   */
  methods: {
    show() {
      this.setData({
        hideModal: false
      })
    },
    closeModal() {
      this.clearCanvas()
      this.setData({
        hideModal: true
      })
      this.triggerEvent('closeSignature')
    },
    scaleStart(event) {
      if (event.type != 'touchstart') return false;
      let currentPoint = {
        x: event.touches[0].x,
        y: event.touches[0].y
      }
      // this.data.ctx.moveTo(currentPoint.x, currentPoint.y)
      this.drawCircle(currentPoint);
      this.setData({
        startPoint: currentPoint,
        hasDraw: true, //签字了
      });
    },
    mouseDown() {},
    scaleEnd(e) {
      this.setData({
        isStart: true
      })
    },
    scaleMove(event) {
      if (event.type != "touchmove") return false;

      let {
        startPoint
      } = this.data
      let currentPoint = {
        x: event.touches[0].x,
        y: event.touches[0].y
      }

      this.drawLine(startPoint, currentPoint)
      this.setData({
        startPoint: currentPoint
      })
    },
    drawCircle(point) { //这里负责点
      let ctx = this.canvasCtx;
      ctx.beginPath();
      // ctx.fillStyle = this.data.lineColor;
      ctx.setFillStyle(this.data.lineColor)
      //笔迹粗细由圆的大小决定
      ctx.arc(point.x, point.y, this.data.radius, 0, 2 * Math.PI);
      ctx.fill();
      ctx.closePath();
      ctx.draw(true)
    },
    drawLine(sourcePoint, targetPoint) {
      let ctx = this.canvasCtx;
      this.drawCircle(targetPoint);
      ctx.beginPath();
      // ctx.strokeStyle = this.data.lineColor;
      ctx.setStrokeStyle(this.data.lineColor)
      // ctx.lineWidth = this.data.radius * 2; //这里乘2是因为线条的粗细要和圆的直径相等
      ctx.setLineWidth(this.data.radius*2)
      ctx.moveTo(sourcePoint.x, sourcePoint.y);
      ctx.lineTo(targetPoint.x, targetPoint.y);
      ctx.stroke();
      ctx.closePath();
      // ctx.draw()
    },
    clearCanvas() { //清空画布
      let ctx = this.canvasCtx;
      ctx.clearRect(0, 0, this.data.canvasWidth, this.data.canvasHeight);
      ctx.fillStyle = 'rgba(0, 0, 0, .1)';
      ctx.draw()
      this.setData({
        hasDraw: false //未签字
      })
    },
    getTempFile(){
      return {
        tempFilePath:this.data.tempFilePath
      }
    },
    saveToImage() {
      let {
        hasDraw,
        canvasHeight,
        canvasWidth
      } = this.data
      let that=this
      if (!hasDraw) {
        wx.showToast({
          title: '还未签字哟!',
          icon: 'none',
          mask: true
        })
        return
      }
      wx.canvasToTempFilePath({
        x: 0,
        y: 0,
        width: canvasWidth,
        height: canvasHeight,
        canvasId: 'signature',
        success(res) {
          if(res.tempFilePath){
            that.setData({
              tempFilePath:res.tempFilePath
            })
            that.triggerEvent('confirm')
          }
          console.log(res.tempFilePath)
        },
        fail(err) {
          console.log(err);
        }
      },that)
    }
  }
})

ps:考虑到是之前的老项目,这里选择的Canvas版本为老版本的API,等有空了用新版的Canvas实现一下


参考文章



版权声明:本文为qq_37603210原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。