news 2026/9/12 17:59:10

Vant Signature 签名组件实战指南:基于 Canvas 的手写签名、撤销与导出

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vant Signature 签名组件实战指南:基于 Canvas 的手写签名、撤销与导出

Vant Signature 签名组件实战指南:基于 Canvas 的手写签名、撤销与导出

【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant

导读

Signature是 Vant 移动端组件库中用于电子签名场景的组件,底层基于 HTML5 Canvas 实现,支持手写笔迹、实时导出 base64 图片、多步撤销以及主题定制。本指南以 packages/vant/src/signature/README.md 为主体,结合组件源码、样式文件与测试用例,系统讲解组件的安装引入、基础用法、全部 Props/Events/Methods/Slots 参数、类型定义与 CSS 变量主题定制,并深入到 Canvas 绘制、撤销历史栈等底层实现原理,帮助你完整掌握在 Vue 3 移动端项目中落地签名能力的方案。

适用前提:本组件要求vant版本 >= 4.3.0,且基于 Vue 3 + Vite 等现代构建环境。

组件概述与适用场景

Signature是专为签名场景设计的组件,核心特点:

  • 基于 Canvas 实现:所有笔迹绘制都发生在<canvas>上,不依赖第三方绘图库,组件体积小;
  • 开箱即用的操作栏:自带「清空 / 撤销 / 确认」三个按钮(内部复用 Vant 的Button组件);
  • 数据出口统一:点击确认后,通过submit事件一次性输出{ image, canvas },方便后续上传、存储或与后端签署流程对接;
  • 历史记录管理:内置撤销历史栈,支持最多history-size步的撤销操作。

从源码结构看(Signature.tsx),组件由签名画布区(__content)和操作按钮区(__footer)两部分渲染组成,非常适合合同签署、确认书、收货签收、登记表确认等移动端表单场景。

安装与引入

通过 npm 安装

npm i vant

注册组件

支持全局注册与按需引入两种方式。全局注册需要先通过createApp创建应用实例:

import { createApp } from 'vue'; import { Signature } from 'vant'; const app = createApp(); app.use(Signature);

在模板中直接以<van-signature>使用:

<van-signature />

从源码看,组件通过withInstall包装导出(index.ts),同时该文件为 Vue 的GlobalComponents声明了Signature全局组件类型,因此在 TypeScript + Volar 环境下使用<van-signature>时可获得完整的类型提示。更多注册方式(如按需引入、unplugin-vue-components自动导入)可参考 组件注册文档。

基础用法

核心事件流:submit 与 clear

组件的事件模型非常简单清晰。当用户点击确认按钮时,组件触发submit事件,回调的第一个参数data包含两个字段:

字段类型说明
imagestring签名对应的图片,base64 字符串格式;若签名为空,则返回空字符串
canvasHTMLCanvasElement当前绘制用的 Canvas 元素

当点击清空按钮时,组件触发clear事件(无参数)。

基础用法示例:

<van-signature @submit="onSubmit" @clear="onClear" /> <van-image v-if="image" :src="image" />
import { ref } from 'vue'; import { showToast } from 'vant'; export default { setup() { const image = ref(''); const onSubmit = (data) => { image.value = data.image; }; const onClear = () => showToast('clear'); return { image, onSubmit, onClear, }; }, };

拿到data.image后,既可以直接赋值给 Vant 的Image组件预览,也可以提交给后端保存。上述完整写法与仓库内 demo/index.vue 中的基础用法示例一致。

空签名处理

值得注意的一个细节:当画布为空时点击确认,submit事件中的image字段是空字符串而非data:image/png;base64,...形式的空图。这一点在实际业务中非常实用——你不需要额外校验签名是否为空。

从源码看(Signature.tsx),组件通过isCanvasEmpty方法创建一张与当前画布同尺寸的空白对照 Canvas(若设置了background-color,则用同色填充后对比),再通过canvas.toDataURL()与当前画布数据比对,完全一致即判定为空。这也意味着纯色背景填充不会影响空签名判定。

自定义笔触与背景

笔触颜色(pen-color)

通过pen-color属性设置笔迹颜色,默认黑色:

<van-signature pen-color="#ff0000" @submit="onSubmit" @clear="onClear" />

线条宽度(line-width)

通过line-width属性设置线条宽度(数值类型),默认3

<van-signature :line-width="6" @submit="onSubmit" @clear="onClear" />

背景颜色(background-color)

通过background-color属性设置签名区背景色,默认透明(无背景色):

<van-signature background-color="#eee" @submit="onSubmit" @clear="onClear" />

源码实现上,背景色通过fillRect在整个画布上填充(Signature.tsx),并在初始化、清空、撤销回退时都会被重新填充,保证任意操作后背景色保持一致。

导出图片类型(type)

type属性控制导出图片的 MIME 类型,默认png。源码中对jpgjpeg做了特殊处理,使用canvas.toDataURL('image/jpeg', 0.8)以 80% 质量压缩导出,其余类型(如webp)则走通用分支canvas.toDataURL('image/${type}')(Signature.tsx)。需要更小体积的图片时(如用于上传),可以指定type="jpg"

Props 完整参数表

以下为组件全部 Props(对应 Signature.tsx 中signatureProps的定义):

参数说明类型默认值
type导出图片类型stringpng
pen-color笔触颜色,默认黑色string#000
line-width线条宽度number3
history-size撤销历史记录最大数量number20
background-color背景颜色string-
tips当不支持 Canvas 时出现的提示文案string-
clear-button-text清空按钮文案string清空(英文Clear
undo-button-text撤销按钮文案string撤销(英文Undo
confirm-button-text确认按钮文案string确认(英文Confirm

补充说明:

  • pen-colorline-width等属性在每次笔画开始时(touchStart)被重新写入 Canvas 2D 上下文(strokeStylelineWidth),所以运行中动态修改也会在下一笔生效;
  • 三个按钮文案属性均为String类型,默认值为空时组件会回退到内置多语言文案(源码中为props.clearButtonText || t('clear')形式),即跟随组件库语言包;
  • history-size控制撤销历史栈的最大容量,超出时最早的记录会被丢弃(详见下文「撤销实现原理」);
  • tips用于 Canvas 不可用的兜底提示。源码通过hasCanvasSupport检测document.createElement('canvas').getContext('2d')是否存在(Signature.tsx);检测失败时渲染区会替换为提示文本(tips属性或tips插槽),而不是报错。

Events 事件

事件名说明回调参数
start开始签名时触发(手指/触摸落下)-
end结束签名时触发(手指抬起)-
signing签名过程中触发event: TouchEvent
submit点击确认按钮时触发data: { image: string; canvas: HTMLCanvasElement }
clear点击清空按钮时触发-

源码将事件绑定在 Canvas 的touchstart/touchmove/touchend上(Signature.tsx),并透传了原生TouchEvent,因此signing事件可用于实时获取触点坐标等场景。绘制过程中调用了preventDefault阻止默认行为(如页面滚动跟随),保证书写体验。

Slots 插槽

名称说明插槽参数
tips自定义提示文案(Canvas 不可用时的兜底展示)-

当浏览器不支持 Canvas 时,若提供了tips插槽则渲染插槽内容,否则渲染tips属性文本。测试用例should render tips correctly(test/index.spec.ts)通过 mockdocument.createElement验证了该兜底分支的渲染结果。

实例方法(Methods)

通过ref可以获取 Signature 实例并调用实例方法(组件通过useExpose将方法挂载到组件实例代理上,use-expose.ts):

方法名说明参数返回值
resizev4.7.3外层元素大小或组件显示状态变化时调用,触发重绘--
clearv4.8.6清除签名--
submitv4.8.6触发submit事件,与点击确认按钮效果等价--
undo撤销上一次笔画--

典型用法:在表单提交前主动调用submit获取签名数据,无需用户手动点击确认按钮:

<van-signature ref="signatureRef" @submit="onSubmit" />
import { ref } from 'vue'; const signatureRef = ref(); // 需要提交时 signatureRef.value?.submit();

关于resize:源码通过watch(windowWidth, resize)监听窗口宽度变化自动触发重绘(Signature.tsx),同时在初始化时根据devicePixelRatio对画布做高清屏适配(canvas.width = offsetWidth * dprctx.scale(dpr, dpr),见 Signature.tsx)。当容器尺寸因布局变化而改变(如从隐藏切换为显示、旋转屏幕)时,可手动调用resize()保留原有笔迹并重绘。测试用例should call resize when window width changes(test/index.spec.ts)验证了窗口 resize 时会触发getImageData重绘逻辑。

类型定义

组件导出以下类型定义:

import type { SignatureProps, SignatureInstance } from 'vant';
  • SignatureProps:Props 对应的类型(由ExtractPropTypes<typeof signatureProps>推导,Signature.tsx);
  • SignatureInstance:组件实例类型,包含resizeclearsubmitundo四个公开方法(types.ts)。

SignatureInstance用法示例:

import { ref } from 'vue'; import type { SignatureInstance } from 'vant'; const signatureRef = ref<SignatureInstance>(); signatureRef.value?.resize();

此外还导出了SignatureThemeVars类型(signaturePaddingsignatureContentHeightsignatureContentBackgroundsignatureContentBorder),用于类型安全的主题变量定制(types.ts)。

撤销与历史栈的底层原理

undohistory-size的实现是 Signature 组件最具技术含量的部分,值得深入理解:

  1. 入栈时机:每次touchend(一笔结束)时调用saveState,将当前画布内容通过getImageData(0, 0, canvasWidth, canvasHeight)快照存入history数组(Signature.tsx);
  2. 容量上限history长度达到history-size时,最早的历史快照通过shift()丢弃,即只保留最近 N 步;
  3. 撤销动作undo弹出栈顶快照后,先clearRect清空画布并重新填充背景色,再将栈顶(上一笔结束时的状态)通过putImageData恢复(Signature.tsx);当历史耗尽时,画布被完全清空;
  4. 清空重置clear在清空画布的同时也会将整个history数组重置为空。

测试用例完整验证了这一机制:

  • undo should restore canvas to previous state(test/index.spec.ts)绘制两笔后撤销一次,断言putImageData被调用恢复第一笔状态;再撤销一次则仅执行clearRect(历史耗尽);
  • history should be limited by historySize prop(test/index.spec.ts)在historySize: 3下绘制 5 笔,撤销 3 次后第 4 次撤销不再触发putImageData,精确验证了容量上限与丢弃策略。

这些测试同时也是理解组件行为契约的最佳参考:支持的撤销步数 =min(已绘制笔画数, history-size)

主题定制(CSS 变量)

组件提供以下 CSS 变量用于自定义样式,推荐通过 ConfigProvider 组件 进行全局或局部主题定制:

名称默认值描述
--van-signature-paddingvar(--van-padding-xs)组件内边距
--van-signature-content-height200px画布高度
--van-signature-content-backgroundvar(--van-background-2)画布背景色
--van-signature-content-border1px dotted #dadada画布边框样式

这些变量的定义与使用位置见 index.less:

  • 变量在:root, :host作用域声明,便于在自定义元素/全局环境生效;
  • 签名区域.van-signature__content使用flex居中布局,画布canvas的宽高被设置为100%以填满内容区,因此修改--van-signature-content-height即可直观改变书写区域高度;
  • 内容区同时带border-radius: var(--van-radius-lg)圆角与overflow: hidden,保证背景填充不溢出圆角;
  • 底部操作栏.van-signature__footer右对齐排布,按钮间通过margin-left分隔。

例如,想将签名区域高度调整为 300px 并改为实线边框:

// 通过 ConfigProvider 局部覆盖 import { ref } from 'vue'; const themeVars = ref({ signatureContentHeight: '300px', signatureContentBorder: '1px solid #666', });
<van-config-provider :theme-vars="themeVars"> <van-signature /> </van-config-provider>

完整实战示例:签署确认表单

结合以上内容,给出一个贴近真实业务(如收货签收)的完整示例:用户签名后立即预览,同时自动提交给后端:

<template> <van-config-provider :theme-vars="themeVars"> <van-signature ref="signatureRef" pen-color="#333" :line-width="4" background-color="#fff" :history-size="10" confirm-button-text="提交签名" @submit="onSubmit" @clear="onClear" /> </van-config-provider> <van-image v-if="preview" :src="preview" width="100%" /> </template> <script setup> import { ref } from 'vue'; import { showToast } from 'vant'; import type { SignatureInstance } from 'vant'; const signatureRef = ref<SignatureInstance>(); const preview = ref(''); const themeVars = { signatureContentHeight: '240px', }; const onSubmit = ({ image, canvas }) => { if (!image) { showToast('请先签名'); return; } preview.value = image; // 此处可将 base64 图片上传到服务端 console.log('canvas:', canvas); }; const onClear = () => { preview.value = ''; showToast('已清空'); }; </script>

在该示例中:空签名提交会被!image拦截;submit事件同时携带了 base64 图片与 Canvas 元素,便于需要原始 Canvas 做进一步处理(如追加水印)的场景;resize()可在容器尺寸变化时调用以保持画布内容。

总结

Vant 的Signature组件用约两百行核心代码(Signature.tsx)完整实现了移动端签名场景的关键能力:Canvas 高清屏适配与绘制、空签名智能判定、多步撤销历史栈、按钮文案本地化与 CSS 变量主题定制。接入时只需关注三件事:通过submit事件接收{ image, canvas }数据、用history-size控制撤销深度、必要时通过resize()响应容器尺寸变化。相关演示与测试分别位于 demo/index.vue 与 test/index.spec.ts,可作为二次开发的直接参考。

【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 17:57:08

基于Dify构建数据治理知识库的实践与优化

1. 项目概述&#xff1a;基于Dify构建数据治理知识库的核心价值数据治理作为企业数字化转型的基础工程&#xff0c;其知识体系往往分散在各类文档、标准、流程记录中。传统方式下&#xff0c;员工需要翻阅大量文件才能找到所需信息&#xff0c;而基于Dify构建的RAG&#xff08;…

作者头像 李华
网站建设 2026/9/12 17:55:55

LeetCode hot100——73.矩阵置零

题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&#xff1a; 输入…

作者头像 李华
网站建设 2026/9/12 17:53:54

1248. 统计「优美子数组」(前缀和)

链接&#xff1a;1248. 统计「优美子数组」 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a;1248. 统计「优美子数组」 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int numberOfSubarrays(vector<int>& nums, int k) {if(nums.siz…

作者头像 李华
网站建设 2026/9/12 17:52:40

STM32直流有刷电机三闭环控制:位置环速度环电流环的级联PID实现

简介&#xff1a;面向嵌入式开发者与电机控制工程师的STM32F4直流有刷电机三闭环控制源码包&#xff0c;聚焦位置环、速度环、电流环的级联设计与位置式PID实现&#xff0c;采用C语言和HAL库编写&#xff0c;可直接在STM32F4平台运行或移植&#xff0c;适合正在学习电机控制或需…

作者头像 李华
网站建设 2026/9/12 17:52:24

ai测试的项目部署

一、将项目部署在本地1.部署项目这里选择之前完成过的博客系统作为ai测试的应用对象下载docker后直接进入root账户&#xff0c;然后本地部署即可使用docker pull crpi-dnvvmwqk3y7594pa.cn-hangzhou.personal.cr.aliyuncs.com/blog_bite/blog_bite:V1.2可以本地部署项目使用doc…

作者头像 李华