uniapp使用Canvas实现电子签名

来源:

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

之前的思路:
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152948.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么小型企业应该拥抱数字化转型?

在当今飞速发展的商业环境中&#xff0c;数字化转型已经成为各种规模组织的必然选择。特别是小型企业&#xff0c;通过数字化转型&#xff0c;可以在保持竞争力、提高运营效率并开启新的增长机会方面获益匪浅。本文探讨了数字化转型的概念&#xff0c;强调了它对小型企业的重要…

测试小白必看:自动化测试入门基础知识

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

小型企业网络搭建方案

在这个日益数字化和连接的世界里&#xff0c;一个稳固的小型企业网络是实现高效运作的关键支柱。不论您是在经营一家初创公司还是小型企业&#xff0c;一个可靠的企业网络都是保证顺畅沟通、数据分享以及访问在线资源的重要因素。本篇文章将会引导您完成构建一个小型企业网络的…

C++入门第七篇--STL模板--vector模拟实现

前言&#xff1a; 有了前面的string库的介绍&#xff0c;在这里我就不再介绍vector库了&#xff0c;而是直接模拟实现了。 vector库的概念和作用&#xff1a; vector库是针对于数组的数据类型的容器&#xff0c;它有点类似我们曾经实现过的顺序表&#xff0c;你完全可以按照…

Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码)

对应的教程文章: https://codelabs.developers.google.com/your-first-webgpu-app?hlzh-cn#7 对应的源码执行效果: 对应的教程源码: 此处源码和教程本身提供的部分代码可能存在一点差异。点击画面&#xff0c;切换效果。 class Color4 {r: number;g: number;b: number;a…

挑战字节软件测试岗,原来这么轻松...

当前就业环境&#xff0c;裁员、失业消息满天飞&#xff0c;好像有一份工作就不错了&#xff0c;更别说高薪了。其实这只是一方面&#xff0c;而另一方面&#xff0c;各大企业依然求贤若渴&#xff0c;高技术人才依然紧缺&#xff0c;只要你技术过硬&#xff0c;拿个年薪50w不是…

锁之间的故事

目录 常用锁策略 1.乐观锁 VS 悲观锁 2.轻量级锁 VS 重量级锁 3.自旋锁 VS 挂起等待锁 4.互斥锁 VS 读写锁 5.公平锁 VS 非公平锁 6.可重入锁 VS 可重入锁 CAS ABA问题 Synchronized原理 1. 锁升级/锁膨胀 2.锁消除 3.锁粗化 常用锁策略 1.乐观锁 VS 悲观锁 站在…

二叉树相关

一、概念 二、题目 2.1 把数组转换成二叉树 2.2.1 使用队列方式 public static Node getTreeFromArr2(int[] arr) {if (arr null || arr.length 0) {return null;}LinkedList<Node> quque new LinkedList<>();Node root new Node(arr[0]);quque.add(root);in…

有大量虾皮买家号想防关联该怎么做?

Shopee平台规定一个买家只能拥有一个买家号&#xff0c;如果一台电脑或者一个手机同时登录好几个买家号&#xff0c;那么很有可能就会关联封号的。那么有大量虾皮买家号想防关联该怎么做&#xff1f; 如果想要运用大量的shopee买家号来操作&#xff0c;那么需要使用有防指纹技术…

利用vscode连接远程服务器进行代码调试

文章目录 一、vscode下载二、连接服务器1. 安装remote development套件2. 配置ssh3. 连接服务器4. 打开服务器文件路径 三、支持GUI显示1. windows系统安装xserver服务&#xff1a;可以用xming或VcXsrv2. windows系统(安装了vscode的系统)下安装插件3. vscode实现免密登录远程服…

<蓝桥杯软件赛>零基础备赛20周--第6周--数组和队列

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

Ansys Speos | 如何利用Speos联合optiSLang进行光导优化设计

在本例中&#xff0c;我们将使用 Speos 和 optiSLang 实现光导的设计优化&#xff0c;以实现汽车日行灯、内饰氛围灯等的光导设计&#xff0c;并改善光导亮度的均匀性&#xff0c;以自动优化设计的方式实现更好的照明外观。 概述 在汽车照明应用中&#xff0c;日行灯是一个独特…

[文件读取]Grafana未授权任意文件读取

1.1漏洞描述 漏洞编号Grafana未授权任意文件读取漏洞类型文件读取漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后&#xff0c;Grafana可以在网络浏览器里显示数据图表和警告。 Grafana 存在…

【自定义列表头】vue el-table表格自定义列显示隐藏,多级表头自定义列显示隐藏,自由搭配版本和固定列版本【注释详细】

前言 功能介绍 最近遇到一个功能&#xff0c;需要的是把表格的列可以配置&#xff0c; 用户可以根据自己想要看的数据来改变表头列显示哪些隐藏哪些。 于是我做了两个版本。第一个版本是自由搭配的。 就是提前顶号所有的列&#xff0c;然后自己可以拖拽到想要位置顺序。 也可以…

打开IE浏览器

原文地址&#xff1a;https://www.xiaoheiwoo.com/windows-11-internet-explorer/#:~:text%E5%A6%82%E4%BD%95%E5%9C%A8%20Windows11%20%E4%B8%AD%E5%90%AF%E7%94%A8%20IE%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%843%E7%A7%8D%E6%96%B9%E6%B3%95%201%20%E6%96%B9%E6%B3%95%E4%B8%80…

俄罗斯方块

一.准备工作 先创建一个新的Java项目命名为“俄罗斯方块”。再在该项目中创建一个文件夹命名为”images”&#xff0c;并将所需的图片素材拖入该文件夹。 二.代码呈现 编写小方块类&#xff1a; import java.awt.image.BufferedImage;/*** 描述:小方块类* 属性&#xff1a;…

Nas搭建webdav服务器并同步Zotero科研文献

无需云盘&#xff0c;不限流量实现Zotero跨平台同步&#xff1a;内网穿透私有WebDAV服务器 文章目录 无需云盘&#xff0c;不限流量实现Zotero跨平台同步&#xff1a;内网穿透私有WebDAV服务器一、Zotero安装教程二、群晖NAS WebDAV设置三、Zotero设置四、使用公网地址同步Zote…

不会代码的时候,如何使用Jmeter完成接口测试?

1.接口测试简介 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 2.接口测试流程 接口测试…

推荐一个非常好用的uniapp的组件库【TMUI3.0】

文章目录 前言官网地址如何使用&#xff1f;注意事项后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;前端系列文章 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果…

Java虚拟机运行时数据区结构详解

Java虚拟机运行时数据区结构如图所示 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 多线程切换时&#xff0c;为了能恢复到正确的执行位置&#xff0c;每条线程…