当前位置: 首页 > article >正文

如何用HTML5 Canvas实现电子签名功能✍️

在这里插入图片描述

🤖 作者简介:水煮白菜王,一位资深前端劝退师 👻
👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。
感谢支持💕💕💕

🖌️电子签名作为数字化转型的重要环节,在前端可通过HTML5 Canvas轻松实现。本文将以HTML De示例🎨,手把手带您完成一个电子签名功能。

目录

  • 一、实现原理与核心技术
  • 二、核心代码实现解析
    • 2.1 画布初始化
    • 2.2 画笔样式配置
    • 2.3 设备兼容处理
    • 2.4 绘制逻辑实现
      • 起笔监听:
      • 移动绘制:
      • 收笔处理:
    • 2.5 功能按钮实现
      • 清空画布:
      • 保存签名:
      • 可加撤销功能:
  • 三、注意事项
  • 四、完整代码示例

一、实现原理与核心技术

通过HTML5 Canvas的2D绘图上下文实现轨迹捕捉,结合事件监听处理完成核心绘制功能。关键技术点包括:

  1. Canvas绘图API:Path路径操作
  2. 事件系统:鼠标/触摸事件统一处理
  3. 文件导出:Canvas转Blob对象

二、核心代码实现解析

2.1 画布初始化

设置600x300画布并获取2D上下文,建议根据屏幕尺寸动态调整画布大小。

const canvas = document.querySelector('canvas');
canvas.width = 500;
canvas.height = 300;
const ctx = canvas.getContext('2d');

2.2 画笔样式配置

通过lineCap和lineJoin实现自然的手写效果。

ctx.lineWidth = 3;
ctx.strokeStyle = 'red';
ctx.lineCap = 'round'; // 圆角线头
ctx.lineJoin = 'round'; // 圆角连接

2.3 设备兼容处理

通过UA检测自动切换触摸/鼠标事件,实际项目中建议使用pointer events实现更优雅的兼容。

const mobileStatus = /Mobile|Android|iPhone/i.test(navigator.userAgent);

2.4 绘制逻辑实现

起笔监听:

function start(event) {const pos = mobileStatus ? event.changedTouches[0] : event;ctx.beginPath();ctx.moveTo(pos.pageX, pos.pageY);window.addEventListener(mobileStatus ? 'touchmove' : 'mousemove', draw);
}

移动绘制:

function draw(event) {const pos = mobileStatus ? event.changedTouches[0] : event;ctx.lineTo(pos.pageX, pos.pageY);ctx.stroke(); // 实时渲染路径
}

收笔处理:

function closeDraw() {window.removeEventListener('mousemove', draw);
}

2.5 功能按钮实现

清空画布:

function cancel() {ctx.clearRect(0, 0, canvas.width, canvas.height);
}

保存签名:

function save() {canvas.toBlob(blob => {const a = document.createElement('a');a.download = `${Date.now()}.png`;a.href = URL.createObjectURL(blob);a.click();});
}

可加撤销功能:

let history = [];
// 绘制时保存状态
history.push(ctx.getImageData(0,0,canvas.width,canvas.height));function undo() {if(history.length > 1) {history.pop();ctx.putImageData(history[history.length-1], 0,0);}
}

三、注意事项

  1. 性能优化:大数据量绘制建议使用requestAnimationFrame
  2. 跨域问题:若涉及图片合成需设置crossOrigin=“anonymous”
  3. 移动端适配:添加CSS样式防止触摸滚动
canvas {touch-action: none;background: #f8f8f8;
}

四、完整代码示例

<!doctype html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><style>* {margin: 0 auto;padding: 0;}button { padding: 3px 5px;margin: 5px; }</style></head><body><canvas id="sign"></canvas><div><button onclick="cancel()">取消</button><button onclick="save()">保存</button></div></body><script>const canvas = document.querySelector('canvas');canvas.width = 600;canvas.height = 300;canvas.style.borderRadius = '5px';canvas.style.border = '1px solid #a6a9ad';const ctx = canvas.getContext('2d');// 高清屏适配const scale = window.devicePixelRatio;canvas.width = 600 * scale;canvas.height = 300 * scale;ctx.scale(scale, scale);ctx.lineWidth = 3; //线宽ctx.strokeStyle = 'black'; //线颜色ctx.lineCap = 'round'; //线条的结束端点样式ctx.lineJoin = 'round'; //两条线相交时,所创建的拐角类型// 检测移动设备const mobileStatus = /Mobile|Android|iPhone/i.test(navigator.userAgent);const start = (event) => {const { offsetX, offsetY, pageX, pageY } = mobileStatus? event.changedTouches[0]: event;ctx.beginPath(); //起始一条路径,或重置当前路径ctx.moveTo(pageX, pageY); //把路径移动到画布中的指定点,不创建线条window.addEventListener(mobileStatus ? 'touchmove' : 'mousemove', draw);};const draw = (event) => {const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event;ctx.lineTo(pageX, pageY); //添加一个新点,然后在画布中创建从该点到最后指定点的线条ctx.stroke(); //绘制已定义的路径};const cloaseDraw = () => {window.removeEventListener('mousemove', draw);};window.addEventListener(mobileStatus ? 'touchstart' : 'mousedown', start);window.addEventListener(mobileStatus ? 'touchend' : 'mouseup', cloaseDraw);const cancel = () => {ctx.clearRect(0, 0, 600, 300); //在给定的矩形内清除指定的像素};const save = () => {canvas.toBlob((blob) => {const date = Date.now().toString();const a = document.createElement('a');a.download = `${date}.png`;a.href = URL.createObjectURL(blob);a.click();a.remove();});};</script>
</html>

实际项目中需结合后端实现签名验证、添加时间戳等扩展功能。可继续丰富代码逻辑:

  1. 添加Base64导出功能
  2. 实现笔锋效果(通过速度计算线宽)
  3. 添加本地存储自动保存

Canvas的绘图能力还能延伸应用于手写笔记、电子批注等场景。

在这里插入图片描述
如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀
在这里插入图片描述

相关文章:

如何用HTML5 Canvas实现电子签名功能✍️

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&a…...

区块链中的数字签名:安全性与可信度的核心

数字签名是区块链技术的信任基石&#xff0c;它像区块链世界的身份证和防伪标签&#xff0c;确保每一笔交易的真实性、完整性和不可抵赖性。本文会用通俗的语言&#xff0c;带你彻底搞懂区块链中的数字签名&#xff01; 文章目录 1. 数字签名是什么&#xff1f;从现实世界到区块…...

RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)

RabbitMQ高级特性 RabbitMQ 高级特性解析&#xff1a;RabbitMQ 消息可靠性保障 &#xff08;上&#xff09;-CSDN博客 RabbitMQ 高级特性&#xff1a;从 TTL 到消息分发的全面解析 &#xff08;下&#xff09;-CSDN博客 引言 RabbitMQ 作为一款强大的消息队列中间件&#xff…...

表格columns拼接两个后端返回的字段(以umi框架为例)

在用组件对前端项目进行开发时&#xff0c;我们会遇到以下情况&#xff1a;项目原型中有取值范围这个表字段&#xff0c;需要存放最小取值到最大取值。 而后端返回给我们的数据是返回了一个最小值和一个最大值&#xff0c; 在columns中我们需要对这两个字段进行拼接&#xff0…...

sparkTTS window 安装

SparkTTS 的简介 Spark-TTS是一种基于SpardAudio团队提出的 BiCodec 构建的新系统&#xff0c;BiCodec 是一种单流语音编解码器&#xff0c;可将语音策略性地分解为两种互补的标记类型&#xff1a;用于语言内容的低比特率语义标记和用于说话者特定属性的固定长度全局标记。这种…...

【K8S系列】深入探究Kubernetes中查看日志的方法

在Kubernetes&#xff08;简称K8s&#xff09;的世界里&#xff0c;日志是诊断和排查问题的关键线索。无论是应用程序的运行状态、错误信息&#xff0c;还是系统的健康状况&#xff0c;都能从日志中找到蛛丝马迹。本文将详细介绍在K8s中查看日志的各种方法&#xff0c;从基础的…...

How to install nodejs with nvm on Linux mint 22.1

nvm是nodejs官方用于管理nodejs多版本环境的一个工具 &#xff0c;今天&#xff0c;我带领大家基于nvm完成nodejs在Linux mint 22.1上的安装。 考虑到Linux mint 22.1是基于ubuntu 24.04.1 LTS的&#xff0c;所以&#xff0c;这里的安装也完全适用于nodejs在nodejs上的安装。 …...

JmeterHttp请求头管理出现Unsupported Media Type问题解决

JmeterHttp请求头管理出现Unsupported Media Type问题解决 大多数的app与pc端压测的时候都会出现这种情况 当我们在jemter测试当中当中遇见Unsupported Media Type&#xff0c;有一种可能就是我们请求的网页的content-Type的类型与我们测试的时候的类型不一致 解决方法 可以添…...

十大数据科学Python库

十大数据科学Python库 1、NumPy&#xff1a;脊髓2、Pandas&#xff1a;数据操纵专家3、Matplotlib&#xff1a;艺术之魂4、Scikit-Learn&#xff1a;瑞士军刀5、TensorFlow&#xff1a;聪明的家伙6、PyTorch&#xff1a;叛逆者7、Selenium&#xff1a;操纵大师8、NLTK&#xff…...

LabVIEW伺服阀高频振动测试

在伺服阀高频振动测试中&#xff0c;闭环控制系统的实时性与稳定性至关重要。针对用户提出的1kHz控制频率需求及Windows平台兼容性问题&#xff0c;本文重点分析NI PCIe-7842R实时扩展卡的功能与局限性&#xff0c;并提供其他替代方案的综合对比&#xff0c;以帮助用户选择适合…...

解决asp.net mvc发布到iis下安全问题

解决asp.net mvc发布到iis下安全问题 环境信息1.The web/application server is leaking version information via the "Server" HTTP response2.确保您的Web服务器、应用程序服务器、负载均衡器等已配置为强制执行Strict-Transport-Security。3.在HTML提交表单中找不…...

CSS-基础选择器,字体属性,文本属性介绍

一、CSS 简介 CSS 是层叠样式表 ( Cascading Style Sheets ) 的简称. 有时我们也会称之为 CSS 样式表或级联样式表&#xff61; CSS 是也是一种标记语言 CSS 主要用于设置 HTML 页面中的文本内容(字体&#xff64;大小&#xff64;对齐方式等)&#xff64;图片的外形(宽高&a…...

git submodule管理的仓库怎么删除子仓库

删除 Git 子模块需要执行一系列步骤&#xff0c;以确保从项目中彻底移除子模块及其相关配置。以下是详细的步骤&#xff1a; 1. 取消初始化子模块 运行以下命令以取消子模块的初始化&#xff0c;这会从 .git/config 文件中移除子模块的配置&#xff1a; git submodule deini…...

vtkDepthSortPolyData 根据相机视图方向对多边形数据进行排序

1. 作用 在 3D 渲染中&#xff0c;透明对象的渲染顺序非常重要。如果透明对象的渲染顺序不正确&#xff0c;可能会导致错误的视觉效果&#xff08;例如&#xff0c;远处的透明对象遮挡了近处的透明对象&#xff09;。vtkDepthSortPolyData 通过对多边形数据进行深度排序&#…...

菜鸟开发之MySQL常见字段值处理

仰天大笑出门去&#xff0c;我辈更要谱天曲。 SQL一日同风起&#xff0c;基础不夯注定倾。 众里寻它千百度&#xff0c;蓦然回首&#xff0c;那个错误还在吐。 Java开发该过程中性能问题是存在的&#xff0c;而数据处理则是占据大头&#xff0c;平时开发一定要注意代码质量和…...

ubuntu-学习笔记-nginx+php

nginxphp nginx下载nginx配置nginx.conf php其他 记录一下在ubuntu中nginxphp部署tp项目 nginx nginx就是正常下载 下载nginx sudo apt-get install nginx tp项目版本是3.2&#xff0c;通过设置路由&#xff0c;以域名/api.php/控制器/xxx的格式进行api的调用&#xff0c;文…...

【MySQL_04】数据库基本操作(用户管理--配置文件--远程连接--数据库信息查看、创建、删除)

文章目录 一、MySQL 用户管理1.1 用户管理1.11 mysql.user表详解1.12 添加用户1.13 修改用户权限1.14 删除用户1.15 密码问题 二、MySQL 配置文件2.1 配置文件位置2.2 配置文件结构2.3 常用配置参数 三、MySQL远程连接四、数据库的查看、创建、删除4.1 查看数据库4.2 创建、删除…...

牛客网刷题(5)(HTML之元素<input>、表格<table>与描述列表<dl>、元素<label>)

目录 一、哪种输入类型定义滑块控件&#xff1f;元素&#xff08;input&#xff09; &#xff08;1&#xff09;官方解析。 &#xff08;2&#xff09;总结。 &#xff08;3&#xff09;牛客大佬总结。 &#xff08;4&#xff09;HTML5——元素&#xff08;input&#xff09;的…...

语音视频App协议安全实战:防御伪造/窃听/Deepfake攻击

一、SDP协议安全加固 1. SDP字段校验&#xff08;防止参数篡改&#xff09; 安全SDP生成示例&#xff08;Node.js&#xff09;&#xff1a; const crypto require(crypto); function signSDP(sdp) { const hmac crypto.createHmac(sha256, SECRET_KEY); hmac.update(sd…...

Git系列之git checkout

git checkout 是 Git 中最常用的命令之一&#xff0c;主要用于切换分支、恢复文件或检出特定提交。以下是关于 git checkout 的所有指令、详细解释及实际应用场景的全面说明。 1. 切换分支 1.1 切换到现有分支 git checkout <branch-name>• 作用&#xff1a;切换到指定…...

IDEA(十一)调整新版本的工具栏显示Git操作(pull、commit、push、revert等)

目录 一、背景二、操作步骤2.1 开启新 UI 样式2.2 设置 Tool Window 工具栏 一、背景 好久没有更新 IDEA 了&#xff0c;更新之后发现 IDEA 的工具栏消失了。一番操作之后&#xff0c;终于把 IDEA 的工具栏的设置调整好了&#xff0c;在此进行记录调整步骤&#xff0c;供大家学…...

C++编程:进阶阶段—4.2对象

目录 4.2 对象特征 4.2.1 构造函数和析构函数 4.2.2 构造函数的分类 4.2.3 拷贝函数调用时机 4.2.4 构造函数调用规则 4.2.5 深拷贝与浅拷贝 4.2.6 初始化列表 4.2.7 类对象作为类成员 4.2.8 静态成员 4.2.9 成员变量和成员函数的存储 4.2.10 this指针 4.2.11 空指针…...

决策树的核心思想

一、决策树的核心思想 本质&#xff1a;通过特征判断对数据集递归划分&#xff0c;形成树形结构。目标&#xff1a;生成一组“若-则”规则&#xff0c;使数据划分到叶子节点时尽可能纯净。关键流程&#xff1a; 特征选择&#xff1a;选择最佳分裂特征&#xff08;如信息增益最…...

TensorFlow.js 全面解析:在浏览器中构建机器学习应用

TensorFlow.js 全面解析&#xff1a;在浏览器中构建机器学习应用 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;可以分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/ccc 文章目录 TensorFlow.js 全面解析&#x…...

CI/CD—Jenkins配置Poll SCM触发自动构建

Poll SCM简介 在 Jenkins 等持续集成工具中&#xff0c;“Poll SCM” 是一种用于轮询软件配置管理&#xff08;SCM&#xff09;系统以检查代码变更的机制&#xff0c;以下是对它的详细介绍&#xff1a; 作用 “Poll SCM” 允许 Jenkins 定期检查指定的 SCM 系统&#xff08;如 …...

《云原生技术:DeepSeek分布式推理的效能倍增器》

在当今人工智能飞速发展的时代&#xff0c;大语言模型的推理能力成为了衡量其性能的关键指标。DeepSeek作为人工智能领域的重要参与者&#xff0c;致力于提升模型的推理效率和准确性。而云原生技术的出现&#xff0c;为DeepSeek实现更高效的分布式推理提供了强大的支持。 云原…...

AI与SEO关键词智能解析

内容概要 人工智能技术正重塑搜索引擎优化的底层逻辑&#xff0c;其核心突破体现在关键词解析维度的结构性升级。通过机器学习算法对海量搜索数据的动态学习&#xff0c;AI不仅能够识别传统TF-IDF模型中的高频词汇&#xff0c;更能捕捉语义网络中隐含的关联特征。下表展示了传…...

OpenHarmony子系统开发 -- 构建系统编码规范与最佳实践

OpenHarmony子系统开发 -- 构建系统编码规范与最佳实践 概述 gn是generate ninja的缩写&#xff0c;它是一个元编译系统&#xff08;meta-build system&#xff09;,是ninja的前端&#xff0c;gn和ninja结合起来&#xff0c;完成OpenHarmony操作系统的编译任务。 gn简介 目…...

1-002:MySQL InnoDB引擎中的聚簇索引和非聚簇索引有什么区别?

在 MySQL InnoDB 存储引擎 中&#xff0c;索引主要分为 聚簇索引&#xff08;Clustered Index&#xff09; 和 非聚簇索引&#xff08;Secondary Index&#xff09;。它们的主要区别如下&#xff1a; 1. 聚簇索引&#xff08;Clustered Index&#xff09; 定义 聚簇索引是表数…...

STM32之BKP

VBAT备用电源。接的时候和主电源共地&#xff0c;正极接在一起&#xff0c;中间连接一个100nf的电容。BKP是RAM存储器。 四组VDD都要接到3.3V的电源上&#xff0c;要使用备用电池&#xff0c;就把电池正极接到VBAT&#xff0c;负极跟主电源共地。 TEMPER引脚先加一个默认的上拉…...