xDocxDoc
AI
前端
后端
iOS
Android
Flutter
AI
前端
后端
iOS
Android
Flutter
  • 电子签名组件 Signature Pad 介绍

电子签名组件 Signature Pad 介绍

🔍 一、Signature Pad 核心原理

Signature Pad 是一个基于 HTML5 Canvas 的轻量级 JavaScript 库,专为创建平滑的电子签名体验而设计。其核心技术灵感源于 Square 公司提出的贝塞尔曲线插值算法,通过动态调整笔迹宽度模拟真实书写压力变化:

// 核心笔迹生成逻辑(简化版)
function _strokeUpdate(event) {
  // 计算点与点之间的速度
  const velocity = this._calculateCurveSpeed(event);
  
  // 动态调整笔迹宽度(压力敏感效果)
  const newWidth = this._calculateStrokeWidth(velocity);
  
  // 使用二次贝塞尔曲线连接点
  this._ctx.quadraticCurveTo(
    lastPoint.x,
    lastPoint.y,
    (lastPoint.x + currentPoint.x) / 2,
    (lastPoint.y + currentPoint.y) / 2
  );
}

📊 贝塞尔曲线插值原理

参数作用默认值
minWidth最小线宽(模拟轻压)0.5px
maxWidth最大线宽(模拟重压)2.5px
velocityFilterWeight速度平滑系数0.7

🛠 二、安装与基础使用

安装方式

# NPM 安装
npm install signature_pad

# Yarn 安装
yarn add signature_pad

# CDN 引入
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.7/dist/signature_pad.umd.min.js"></script>

初始化签名板

const canvas = document.getElementById('signature-canvas');
const signaturePad = new SignaturePad(canvas, {
  penColor: "rgb(0, 0, 255)",  // 蓝色笔迹
  backgroundColor: "rgba(255, 255, 255, 0.5)",  // 半透明白色背景
  minWidth: 1.0,
  maxWidth: 4.0,
  throttle: 8  // 降低采样频率提升性能
});

⚙️ 三、核心 API 深度解析

1. 数据导出方法

// 获取 PNG 格式的 Base64 数据
const pngData = signaturePad.toDataURL(); 

// 获取 SVG 原始字符串(保留矢量特性)
const svgString = signaturePad.toSVG({
  includeBackgroundColor: true // 包含背景色
});

// 获取原始点数据(适合实时同步)
const pointGroups = signaturePad.toData();

2. 数据导入方法

// 从 PNG 导入签名(会重置画布)
signaturePad.fromDataURL('data:image/png;base64,iVBORw0K...');

// 从原始点数据恢复签名(支持增量绘制)
signaturePad.fromData(pointGroups, { clear: false });

3. 画布控制

// 清空画布并重置状态
signaturePad.clear();

// 检测签名状态
if(signaturePad.isEmpty()) {
  alert('请先签署您的名字!');
}

📱 四、高DPI屏幕适配方案

function resizeCanvas() {
  const ratio = Math.max(window.devicePixelRatio || 1, 1);
  const canvas = signaturePad.canvas;
  
  // 调整画布物理分辨率
  canvas.width = canvas.offsetWidth * ratio;
  canvas.height = canvas.offsetHeight * ratio;
  
  // 缩放绘图上下文
  canvas.getContext('2d').scale(ratio, ratio);
  
  // 保持签名显示
  signaturePad.fromData(signaturePad.toData());
}

// 响应窗口变化
window.addEventListener('resize', resizeCanvas);

🧩 五、实战应用技巧

1. 服务器端数据处理(Node.js示例)

// 解析Base64图片数据
app.post('/save-signature', (req, res) => {
  const dataURL = req.body.signature;
  const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
  
  fs.writeFile('signature.png', base64Data, 'base64', (err) => {
    if(err) return res.status(500).send('保存失败');
    res.send('签名保存成功');
  });
});

2. 签名图片优化处理

// 使用 trim-canvas 库去除空白边缘
import trimCanvas from 'trim-canvas';

const trimmedCanvas = trimCanvas(signaturePad.canvas);
const optimizedData = trimmedCanvas.toDataURL('image/png');

3. 签名事件监听

// 笔迹开始事件
signaturePad.addEventListener('beginStroke', () => {
  console.log('签名开始');
});

// 笔迹更新事件
signaturePad.addEventListener('afterUpdateStroke', () => {
  realtimePreview(signaturePad.toData());
});

🔧 六、高级配置选项详解

参数类型说明
dotSizefloat/function点的半径(支持动态函数:(pressure) => pressure * 2 + 1)
minDistanceinteger采样最小间距(降低锯齿)默认 5px
throttleinteger节流间隔(毫秒),设为 0 禁用节流
canvasContextOptionsobject透传 CanvasRenderingContext2D 配置(如 { willReadFrequently: true })

🚀 性能优化建议

  1. 节流采样优化

    // 移动端建议值
    signaturePad.throttle = 10; // 毫秒
    signaturePad.minDistance = 3; // 像素
  2. 内存管理

    // 离开页面时释放资源
    window.addEventListener('beforeunload', () => {
      signaturePad.off();
      signaturePad.canvas = null;
    });
  3. 分层渲染

    <!-- 背景层与签名层分离 -->
    <div class="canvas-container">
      <canvas id="bg-layer"></canvas>
      <canvas id="signature-layer"></canvas>
    </div>

💎 总结

Signature Pad 通过三项关键技术解决了电子签名的核心痛点:

  1. 动态笔迹算法
    基于速度敏感的贝塞尔曲线插值,完美还原真实书写笔锋
  2. 跨平台兼容
    纯 Canvas 实现,无外部依赖,支持 iOS/Android/PC 全平台
  3. 数据灵活性
    支持从原始点数据到多种图片格式的完整转换链

🧩 建议

  • 高DPI设备:必须结合 devicePixelRatio 做画布缩放
  • 数据存储:优先保存矢量格式(SVG)或原始点数据
  • 性能敏感场景:调整 throttle 和 minDistance 平衡流畅度与精度

🔮 未来演进方向

随着 WebGL 的普及,后续版本可探索:

最新技术动态:社区正在实验 WebAssembly 版本,预计提升复杂签名渲染性能 300% 🚀

💡 提示

生产环境中建议结合 Web Workers 处理签名数据转换,避免主线程阻塞导致交互卡顿

最后更新: 2025/9/8 21:47