微信小程序实现电子签名功能

具体效果如下

1、 未签名前


a1.png

2、签名中


a2.png

3、完成签名
a3.png

使用canvas 功能来实现

1、获取canvas对象

const query = this.createSelectorQuery();
      query.select('#myCanvas').fields({id:true,
        node:true,
        size:true,
        dataset: true,
        size: true})
        .exec((res) => {
           const canvas = res[0].node
          const ctx = canvas.getContext('2d')
  })    

2、初始化提示语

    this.data.ctx.lineWidth = 3; // 字体粗细
      this.data.ctx.font = '30px Arial'; // 字体大小,
      this.data.ctx.fillStyle = '#ecf0ef'; // 填充颜色
      var metrics = this.data.ctx.measureText(this.data.writeTips);
      var textWidth = metrics.width;
      var textHeight = 30; 
      var x = (this.data.canvas.width / 2 / this.data.dpr) - textWidth / 2;
      var y = this.data.canvas.height / 2 / this.data.dpr + textHeight / 2; 
      this.data.ctx.fillText(this.data.writeTips,   x ,   y)
      this.data.ctx.closePath();

绘制

绑定 start、move、end事件

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

3、保存成图片

 wx.canvasToTempFilePath({
          x: 0,
          y: 0,
          width: this.data.canvas.width,
          height: this.data.canvas.height,
          // canvasId: 'myCanvas',
          canvas:this.data.canvas,
          fileType:'png',
          quality:1,
          success:(res)=> {
           
          },
          fail(err) {
              console.log('canvas 转换成图片',err);
          }
      },this)

4、封装成组件(完整代码)

1、wxml

<view class="modal-content-area">
  <view class="modal-content">
    <canvas type="2d" id="myCanvas"  class="modal-canvas"  disable-scroll="{{true}}" 
    bindtouchstart="onStart" 
    bindtouchmove="onMove" 
    bindtouchend="onEnd"></canvas>
  </view>
  <view class="modal-bottom">
      <view class="model-btn modal-clear {{!hasDraw?'disabled':''}}" bindtap="onClear">重新签名</view>
      <view class="model-btn modal-confirm {{!hasDraw?'disabled':''}}" bindtap="onConfrim" wx:if="{{!hasConfirm}}"> 确认签名</view>
    </view> 
</view>

2、scss

.modal-content-area {
  z-index: 5;
  width: 100%;
  background: #F5F7FB;
  position: relative;
  .modal-content {
    width: 100%;
    height: 300px;
    background: #ffffff;
    position: relative;
    .toast {
      position: absolute;
      top:0;
      left: 0;
      right:0;
      bottom:0;
      z-index: 100;
      font-size: 26px;
      font-family: PingFangSC-Regular, PingFang SC;
      font-weight: 400;
      color: rgba(58, 65, 85, 0.4);
      display: flex;
      align-items: center;
      justify-content: center;

    }
  }
  .modal-bottom {
    display: flex;
    justify-content: flex-end;
    width: 100%;
    padding:20px;
    box-sizing: border-box;
    .model-btn{
      font-size: 14px;
      padding:10px 15px;
      box-sizing: border-box;
      &.modal-clear {
        background: rgba(103, 149, 255, 0.2);
        color: #3670F5;
        margin-right: 12rpx;
        &.disabled{
          opacity: 0.5;
        }
      }
      &.modal-confirm {
        background: rgba(103, 149, 255,1);
        color: #FFFFFF;
        &.disabled{
          background: rgba(103, 149, 255,.5);
        }
      }
    }
  }
}
.signature-modal {
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 3;
  background: blue;
}

.modal-mask {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 4;
  background-color: rgba(0, 0, 0, .5);
}
.modal-canvas {
  width: 100%;
  height: 100%;
  background: transparent;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 4px;
  z-index: 2;
  position: relative;
}
.flex_btn {
  display: flex;
  align-items: center;
}

3、js

Component({
  /**
   * 组件的初始数据
   */
  data: {
      tempFilePath: '',
      writeTips: '请在此区域签名',
      writeTipsTrue: true,
      hasDraw: false,
      hasConfirm:false,
      canvasName: '#handWriting',
      ctx: '',
      dpr:'1',
      canvas:'',
      canvasWidth: 0,
      canvasHeight: 0,
      startPoint: {
          x: 0,
          y: 0,
      },
      selectColor: 'black',
      lineColor: '#000', // 颜色
      lineWidth: 1, // 笔记倍数
      radius: 2, //画圆的半径
  },
  // 初始化画布
  attached() {
    this.setData({
        hideModal: false,
        hasConfirm:false
    })
  this.initCanvas()
   this.triggerEvent('reset')
  },
  // 方法列表
  methods: {
    ininIips(){
      this.data.ctx.lineWidth = 3; // 字体粗细
      this.data.ctx.font = '30px Arial'; // 字体大小,
      this.data.ctx.fillStyle = '#ecf0ef'; // 填充颜色
      var metrics = this.data.ctx.measureText(this.data.writeTips);
      var textWidth = metrics.width;
      var textHeight = 30; 
      var x = (this.data.canvas.width / 2 / this.data.dpr) - textWidth / 2;
      var y = this.data.canvas.height / 2 / this.data.dpr + textHeight / 2; 
      this.data.ctx.fillText(this.data.writeTips,   x ,   y)
      this.data.ctx.closePath();
    },
    initCanvas(){
      const query = this.createSelectorQuery();
      query.select('#myCanvas').fields({id:true,
        node:true,
        size:true,
        dataset: true,
        size: true})
        .exec((res) => {
          const canvas = res[0].node
          const ctx = canvas.getContext('2d')
          const dpr=wx.getWindowInfo().pixelRatio||1;//缩放级
          canvas.width = res[0].width * dpr 
          canvas.height= res[0].height * dpr
          ctx.scale(dpr, dpr);
          ctx.lineGap ='round';
          ctx.lineJoin ='round';
          this.setData({
            ctx,
            canvas,
            dpr
          })
          this.ininIips()
      
      })
     
    },
    onStart(event) {
      // console.log('kaishi',event)
        if (event.type != 'touchstart') return false;
        if(this.data.hasConfirm) {
          wx.showToast({
            title: '已经签过名字了呦,请勿重复签名!',
            icon:'none'
          })
          return
        }
        let currentPoint = {
          x: event.touches[0].x,
          y: event.touches[0].y
        }
           //清空画布
          if(this.data.writeTipsTrue){
            this.data.ctx.clearRect(0, 0, this.data.canvas.width,this.data.canvas.height);
            this.data.ctx.linewidth=this.data.lineWidth;//字体粗细    
            this.data.ctx.strokeStyle = this.data.selectColor
            this.data.ctx.font = '20px Arial'; // 字体大小,
            this.setData({
              writeTipsTrue:false
            })
          }
        this.data.ctx.fillStyle = 'rgba(0, 0, 0, .1)';
        this.drawCircle(currentPoint);
        this.setData({
          startPoint: currentPoint,
          hasDraw: true, //签字了
        });
      },
    onMove(event) {
      // console.log('移动')
        if (event.type != "touchmove") return false;
        if(this.data.hasConfirm) return
        let {
            startPoint
        } = this.data
        let currentPoint = {
            x: event.touches[0].x,
            y: event.touches[0].y
        }
        // console.log('===',startPoint,currentPoint)
        this.drawLine(startPoint, currentPoint)
        this.setData({
            startPoint: currentPoint
        })
    },
    onDown() {},
    onEnd(e) {
      this.data.ctx.stroke();
    },
    drawCircle(point) { //这里负责点
        this.data.ctx.beginPath();
        this.data.ctx.fillStyle = this.data.lineColor
       //笔迹粗细由圆的大小决定
        this.data.ctx.arc(point.x, point.y, this.data.radius, 0, 2 * Math.PI);
        this.data.ctx.fill();
        this.data.ctx.closePath();
        // this.data.ctx.draw(true)
    },
    drawLine(sourcePoint, targetPoint) {
     
        this.drawCircle(targetPoint);
        this.data.ctx.beginPath();
      
        this.data.ctx.lineWidth = this.data.radius * 2
     
        this.data.ctx.moveTo(sourcePoint.x, sourcePoint.y);
        this.data.ctx.lineTo(targetPoint.x, targetPoint.y);
        this.data.ctx.stroke();
        this.data.ctx.strokeStyle = this.data.lineColor
        this.data.ctx.closePath();
        // console.log('绘制线条',this.data.ctx)
    },
    onClear() {
      if(!this.data.hasDraw){
        wx.showToast({
          title: '还未签字哟!',
          icon: 'none',
          mask: true
        })
        return
      }
        //清空画布
        this.data.ctx.clearRect(0, 0, this.data.canvas.width,this.data.canvas.height);
        this.data.ctx.fillStyle = 'rgba(0, 0, 0, .1)';
        
        this.setData({
            hasDraw: false, //未签字
            writeTipsTrue:true,
            hasConfirm:false
        })
        this.ininIips()
        this.triggerEvent('confirm','')
    },
    onConfrim() {
    
      if (!this.data.hasDraw) {
        wx.showToast({
            title: '还未签字哟!',
            icon: 'none',
            mask: true
        })
        return
      } else {
        this.setData({
          hideModal:false
        })
        this.saveToImage();
      }
    },
    saveToImage() {
      wx.canvasToTempFilePath({
          x: 0,
          y: 0,
          width: this.data.canvas.width,
          height: this.data.canvas.height,
          // canvasId: 'myCanvas',
          canvas:this.data.canvas,
          fileType:'png',
          quality:1,
          success:(res)=> {
            // console.log(res, "res.tempFilePath");
            if (res.tempFilePath) {
              wx.showToast({
                title: '签名保存成功!',
                icon: 'none',
                mask: true
              })
              this.setData({
                hasConfirm:true
              })
              this.triggerEvent('confirm',res.tempFilePath)
            }
          },
          fail(err) {
              console.log('canvas 转换成图片',err);
          }
      },this)
    }
  }
})

5、引入使用

wxml

 <signature bindconfirm = "onSignatureConfrim" id="signature" />

JSON文件

 "usingComponents": {
   "signature": "./components/signature/index"
}

js 文件

 onSignatureConfrim(e){
      console.log('签名',e)
 },
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容