html5如何嵌入电子签名?手写签名板实现教程详解

来源:Android教程作者:松松建站头衔:草根站长
导读:本期聚焦于松松建站创作的《html5如何嵌入电子签名?手写签名板实现教程详解》,敬请观看详情。合同签署、审批确认等场景越来越依赖线上化,网页端的手写电子签名成了高频需求。本文围绕html5嵌入电子签名这一主题,详细讲解如何利用Canvas与鼠标及触摸事件搭建一个可用的手写签名板,内容涵盖Canvas绘图原理、事件监听与坐标换算、笔画平滑处理、签名图片导出与Base64保存、常见兼容性问题排查,并附带完整可运行的示例代码,帮助你快速在自己的网页中集成电子签名功能。

在网页里直接手写签名,早已不是什么新鲜事。无论是OA系统的审批确认,还是电子合同平台在线签署,都离不开一个能在浏览器中流畅书写的签名板。要实现这个功能,html5提供的Canvas是最合适的载体:它既能在PC端响应鼠标操作,也能在手机、平板上响应触摸事件,配合JavaScript就能把用户的手写轨迹实时绘制出来。这篇文章从零开始,带你完整实现一个可嵌入任意页面的电子签名板。

html5如何嵌入电子签名?手写签名板实现教程详解

一、Canvas绘图原理与页面结构搭建

Canvas是html5新增的画布元素,它提供了一个矩形绘图区域,所有绘制操作都通过getContext('2d')返回的上下文对象完成。手写签名的本质就是不断记录鼠标或手指经过的坐标点,再用lineTo把相邻的点连接成线段。当点足够密集时,这些线段在视觉上就形成了一条平滑的笔画。

先来看最基础的页面结构。一个容器包裹画布,画布下方放置清除和保存两个按钮,这是签名板最经典的布局。需要注意的是,不要用CSS去拉伸Canvas的尺寸,而是应该通过JS动态设置它的宽高属性,否则绘制出来的线条会出现模糊或变形。

<div class="signature-box">
  <canvas id="signCanvas"</canvas>
</div>
<button id="clearBtn">清除重写</button>
<button id="saveBtn">保存签名</button>

画布尺寸自适应的写法如下,利用offsetWidth和offsetHeight获取容器的实际渲染尺寸,再赋值给Canvas的width和height属性,这样能保证画布逻辑尺寸与显示尺寸一致,避免锯齿。

function resizeCanvas() {
  const canvas = document.getElementById('signCanvas');
  const box = canvas.parentElement;
  // 逻辑尺寸与显示尺寸保持一致,避免模糊
  canvas.width = box.clientWidth;
  canvas.height = box.clientHeight;
}
window.addEventListener('load', resizeCanvas);
window.addEventListener('resize', resizeCanvas);

二、事件监听与坐标换算:让PC和手机都能写

签名板必须同时支持鼠标和触摸两种输入。PC端使用mousedown、mousemove、mouseup三个事件,移动端则要监听touchstart、touchmove、touchend。触摸事件返回的是touches数组,取第一个触点即可。此外,移动端书写时页面会跟着滚动,必须在事件里调用preventDefault()阻止默认行为,否则写到一半页面就滑走了。

另一个关键点是坐标换算。事件对象中的clientX和clientY是相对于浏览器视口的坐标,而Canvas绘图使用的是画布内部坐标系,两者必须通过getBoundingClientRect()换算,否则签名位置会和手指位置对不上。

function getPos(canvas, e) {
  const rect = canvas.getBoundingClientRect();
  const point = e.touches ? e.touches[0] : e;
  return {
    x: point.clientX - rect.left,
    y: point.clientY - rect.top
  };
}

const canvas = document.getElementById('signCanvas');
const ctx = canvas.getContext('2d');
let drawing = false;

canvas.addEventListener('mousedown', startDraw);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', endDraw);
canvas.addEventListener('mouseleave', endDraw);
canvas.addEventListener('touchstart', startDraw);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', endDraw);

function startDraw(e) {
  e.preventDefault();
  drawing = true;
  const p = getPos(canvas, e);
  ctx.beginPath();
  ctx.moveTo(p.x, p.y);
}

function draw(e) {
  if (!drawing) return;
  e.preventDefault();
  const p = getPos(canvas, e);
  ctx.lineWidth = 3;          // 笔画粗细
  ctx.lineCap = 'round';      // 线段端点为圆形
  ctx.lineJoin = 'round';     // 转角圆润
  ctx.strokeStyle = '#000';
  ctx.lineTo(p.x, p.y);
  ctx.stroke();
}

function endDraw() {
  drawing = false;
}

这里有个容易被忽略的细节:lineCap和lineJoin设置成round能让笔画连接处圆润自然,如果保持默认的butt和miter,快速书写时会出现明显的折角和断点,观感会很差。

三、笔画平滑处理与签名导出

上面的实现已经可以写字,但快速书写时笔画容易呈现折线感。改善的方法是引入贝塞尔曲线:不直接用lineTo连接点,而是以上一点和当前点的中点作为曲线端点,用quadraticCurveTo绘制二次贝塞尔曲线,笔迹会顺滑许多。

let lastX = 0, lastY = 0;

function smoothDraw(e) {
  if (!drawing) return;
  e.preventDefault();
  const p = getPos(canvas, e);
  // 中点法平滑:以两点中点为端点画二次贝塞尔
  const midX = (lastX + p.x) / 2;
  const midY = (lastY + p.y) / 2;
  ctx.beginPath();
  ctx.moveTo(lastX, lastY);
  ctx.quadraticCurveTo(lastX, lastY, midX, midY);
  ctx.stroke();
  lastX = p.x;
  lastY = p.y;
}

签名写完后通常需要保存。Canvas提供了toDataURL方法,可以把画布内容导出为png或jpg格式的Base64字符串。png支持透明背景,适合叠加到合同文件上;jpg体积更小但背景不透明。导出时建议先创建一个白底画布把签名复制过去,避免透明背景在某些查看器中显示异常。

document.getElementById('saveBtn').onclick = function () {
  // 检查画布是否为空白
  if (canvas.toDataURL() === emptyCanvasDataURL()) {
    alert('请先书写签名');
    return;
  }
  const exportCanvas = document.createElement('canvas');
  exportCanvas.width = canvas.width;
  exportCanvas.height = canvas.height;
  const ectx = exportCanvas.getContext('2d');
  ectx.fillStyle = '#fff';
  ectx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
  ectx.drawImage(canvas, 0, 0);
  const base64 = exportCanvas.toDataURL('image/png');
  // 将base64提交给后端存储,或下载到本地
  console.log(base64);
};

document.getElementById('clearBtn').onclick = function () {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
};

判断画布是否为空的小技巧是:初始化完成后先调用一次toDataURL记录空白画布的字符串,保存时与当前值比较,相同则说明用户还没写。Base64字符串可以直接通过表单或AJAX提交到后端,存入数据库后再配合业务流程生成签章图片。

四、常见问题与成熟方案选择

自己实现签名板虽然代码量不大,但真要在生产环境使用,还有不少坑要填:一是高分辨率屏幕上的模糊问题,需要按devicePixelRatio放大画布再缩小绘制;二是横竖屏切换时画布内容会被清空,需要在resize前先把内容暂存到图片再恢复;三是iOS Safari对触摸事件的特殊行为,有时需要在touchmove里显式设置passive: false才能生效。

如果项目周期紧张,也可以直接使用成熟的开源库,比如signature_pad,它内置了笔画压感模拟、平滑算法和高分屏适配,API也很简洁:

const canvas = document.querySelector('canvas');
const pad = new SignaturePad(canvas, {
  backgroundColor: 'rgb(255,255,255)',
  penColor: 'rgb(0,0,0)'
});
// 导出png
const data = pad.toDataURL('image/png');
// 清空
pad.clear();

总体来说,理解了Canvas事件监听、坐标换算和曲线平滑这几个核心点,无论是自己造轮子还是使用第三方库,都能做到心中有数。自己实现的好处是代码可控、无额外依赖,第三方库的优势在于兼容性处理更完善,按项目实际情况选择即可。最后提醒一句,网页端的签名图片只解决采集问题,如果要具备法律效力,还需要后端配合数字证书、时间戳等技术手段,这才是完整的电子签名方案。

html5电子签名Canvas手写板签名板插件修改时间:2026-09-16 14:44:49

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0916/58006.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。