uniapp使用Canvas实现电子签名

文章讲述了在满足公司需求为新会员和客商增加签署协议功能时,如何克服vue-signature-pad在安卓手机上的兼容问题和uniapp对HBuilderX版本的要求限制,选择使用HTML5Canvas技术,详细介绍了在EctronicSignature.vue组件中通过JavaScriptAPI实现电子签名的过程。
摘要由CSDN通过智能技术生成

来源:

公司的一个需求,需要给新注册的会员和客商需要增加签署协议功能;

之前的思路:
1、使用vue-signature-pad来实现电子签名,但是安卓手机不兼容;
2、uniapp插件市场来实现,但是对HBuilderX的版本有要求,无奈公司只能使用3.4.7版本;

前面2种思路都不行那就用Canvas来实现

Canvass

canvas 是 html 的一个标签,它可以结合 JavaScript 提供的 canvasApi 来绘制各种各样的图形。canvas 主要用于绘制 2D 图形。注意:当你不设置 canvas 的宽高时,它的默认宽高是 300px、150px。

需要实现的功能如图:
在这里插入图片描述

代码实现

EctronicSignature.vue

<template>
  <view class="ectronic-signature">
    <canvas
      class="board-canvas"
      canvas-id="drawCanvas"
      disable-scroll="true"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
    ></canvas>
    <view class="btn">
      <u-button type="success" @click="saveImage" text="保存" class="btnItem"></u-button>
      <u-button @click="clearDrawBoard" text="清空" class="btnItem"></u-button>
    </view>
  </view>
</template>

<style lang="scss" scoped>
……
</style>
EctronicSignature.ts

 curDrawArr: any = [];
  startX: any = 0;
  startY: any = 0;
  ctx: any = {};
  begin: Boolean = false;
  bgColor: String = 'white'; //背景色
  lineWidth: Number = 4; //画笔宽度
  penMode: Boolean = true; //打开画笔  开关
  currentTab: any = 1;
  currentIndex: any = 0;
  selectPointer: any = []; //所有选中的线
  cache: any = '';
  points: any = [];

  onReady() {
    this.ctx = uni.createCanvasContext('drawCanvas', this); // 获取canvas上下文对象 vue的写法是先获取,之后通过.getContext("2d")来获取上下文
    this.cache = new Map(); // 缓存
    this.ctx.setLineWidth(this.lineWidth); // 设置画笔的粗细
  }

  // ctx.draw()绘制结果呈现在canvas
  // isReverse: 是否保留之前的像素
  draw(isReverse = false, cb) {
    this.ctx.draw(isReverse, () => {
      if (cb && typeof cb == 'function') {
        cb();
      }
    });
  }

  // 绘画 开始
  touchStart(e) {
    console.log('绘画开始', e.touches[0].x, e.touches[0].y);
    // customPrint('我能够进行绘制');
    if (this.penMode) {
      this.lineBegin(e.touches[0].x, e.touches[0].y);
      this.lineAddPoint(e.touches[0].x, e.touches[0].y);
      this.draw(true, ''); // 呈现画面
    }
    this.points.push([e.touches[0].x, e.touches[0].y]); // 存储绘制的点
    this.selectPointer.push([[e.touches[0].x, e.touches[0].y]]);
  }

  // 开始绘制线条
  lineBegin(x, y) {
    this.begin = true; // 作为一个标记代表开始绘画
    this.ctx.beginPath(); // 开始创建一个路径
    this.startX = x;
    this.startY = y;
    this.ctx.moveTo(this.startX, this.startY); // 将路径移动到画布中的指定点
    this.lineAddPoint(x, y); // 绘制线条中间添加
  }

  // 绘画 移动
  touchMove(e) {
    console.log('绘画移动', e.touches[0].x, e.touches[0].y);

    if (this.begin) {
      if (this.penMode) {
        this.lineAddPoint(e.touches[0].x, e.touches[0].y);
        this.draw(true, '');
      }
      this.points.push([e.touches[0].x, e.touches[0].y]);
      this.selectPointer[this.selectPointer.length - 1].push([e.touches[0].x, e.touches[0].y]);
    }
  }

  // 绘制线条中间添加点
  lineAddPoint(x, y) {
    this.ctx.moveTo(this.startX, this.startY);
    this.ctx.lineTo(x, y); // 增加一个新的点,然后创建一条从上次指定点到目标点的线
    this.ctx.stroke(); // 画出当前路径的边框
    this.startX = x;
    this.startY = y;
  }

  // 绘画 结束
  touchEnd(e) {
    if (this.penMode) {
      // this.curDrawArr = [];
      this.points = [];
      this.lineEnd();
    }
  }

  // 绘制线条结束
  lineEnd() {
    this.ctx.closePath(); // 关闭一个路径,关闭路径会连接起点和终点
    this.begin = false;
  }

  // 检验画布是否为空
  isCanvasBlank(canvas) {
    const blank = document.createElement('canvas'); //系统获取一个空canvas对象
    blank.width = canvas.width;
    blank.height = canvas.height;
    return canvas.toDataURL() == blank.toDataURL();  // .toDataURL()将canvas对象转换为base64位编码
  }

  // 保存 图片数据
  saveImage() {
    const _this = this;
    uni.canvasToTempFilePath({
      canvasId: 'drawCanvas',
      success: function(res) {
        console.log('res ==>', res);
        // 在H5平台下,tempFilePath 为 base64
        // this.uploadimage(res.tempFilePath,"save",page.currentTab)
        const canvas = document.getElementsByTagName('canvas')[0];
        if (_this.isCanvasBlank(canvas)) {
          uni.showToast({
            title: '请签字!!!',
            icon: 'none',
          });
          return false;
        }
      },
    });
  }

   //清除
  clearDrawBoard() {
    this.ctx.draw();
    this.selectPointer = [];
  }
  }

推荐资料

https://www.runoob.com/html/html5-canvas.html
https://uniapp.dcloud.net.cn/component/canvas.html#canvas
https://open.dingtalk.com/document/personalapp/setlinewidth

1. 创建canvas画布 在uniapp的vue文件中,我们可以使用`<canvas>`标签来创建canvas画布。我们需要设置画布的宽高,以及给画布一个唯一的id。 ``` <canvas id="snow-canvas" canvas-id="snow-canvas" :style="{width: canvasWidth + 'px', height: canvasHeight + 'px'}"></canvas> ``` 在`data`中定义`canvasWidth`和`canvasHeight`,并在`mounted`生命周期函数中获取canvas的上下文。 ``` data() { return { canvasWidth: 0, canvasHeight: 0, ctx: null, snows: [] } }, mounted() { uni.getSystemInfo({ success: (res) => { this.canvasWidth = res.windowWidth this.canvasHeight = res.windowHeight const context = uni.createCanvasContext('snow-canvas', this) this.ctx = context } }) } ``` 2. 定义雪花类 我们可以定义一个雪花类,包含雪花的位置、大小、速度等属性,以及雪花的绘制方法。 ``` class Snow { constructor(canvasWidth, canvasHeight) { this.x = Math.random() * canvasWidth this.y = Math.random() * canvasHeight this.radius = Math.random() * 3 + 1 this.speed = Math.random() * 2 + 1 this.alpha = Math.random() * (1 - 0.5) + 0.5 } draw(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})` ctx.fill() } update(canvasWidth, canvasHeight) { this.y += this.speed if (this.y > canvasHeight) { this.y = 0 this.x = Math.random() * canvasWidth } } } ``` 3. 在canvas中绘制雪花 在`mounted`生命周期函数中,我们可以循环创建多个雪花对象,并将它们存储在`snows`数组中。 ``` for (let i = 0; i < 100; i++) { const snow = new Snow(this.canvasWidth, this.canvasHeight) this.snows.push(snow) } ``` 在`drawSnows`方法中,我们可以循环遍历`snows`数组,对每个雪花对象进行绘制和更新。 ``` drawSnows() { this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) for (let i = 0; i < this.snows.length; i++) { const snow = this.snows[i] snow.draw(this.ctx) snow.update(this.canvasWidth, this.canvasHeight) } uni.drawCanvas({ canvasId: 'snow-canvas', actions: this.ctx.getActions() }, this) } ``` 4. 实现动画效果 我们可以使用`setInterval`定时器来不断调用`drawSnows`方法,实现雪花飘落的动画效果。 ``` setInterval(() => { this.drawSnows() }, 30) ``` 完整代码如下: ``` <template> <canvas id="snow-canvas" canvas-id="snow-canvas" :style="{width: canvasWidth + 'px', height: canvasHeight + 'px'}"></canvas> </template> <script> class Snow { constructor(canvasWidth, canvasHeight) { this.x = Math.random() * canvasWidth this.y = Math.random() * canvasHeight this.radius = Math.random() * 3 + 1 this.speed = Math.random() * 2 + 1 this.alpha = Math.random() * (1 - 0.5) + 0.5 } draw(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false) ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})` ctx.fill() } update(canvasWidth, canvasHeight) { this.y += this.speed if (this.y > canvasHeight) { this.y = 0 this.x = Math.random() * canvasWidth } } } export default { data() { return { canvasWidth: 0, canvasHeight: 0, ctx: null, snows: [] } }, mounted() { uni.getSystemInfo({ success: (res) => { this.canvasWidth = res.windowWidth this.canvasHeight = res.windowHeight const context = uni.createCanvasContext('snow-canvas', this) this.ctx = context } }) for (let i = 0; i < 100; i++) { const snow = new Snow(this.canvasWidth, this.canvasHeight) this.snows.push(snow) } setInterval(() => { this.drawSnows() }, 30) }, methods: { drawSnows() { this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) for (let i = 0; i < this.snows.length; i++) { const snow = this.snows[i] snow.draw(this.ctx) snow.update(this.canvasWidth, this.canvasHeight) } uni.drawCanvas({ canvasId: 'snow-canvas', actions: this.ctx.getActions() }, this) } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*neverGiveUp*

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值