3 分钟实现一个 Canvas 签名板,支持 PC 和移动端

在很多场景中我们需要用户在线签署,比如:在线合同、快递确认、表单签名等。 今天我们来实现一个简单的 Canvas 签名板 ,它支持 PC 鼠标绘制 和 移动端触摸绘制。

需求分析

  1. 实现签名功能。
  2. 既支持鼠标操作,也支持触摸操作。
  3. 支持修改线条宽度和颜色
  4. 支持导出 PNG、SVG、JSON、点坐标等格式

核心思路

  1. 使用 Canvas API 来绘制签名:

    • ctx.beginPath() 开始绘制路径。
    • ctx.moveTo(x, y) 移动画笔到起始点。
    • ctx.lineTo(x, y) 绘制到目标点。
    • ctx.stroke() 执行绘制指令。
  2. 监听鼠标和触摸事件,将用户的手势轨迹映射到 Canvas 上。

  3. 关键点:如何将鼠标/手指在屏幕上的位置,转换为 相对于 Canvas 的坐标。

    • 使用 element.getBoundingClientRect() 获取 Canvas 的偏移量。

整体思维导图

实现步骤

1. 封装获取坐标值的工具类(兼容鼠标和触摸事件)

js 复制代码
function getCoordinates(e) {
    let x, y;
    if (e.type.includes('touch')) {
        const touch = e.touches[0] || e.changedTouches[0];
        const rect = canvas.getBoundingClientRect();
        x = touch.clientX - rect.left;
        y = touch.clientY - rect.top;
    } else {
        const rect = canvas.getBoundingClientRect();
        x = e.clientX - rect.left;
        y = e.clientY - rect.top;
    }
​
    return [x, y];
}

2. 创建 Canvas

html 复制代码
<canvas id="signatureCanvas"></canvas>

3. 监听鼠标事件

js 复制代码
// 鼠标按下
canvas.addEventListener('mousedown', startDrawing); // 开始绘制
// 鼠标移动
canvas.addEventListener('mousemove', draw); // 绘制线条
// 鼠标弹起
canvas.addEventListener('mouseup', stopDrawing); // 结束绘制
// 鼠标从元素上移出,防止用户将鼠标移出画布时意外中断绘制状态
canvas.addEventListener('mouseout', stopDrawing); // 结束绘制

4. 监听触摸事件(移动端)

js 复制代码
canvas.addEventListener('touchstart', startDrawing); // 开始绘制
canvas.addEventListener('touchmove', draw); // 绘制线条
canvas.addEventListener('touchend', stopDrawing); // 结束绘制

5. 开始绘制 startDrawing 具体实现

js 复制代码
function startDrawing(e) {
    isDrawing = true;
    [lastX, lastY] = getCoordinates(e);
​
    // 开始新路径
    currentPath = {
        points: [], // 点坐标列表
        color: strokeStyle, // 线条颜色
        width: lineWidth // 线条宽度
    };
}

6. 绘制中 draw 具体实现

js 复制代码
function draw(e) {
    if (!isDrawing) return;
​
    const [x, y] = getCoordinates(e);
​
    ctx.beginPath();
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(x, y);
    ctx.strokeStyle = strokeStyle;
    ctx.lineWidth = lineWidth; // 设置的线条宽度
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.stroke();
​
    // 记录点
    if (currentPath) {
        currentPath.points.push({x, y});
    }
​
    [lastX, lastY] = [x, y];
}

7.结束绘制 stopDrawing 具体实现

js 复制代码
function stopDrawing() {
    if (!isDrawing) return;
​
    isDrawing = false;
​
    // 保存当前路径,便于后续撤销、重绘、导出等操作
    if (currentPath && currentPath.points.length > 0) {
        drawingData.push(currentPath);
    }
    currentPath = null;
}

8. 设置线条宽度

js 复制代码
function setWidth() {
    lineWidth = lineWidth < 10 ? lineWidth + 1 : 1;
    widthValue.textContent = lineWidth;
}

导出图片等功能

js 复制代码
function exportPNG() {
  const dataURL = canvas.toDataURL('image/png');
  console.log("PNG Base64:", dataURL);
  return dataURL;
}

总结

源码

通过简单的 Canvas API + 鼠标/触摸事件 ,我们就能实现一个跨平台的签名功能。 这类功能的关键在于 事件处理 和 坐标转换,同时也为我们理解前端与用户交互提供了一个很好案例。

当然,实际项目中我们还可以做更多扩展,比如:

  • 增加 清空画布、撤销操作;
  • 支持 保存为图片并上传服务器;
  • 提供 不同颜色和笔刷粗细选择。

这样,一个小小的 Canvas 签名板,就能变成实用的电子签署工具。

相关推荐
deli0075 分钟前
DLA 枝晶生长实测:5000 个粒子集体停摆之后,分形维数收敛到 1.70
前端
IT_陈寒13 分钟前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端
可乐鸡翅yeah_20 分钟前
video.js 集成 hls.js 开发 M3U8 播放器,新手高频踩坑
开发语言·前端·javascript·后端·ecmascript·m3u8·音视频在线播放
程序猿追1 小时前
HarmonyOS 6 上做个极简浏览器:Web 组件 + 前进后退 + 加载进度
前端·华为·harmonyos
沐沐师1 小时前
Mybatis 框架教程
前端
三天不学习1 小时前
Tailwind CSS 快速入门(2026 版):从 v4 零配置上手,到「该不该用、怎么用好」的选型实战
前端·css·tailwind
明月_清风1 小时前
面对陌生的 GitHub 项目无从下手?这 4 个网站帮你快速读懂源码
前端·后端·github
htzyl2062 小时前
前端阶梯——第九章、颜色、文本与背景
前端·css
zhangzeyuaaa2 小时前
Ruby `require` 完全指南:从 `$LOAD_PATH` 到 `require_relative`
服务器·前端·ruby
htzyl2062 小时前
前端阶梯——第八章、CSS入门与选择器
前端·css