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 签名板,就能变成实用的电子签署工具。

相关推荐
kingwebo'sZone1 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_090120 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农32 分钟前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式