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

🤖 作者简介:水煮白菜王,一位资深前端劝退师 👻

👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。

感谢支持💕💕💕

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

目录

一、实现原理与核心技术

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

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

二、核心代码实现解析

2.1 画布初始化

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

javascript 复制代码
const canvas = document.querySelector('canvas');
canvas.width = 500;
canvas.height = 300;
const ctx = canvas.getContext('2d');

2.2 画笔样式配置

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

javascript 复制代码
ctx.lineWidth = 3;
ctx.strokeStyle = 'red';
ctx.lineCap = 'round'; // 圆角线头
ctx.lineJoin = 'round'; // 圆角连接

2.3 设备兼容处理

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

javascript 复制代码
const mobileStatus = /Mobile|Android|iPhone/i.test(navigator.userAgent);

2.4 绘制逻辑实现

起笔监听:

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

移动绘制:

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

收笔处理:

javascript 复制代码
function closeDraw() {
  window.removeEventListener('mousemove', draw);
}

2.5 功能按钮实现

清空画布:

javascript 复制代码
function cancel() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

保存签名:

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

可加撤销功能:

javascript 复制代码
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样式防止触摸滚动
html 复制代码
canvas {
  touch-action: none;
  background: #f8f8f8;
}

四、完整代码Demo示例

html 复制代码
<!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. 支持移动端与PC端双平台
  2. 输出为PNG透明图片
  3. 代码精简
  4. 无第三方依赖

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

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

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

如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀

相关推荐
却尘1 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare2 分钟前
浅浅看一下设计模式
前端
Lee川6 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix32 分钟前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人35 分钟前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl39 分钟前
OpenClaw 深度技术解析
前端
崔庆才丨静觅43 分钟前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空1 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust