微信小程序用 canvas 实现手写签名弹框(全网最最最详细!!)

news2024/12/24 9:35:51

文章目录

  • 一、签字面板效果图
  • 二、WXML文件
  • 三、JS文件
  • 四、WXSS文件
  • 五、小Tips ~

一、签字面板效果图

二、WXML文件

🌸点击弹出手写签名面板事件

<van-button type="default" bindtap="handWrittenSign">点击弹出手写签名弹框</van-button>

🌸手写签名面板 Popup 弹出层(vant

<van-popup show="{{ showWritten }}" position="bottom" custom-class="writtenArea" bind:close="writtenSignClose">
    <view class="agree-area">
        <text>请签字以确认同意用户服务协议</text>
    </view>
    <canvas type="3d" canvas-id="myCanvas" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" class="canvas-area"></canvas>
    <view class="written-btn-area">
        <van-button type="default" custom-class="write" bindtap="resetWrite" size="small">重置</van-button>
        <van-button plain type="info" custom-class="write" bindtap="cancelWrite" size="small">取消</van-button>
        <van-button type="info" custom-class="write" bindtap="confirmWrite" size="small">确认</van-button>
    </view>
</van-popup>

三、JS文件

Page({
	/**
     * 页面的初始数据
     */
    data: {
        showWritten: false,  //展示手写签名弹框
        startX: undefined, // 线条的坐标点
        startY: undefined,
        userSignatureId: undefined, // 签名图片id
        screenWidth: undefined, // 屏幕宽
        screenHeight: undefined, // 屏幕高
    },
	
	/**
     * 事件
     */
	
	// 点击弹出手写签名弹框
	handWrittenSign() {
        this.setData({ showWritten: true });
        this.initCanvas();
    },
	
	// 点击蒙层关闭弹框
	writtenSignClose() {
        this.setData({ showWritten: false });
        this.resetWrite();
    },

	// 初始化画布
	initCanvas() {
        const context = wx.createCanvasContext('myCanvas', this);
        context.setStrokeStyle('#000'); // 设置线条样式
        context.setLineWidth(3); // 线条粗细
        context.setLineCap('round'); // 设置线条端点样式
        context.setLineJoin('round'); // 设置线条交点样式(拐角)
        context.beginPath(); // 开始新的绘制路径
        context.clearRect(0, 0, this.data.startX, this.data.startY); // 清除画布上的内容
        context.draw(); // 绘制到canvas上
    },

	// 手指触摸动作开始
	onTouchStart(e) {
        const context = wx.createCanvasContext('myCanvas', this);

        context.setStrokeStyle('#000000');
        context.setLineWidth(3);

        this.setData({
            'startX': e.touches[0].x,
            'startY': e.touches[0].y,
        })
    },

	// 手指触摸后移动
	onTouchMove(e) {
        const context = wx.createCanvasContext('myCanvas', this);
        
        context.moveTo(this.data.startX, this.data.startY);
        context.lineTo(e.touches[0].x, e.touches[0].y);
        context.stroke();
        context.draw(true);
        
        this.setData({
            'startX': e.touches[0].x,
            'startY': e.touches[0].y,
        })
    },

	// 手指触摸动作结束
	onTouchEnd() {
        const context = wx.createCanvasContext('myCanvas', this);
        context.closePath();
        context.draw(true);
    },

	// 重置签名
    resetWrite() {
        const context = wx.createCanvasContext('myCanvas', this);
        let { screenWidth, screenHeight } = this.data;
        // 清空画布
        context.clearRect(0, 0, screenWidth, screenHeight);
        context.beginPath();
        // 绘制白色背景
        context.setFillStyle('#ffffff'); // 填充色 白色
        context.fillRect(0, 0, screenWidth, screenHeight); // 绘制一个矩形清除画布内容
        context.setLineWidth(3);  // 线条粗细
        // 绘制提示文字(根据需求可要可不要)
        context.setFontSize(14);
        context.setFillStyle('#999999');
        context.setTextAlign('center');
        context.fillText('请在此区域签名', this.data.startX / 2, this.data.startY / 2);
        // 绘制到canvas上
        context.draw();
    },

	// 取消签名
    cancelWrite() {
		this.setData({ 
            showWritten: false
        })
        const context = wx.createCanvasContext('myCanvas', this);
        let { screenWidth, screenHeight } = this.data;
         // 清空画布
        context.clearRect(0, 0, screenWidth, screenHeight);
        context.beginPath();
        context.setFillStyle('#ffffff');
        context.fillRect(0, 0, screenWidth, screenHeight);
        context.setLineWidth(3);
        // 绘制到canvas上
        context.draw();
    },
  
  	// 确认提交
    confirmWrite() {
		this.setData({showWritten: false});  // 关闭手写面板
        wx.canvasToTempFilePath({
            canvasId: 'myCanvas',
            success: function(res) {
              const tempFilePath = res.tempFilePath; // 取图片文件路径
              // 将 tempFilePath 传递给后端接口
    		  uploadFile({fileType: 'image', tempFilePath: tempFilePath})
                .then(file => {
                // 由于签名面板在表单中,提交表单需要传签名文件id,在这里赋值
                    that.setData({ userSignatureId: file.id })
                })
                .catch(err => {
                    console.error(err)
                })
            }
        });
    },  
	
	/**
     * 生命周期函数--监听页面显示
     */
    onShow() {
        // 获取屏幕的宽高 可结合画布在父元素的百分比获取实际宽高度;若画布为固定值,以上所用宽高度可不用在此获取,直接写死即可。
        const systemInfo = wx.getSystemInfoSync();
        this.setData({
            screenWidth: systemInfo.screenWidth,
            screenHeight: systemInfo.screenHeight
        })
    },
})

🌸最后可以使用 canvas 组件的 toTempFilePath 方法将 canvas 画布内容保存为临时文件路径,然后将该路径传递给后端即可。

四、WXSS文件

.writtenArea {
    height: 60%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-direction: column;
}

.canvas-area {
    width: 90%;
    flex: 1;
    border: 1px solid #ccc;
}

.write {
    width: 180rpx;
}

.written-btn-area {
    width: 100%;
    display: flex;
    justify-content: space-between;
    padding: 0 40rpx;
    margin-top: 20rpx;
}

.agree-area {
    width: 90%;
    margin: 20rpx 0;
    text-align: left;
    font-size: 36rpx;
    font-weight: 700;
}

五、小Tips ~

💡 文中触摸板的方法中多次获取canvas的上下文,即const context = wx.createCanvasContext('myCanvas', this);,我这里是直接在函数内部定义方法,内部使用。也可全局定义,使用wx.createCanvasContext 获取绘图上下文 context全局使用,如下:

Page({
  data: {
    canvasContext: null // canvas上下文对象
  },
  
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {
    this.data.canvasContext = wx.createCanvasContext('myCanvas', this);
  },
  // ...
})

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过电商API接口,代购系统可以获取到商品、订单、物流等多种信息

代购系统需要接入电商API接口&#xff0c;以便实现与电商平台的交互与数据共享。通过电商API接口&#xff0c;代购系统可以获取到商品、订单、物流等多种信息&#xff0c;同时也可以完成下单、支付、退货等多种操作。 对于用户来说&#xff0c;接入电商API接口可以提供以下好处…

Linux性能基础:CPU、内存、磁盘等概述

目录 1. CPU 1.1. CPU常见品牌 1.2. CPU性能概述 ① CPU主频 ② CPU位数 ③ CPU缓存指令集 ④ CPU核心数 ⑤ IPC 1.3. 上下文切换 1.4. 进程与线程 ① 进程 ② 线程 2. 内存 2.1. 内存主频 2.2. 内存带宽 2.3. 内存分类 2.4. 内存的分配 2.5. 内存的回收 2.6. 内存泄漏 3. 磁盘…

Mysql系列---【linux安装mysql8.1.0】

1.下载安装包 官网链接: https://dev.mysql.com/downloads/mysql/ 注意:linux查看glibc版本的命令: rpm -qa|grep glibc 2.把下载的包上传到/opt/app/middles目录下 注意: 速度可能有点慢。 3.解压压缩包 #xz解压 xz -d mysql-8.1.0-linux-glibc2.17-x86_64.tar.xz #tar解压 t…

c++仿写小波分解和去噪代码(只使用基础库)

小波分解C版本 C代码 参考了一些Github的代码 最终代码可从链接下载。 main函数如果打不开的话&#xff0c;使用 #include <iostream> #include <vector> #include <fstream> #include "wavelet.h"int main() {// 读取原始信号数据std::ifstrea…

用户生成内容vs专业生成内容:谁主海外社媒营销江山?

随着社交媒体和数字营销的崛起&#xff0c;海外社媒营销已经成为各大品牌推广产品和服务的一种主要方式。然而&#xff0c;在选择最佳策略时&#xff0c;品牌经常会面临一个关键的问题&#xff1a;是使用用户生成内容&#xff08;UGC&#xff09;还是专业生成内容&#xff08;P…

fix: prosemirror adds two extra spaces when paste

bug 项目使用 prosemirror&#xff0c;复制 NodeSelection 时&#xff0c;会在末尾多出两个空格。 NodeSelection prosemirror 的 Selection 是抽象类&#xff0c;它有三个子类 TextSelection 最常见的NodeSelection 指向单一节点的选区。设置了 selectable true 的节点&am…

C++项目实战——基于多设计模式下的同步异步日志系统-⑪-日志器管理类与全局建造者类设计(单例模式)

文章目录 专栏导读日志器建造者类完善单例日志器管理类设计思想单例日志器管理类设计全局建造者类设计日志器类、建造者类整理日志器管理类测试 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计…

达梦数据库适配ServiceStack框架

注&#xff1a;达梦的驱动版本请使用2023第四季度及以后版本驱动才可以 ServiceStack介绍 ServiceStack官网&#xff1a; https://github.com/ServiceStack/ServiceStack ServiceStack是一个开源的十分流行的WebService框架&#xff0c;引用其官网的介绍&#xff1a;“Servic…

创建React Native的第一个hello world工程

创建React Native的第一个hello world工程 需要安装好node、npm环境 如果之前没有安装过react-native-cli脚手架的&#xff0c;可以按照下述步骤直接安装。如果已经安装过的&#xff0c;但是在使用这个脚手架初始化工程的时候遇到下述报错的话 cli.init(root, projectname);…

FPGA中的LUT查找表工作原理。

在RAM中填入1110,后续的不同AB组合Y输出对应的值&#xff0c;实现上面逻辑表达式的功能。

windows编译ollvm笔记

准备工作 1.找到Android SDK目录配置好cmake环境变量 E:\AndroidSDK\cmake\3.18.1&#xff08;E:\AndroidSDK为 Android SDK目录地址&#xff09;。 下载llvm-mingw编译环境(gcc编译器的windows版本&#xff0c;即可以在windows平台上使用gcc编译器)&#xff0c;下载地址&…

Linux安装rpm包在线安装mysql5.7

以前安装过mysql 前言&#xff1a;检查以前是否装有mysql rpm -qa|grep -i mysql安装了会显示&#xff1a;   bt-mysql57-5.7.31-1.el7.x86_64 停止mysql服务和删除之前安装的mysql rpm -e bt-mysql57-5.7.31-1.el7.x86_64查找并删除mysql相关目录 find / -name mysql/va…

QT开发工业自动化控制软件的几个常用模块

最近两年一直从事工业自动化制造企业的软件开发&#xff0c;发现跟以前开发网络软件还是有较大的区别&#xff0c;重点就是在一些细的方面&#xff0c;比如架构、模块、通讯之类的。下面举几个例子&#xff1a; 1、数字键盘&#xff08;替代普通键盘的小数字键盘&#xff09; …

Jenkins 内存占用

查看内存占用 # ps aux | grep 9090 root 130854 0.0 0.0 8900 708 pts/1 S 16:23 0:00 grep --colorauto 9090 root 4010748 0.2 30.7 5826500 2502884 ? Ssl Oct13 8:55 /usr/bin/java -Djava.awt.headlesstrue -jar /usr/share/java/jenkins…

数据在内存中的存储(2)

文章目录 3. 浮点型在内存中的存储3.1 一个例子3.2 浮点数存储规则 3. 浮点型在内存中的存储 常见的浮点数&#xff1a; 3.14159 1E10 ------ 1.0 * 10^10 浮点数家族包括&#xff1a; float、double、long double 类型 浮点数表示的范围&#xff1a;float.h中定义 3.1 一个例…

C++基本语法【恩培学习笔记(一)】

文章目录 1、C程序结构1.1 C程序的基本组成部分1.2 预处理指令1.3 注释1.4 main() 主函数1.5 命名空间 namespace 2、 C的变量和常量2.1 变量2.2 变量的声明2.3 变量的类型 3、C 数组和容器3.1 数组&#xff08;array&#xff09;3.2 容器&#xff08;vector&#xff09; 4、C …

创建scala项目并增加新的object试运行

一、创建scala项目 依赖配置&#xff1a; scala&#xff0c;jdk&#xff0c;maven 没有maven也可以创建 1.1 直接创建 1.1.1 创建 选择新project 路径、依赖配置、代码调试 1.1.2 项目结构 Scala项目中几个文件&#xff1a; .idea&#xff1a;这个文件夹是用来存储项目的…

Android酒店客房预订系统 后台管理+前端app 包含视频教程

【项目功能介绍】 功能列表: 本系统包含后台管理和前端app双端系统, 本系统包含三个角色: 管理员,员工,app用户。 后台管理员的功能包含: 登录, 退出, ,酒店管理,添加酒店,修改酒店,禁用启用酒店; 酒店客房管理,添加客房,修改客房,启用禁用客房; 订单管理,确定订单,拒绝订单,用…

Android视音频知识

Android视音频知识 视音频完整解码播放流程分析。 视音频完整录制编码流程分析。 为什么要编码&#xff0c;如何编码(编码原理) ?。 为什么要编码&#xff1f; 因为视频文件实在太大了&#xff0c;一部电影 200多个GB&#xff0c;编码&#xff1a;1G 视频是连续的图像序列&a…

【Linux】chmod 命令使用

chmod&#xff08;英文全拼&#xff1a;change mode&#xff09;命令是控制用户对文件的权限的命令。 chmod命令 -Linux手册页 著者 作者&#xff1a;David MacKenzie和Jim Meyering。 语法 chmod [选项] [模式] 文件或目录 Linux/Unix 的文件调用权限分为三级 : 文件所有者…