更新记录
1.0.0(2026-06-17)
下载此版本
v1.0.0 (2024-06-17)
新功能
- 首次发布 jz-writepad 多端手写签名画板组件
- 支持三端通用:H5、微信小程序、支付宝小程序、App 等多平台
- 实现手指触摸绘制(主动绘制)功能
- 实现外部数据驱动绘制(被动绘制)功能
- 实现实时数据同步功能(draw-frame 事件)
- 实现透明背景 PNG 图片导出
- 支持临时文件链接和 base64 两种导出格式
- H5 平台支持
uni.createWritepad() API 方式
- 使用贝塞尔曲线算法实现平滑绘制
- 支持撤销、清空画布功能
- 支持自定义笔触颜色、宽度、样式
技术架构
- 使用纯 JS Class 实现
- 基础类 + 实体类继承架构
- 平台差异通过渲染器子类实现
- 事件系统基于 BaseEventEmitter
已知问题
- App-Nvue 平台兼容性待测试
- 压感功能仅部分设备支持
平台兼容性
uni-app(4.73)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| × |
√ |
√ |
√ |
√ |
× |
√ |
√ |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
jz-writepad 多端手写签名画板
支持三端通用(H5、小程序、App)的手写签名/涂鸦画板组件,使用纯 JS Class 实现。
核心特性
- ✅ 全屏画布 - 支持自定义尺寸或全屏模式
- ✅ 双模式绘制 - 支持手指触摸绘制(主动绘制)和外部数据驱动绘制(被动绘制)
- ✅ 实时数据同步 - 触摸绘制时实时抛出绘制数据,支持远程同步场景
- ✅ 透明背景导出 - 导出 PNG 透明背景图片,支持临时链接或 base64 格式
- ✅ H5 特殊挂载 - H5 平台支持通过 API 直接挂载到 DOM,无需引入组件
- ✅ 多端兼容 - H5、微信小程序、支付宝小程序、App 等多平台支持
- ✅ 纯 JS Class 实现 - 基础类 + 实体类继承架构,代码结构清晰
安装使用
方式一:组件引入(小程序/App)
<template>
<view class="page">
<jz-writepad
ref="writepad"
:fullscreen="false"
width="750rpx"
height="500rpx"
strokeColor="#333333"
:strokeWidth="2"
@draw-frame="handleDrawFrame"
@draw-end="handleDrawEnd"
@ready="handleReady"
/>
<view class="actions">
<button @tap="handleUndo">撤销</button>
<button @tap="handleClear">清空</button>
<button @tap="handleExport">导出签名</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const writepad = ref(null);
// 接收实时绘制数据(用于远程同步)
function handleDrawFrame(frame) {
// 将 frame 发送到远程服务器或 WebSocket
console.log('Draw frame:', frame);
// ws.send(JSON.stringify(frame));
}
// 画布就绪
function handleReady(canvasId) {
console.log('Canvas ready:', canvasId);
}
// 导出签名
async function handleExport() {
const result = await writepad.value.exportImage({
format: 'base64'
});
console.log('Export result:', result);
}
// 撤销
function handleUndo() {
writepad.value.undo();
}
// 清空
function handleClear() {
writepad.value.clear();
}
</script>
方式二:H5 API 方式(无需组件引入)
// H5 平台直接通过 API 创建全屏画板
const writepad = uni.createWritepad({
strokeColor: '#000000',
strokeWidth: 3,
zIndex: 9999,
sessionId: 'user_123'
});
// 监听绘制事件(核心同步功能)
writepad.on('draw-frame', (frame) => {
// 发送到远程同步
socket.send(JSON.stringify(frame));
});
// 监听其他事件
writepad.on('draw-start', (point) => console.log('开始绘制', point));
writepad.on('draw-move', (point) => console.log('绘制移动', point));
writepad.on('draw-end', (path) => console.log('结束绘制', path));
// 导出签名
writepad.exportImage({ format: 'base64' }).then(result => {
console.log('签名图片:', result.data);
});
// 销毁画板
writepad.destroy();
组件属性 Props
| 属性 |
类型 |
默认值 |
说明 |
width |
String/Number |
'100%' |
画布宽度 |
height |
String/Number |
'100%' |
画布高度 |
fullscreen |
Boolean |
true |
是否全屏模式 |
strokeColor |
String |
'#000000' |
笔触颜色 |
strokeWidth |
Number |
3 |
笔触宽度(像素) |
strokeStyle |
String |
'solid' |
笔触样式:'solid' | 'dashed' |
disabled |
Boolean |
false |
是否禁用触摸绘制 |
enablePressure |
Boolean |
false |
是否启用压感 |
backgroundColor |
String |
'transparent' |
背景颜色 |
backgroundImage |
String |
'' |
背景图片 URL |
sessionId |
String |
'default' |
会话 ID(用于区分绘制来源) |
drawData |
Array |
[] |
外部绘制数据队列(被动绘制) |
exportFormat |
String |
'tempFile' |
导出格式:'tempFile' | 'base64' |
exportQuality |
Number |
1 |
导出图片质量 0-1 |
showToolbar |
Boolean |
false |
是否显示内置工具栏 |
组件事件 Events
| 事件 |
参数 |
说明 |
draw-frame |
frame: DrawFrame |
核心事件,每个绘制动作实时抛出,用于远程同步 |
draw-start |
point: DrawPoint |
开始绘制 |
draw-move |
point: DrawPoint |
绘制移动 |
draw-end |
path: DrawPath |
结束绘制(返回完整路径) |
clear |
- |
清空画布 |
undo |
pathId: String |
撤销路径 |
export-success |
result: ExportResult |
导出成功 |
export-error |
error: Error |
导出失败 |
ready |
canvasId: String |
画布初始化完成 |
组件方法 Methods
通过 ref 调用组件方法:
const writepad = ref(null);
// 导出图片
await writepad.value.exportImage({ format: 'base64' });
// 导出签名(自动裁剪空白区域)
await writepad.value.exportSignature({ padding: 10 });
// 清空画布
writepad.value.clear();
// 撤销最后一笔
writepad.value.undo();
// 撤销指定路径
writepad.value.undoPath(pathId);
// 获取所有路径
const paths = writepad.value.getPaths();
// 获取路径数据(可序列化)
const data = writepad.value.getPathsData();
// 设置笔触配置
writepad.value.setStroke({ color: '#ff0000', width: 5 });
// 执行外部绘制数据
writepad.value.executeDrawData(frames);
绘制数据结构
DrawPoint 绘制点
{
id: 'point_xxx', // 点 ID
x: 100, // X 坐标
y: 200, // Y 坐标
timestamp: 1700000000, // 时间戳(毫秒)
action: 'start', // 动作类型:'start' | 'move' | 'end'
pressure: 0.5 // 压感值(可选,0-1)
}
DrawPath 绘制路径
{
id: 'path_xxx', // 路径 ID
points: [...], // 路径点集合
strokeColor: '#000000',// 笔触颜色
strokeWidth: 3, // 笔触宽度
strokeStyle: 'solid', // 笔触样式
startTime: 1700000000, // 开始时间
endTime: 1700000010, // 结束时间
completed: true // 是否完成
}
DrawFrame 绘制帧(同步数据)
{
type: 'path_point', // 帧类型:'path_start' | 'path_point' | 'path_end' | 'clear' | 'undo'
data: {...}, // 帧数据(点或路径)
sessionId: 'user_123', // 会话 ID
sequence: 1 // 序列号(用于排序)
}
远程同步绘制示例
<template>
<jz-writepad
ref="writepad"
:drawData="remoteDrawData"
:sessionId="sessionId"
@draw-frame="sendToRemote"
/>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const writepad = ref(null);
const remoteDrawData = ref([]);
const sessionId = ref('user_123');
let ws = null;
onMounted(() => {
// 建立 WebSocket 连接
ws = new WebSocket('ws://your-server/sync');
ws.onmessage = (event) => {
const frame = JSON.parse(event.data);
if (frame.sessionId !== sessionId.value) {
// 来自其他用户,添加到绘制队列
remoteDrawData.value.push(frame);
}
};
});
onUnmounted(() => {
if (ws) ws.close();
});
// 发送本地绘制数据到远程
function sendToRemote(frame) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(frame));
}
}
</script>
类架构说明
本插件使用纯 JS Class 实现,采用基础类 + 实体类继承架构:
基础类层
BaseEntity - 最基础实体类,提供 ID 生成、属性管理
BaseEventEmitter - 事件发射器基类,提供 on/off/emit 方法
数据实体类层(继承 BaseEntity)
DrawPoint - 绘制点类
DrawPath - 绘制路径类(聚合 DrawPoint)
DrawFrame - 绘制帧类(用于同步)
渲染器类层(继承 BaseCanvasRenderer)
BaseCanvasRenderer - Canvas 渲染器基类(定义抽象接口)
H5CanvasRenderer - H5 渲染器(实现 H5 平台 Canvas 2D API)
UniCanvasRenderer - 小程序/App 渲染器(实现 uni Canvas API)
管理器类层(继承 BaseEventEmitter)
DrawEngine - 绘制引擎(聚合渲染器,处理绘制逻辑)
DataParser - 数据解析器(解析外部绘制数据)
ExportManager - 导出管理器(处理图片导出)
WritepadManager - H5 全屏管理器(H5 API 方式使用)
WritepadInstance - H5 实例类(单个画板实例)
支持平台
| 平台 |
支持状态 |
说明 |
| H5 |
✅ 完全支持 |
支持 API 方式和组件方式 |
| 微信小程序 |
✅ 完全支持 |
使用组件方式 |
| 支付宝小程序 |
✅ 完全支持 |
使用组件方式 |
| 百度小程序 |
✅ 完全支持 |
使用组件方式 |
| 字节跳动小程序 |
✅ 完全支持 |
使用组件方式 |
| App-Vue |
✅ 完全支持 |
使用组件方式 |
| App-Nvue |
⚠️ 不确定 |
Nvue 使用 gcanvas,需要特殊处理 |
更新日志
v1.0.0
- 首次发布
- 支持三端通用(H5、小程序、App)
- 实现实时数据同步功能
- 支持透明背景导出
- H5 平台支持 API 方式挂载
License
MIT