1. 项目概述与方案选型1.1 为什么需要独立的签名组件做 Vue 后台或者移动端 H5 项目经常会碰到一个看起来不起眼、真做起来又特别容易翻车的需求电子签名。不管是合同审批、订单确认、体检报告签字还是物业维修确认单都会用到手写签名。第一次接到这个需求的时候我脑子里的第一反应是“直接画个 canvas 不就行了”结果真动手才发现事情没那么简单。签名这个动作看着简单实际拆解下来要处理的事情不少。首先你得兼容鼠标和手指两类输入设备桌面端用鼠标画移动端用手指画事件绑定的逻辑完全是两套。其次签名笔迹要平滑不能画出来是锯齿感很重的折线这涉及 canvas 的 lineCap、lineJoin 设置以及画点时的距离插值处理。再往后说用户签完名要能生成图片图片格式、裁剪范围、背景是否透明、导出清晰度这些都是问题。最后还有一批细节比如签名内容没填就提交表单时要能拦截比如反悔了要能一键清空重签比如在移动端画布上的签名区域要防止页面跟着滚动。这些逻辑自己写一遍少说也要一两天时间而且写出来的代码未必稳定。实际项目里我做过技术对比自己实现一个签名画布组件核心逻辑大概需要 300 到 500 行代码还要兼顾不同浏览器的 PointerEvent 兼容问题。而用成熟的签名库比如 vue-esign只需要几十行代码就能把事情办完功能覆盖还很完整。vue-esign 是 Vue 生态里一个基于 canvas 实现的电子签名组件支持 PC 端鼠标操作和移动端手指/触控笔操作可以自定义笔迹颜色、画布背景、线条粗细、裁剪范围签名完成后能生成 base64 编码的 PNG 图片直接塞进表单数据里提交给后端。它同时支持 Vue 2 和 Vue 3安装方式也很简单。这东西最香的地方在于它把签名这个最核心的交互闭环做到了开箱即用不用自己再去造轮子。1.2 与其他实现方式的横向对比我在决定采用 vue-esign 之前其实做了好几个方案的对比分析这里把对比结果整理一下方便后面有人遇到同样需求时少走弯路。第一种方案是自己写 canvas。这个方案的优点是灵活度高想怎么改就怎么改代码完全可控不用依赖第三方。缺点是开发周期长要处理上面说的几百行代码而且签名笔迹的平滑处理、不同设备的坐标换算这些细节特别容易踩坑。尤其是触控笔压感这块普通的 mousedown、mousemove 事件根本不支持得用 PointerEvent 的 pressure 属性才能获取压感数据自己实现的成本就更高了。第二种方案是用一些大而全的 UI 组件库自带的签名功能。比如某些企业级组件库里面有签名面板组件功能确实很全但问题在于这些组件库本身的体积非常大如果项目里只是需要一个签名功能为了这一个功能把整套组件库引进来性价比很低。另外这类组件库的定制化程度有限想要调整一些细节得深入去改组件源码或者写覆盖样式挺折腾的。第三种方案就是 vue-esign。它的优势在于轻量核心文件很小不引入额外依赖API 设计得很直观使用上几乎没有学习成本。虽然它不支持触控笔压感也没有橡皮擦这种细粒度编辑能力但对于绝大多数的业务签字场景来说够用了。我在实际项目中用下来整套接入下来不超过半天时间稳定性也经受住了多个线上项目的考验。这么一对比结论很清晰如果你只是需要在 Vue 项目里快速落地一个实用的电子签名功能vue-esign 是目前性价比相当高的选择。如果你的业务真的需要那种专业的压感签名体验那就不是组件库层面能解决的问题得去考虑专门的签署平台了。2. 环境准备与安装配置2.1 安装与版本兼容性说明vue-esign 的安装方式非常常规用 npm 或者 yarn 都行。我一般习惯用 npm命令是npm install vue-esign --save如果你的项目用的是 yarn那对应的命令就是yarn add vue-esign安装完以后需要特别注意的是 Vue 2 和 Vue 3 在引入方式上有一点点区别。Vue 2 项目里直接在 main.js 里全局注册就可以了import Vue from vue import vueEsign from vue-esign Vue.use(vueEsign)Vue 3 项目里的全局注册方式稍有不同import { createApp } from vue import App from ./App.vue import vueEsign from vue-esign const app createApp(App) app.use(vueEsign) app.mount(#app)如果不喜欢全局注册也可以按需引入。这种方式的优点是依赖关系更清晰组件只在用到它的页面里才打包对首屏加载速度有一点点帮助。以 Vue 3 为例在单文件组件里的写法是script setup import vueEsign from vue-esign /script template vue-esign refesign / /template有一点要留意vue-esign 的 2.x 版本是针对 Vue 3 发布的1.x 版本才是支持 Vue 2 的。如果项目还在用 Vue 2安装的时候最好指定一下版本号避免意外安装到不兼容的版本。我自己之前就在一个 Vue 2 老项目里踩过这个坑当时没看版本直接装了最新版结果项目直接跑不起来了。注意vue-esign 组件本体是基于 canvas 的不需要额外引入任何第三方 canvas 库安装完即可直接使用。2.2 组件的基本使用方式vue-esign 的使用方式非常直接在模板里写一个组件标签就完事了。最简单的例子长这样template div vue-esign refesignRef :width800 :height300 :is-cropfalse :line-width4 line-color#000000 bg-color#ffffff / button clickhandleGenerate生成签名图片/button /div /template script setup // 生成签名图片的方法 const handleGenerate () { // 获取组件实例 const esign document.querySelector(.vue-esign) // 这里需要通过 ref 来调用组件方法 } /script看到这里可能有人要问了上面代码里的ref是怎么绑定到组件实例上的这里我重新写一个更标准的示例。在 Vue 3 的script setup语法下正确写法是这样的template div vue-esign refesignRef :width800 :height300 :is-cropfalse :line-width4 line-color#000000 bg-color#ffffff / button clickhandleGenerate生成签名图片/button /div /template script setup import { ref } from vue import vueEsign from vue-esign const esignRef ref(null) const handleGenerate () { const esignInstance esignRef.value if (!esignInstance) return const result esignInstance.generate() console.log(签名图片base64, result) } /script在 Vue 2 的选项式 API 里用this.$refs.esignRef就能拿到组件实例。生成签名图片的方法是generate()它会返回一个 base64 编码的 PNG 图片数据。还有一个reset()方法调用后会把画布内容清空让用户重新签字。这两个方法是最常用的基本覆盖了签名场景里“写”和“改”两个核心动作。设计思路上vue-esign 把画布尺寸、笔迹样式、裁剪开关这些全部做成了 props把清空、生成图片做成了组件方法这种“配置项 方法调用”的模型非常符合直觉也方便在业务代码里灵活控制。2.3 核心属性的作用与参数说明vue-esign 的 props 设计得非常精简总共就那么几个但每一个都很关键。我把它们整理成一个表格方便查阅。属性名类型默认值说明widthNumber800画布宽度单位是 pxheightNumber300画布高度单位是 pxisCropBooleanfalse是否开启裁剪开启后生成的图片会去掉画布空白边缘lineWidthNumber4笔迹线条的粗细单位是 pxlineColorString#000000笔迹颜色支持十六进制色值bgColorString画布背景色默认是透明背景bgImageString画布背景图片的 base64 数据设置后会覆盖 bgColor这几个参数里面我实际使用下来最需要花心思调的是两个一个是height一个是isCrop。height这个参数直接决定了签名区域的物理高度。如果设得太矮用户签个名发现笔迹都写到边界外面去了体验很糟糕。但如果设得太高又会把页面撑得很难看。我一般建议在移动端场景下把高度设置在 200 到 300px 之间PC 端可以适当放宽到 300 到 400px。另外要记得把宽度设置成和容器等宽并且考虑好不同屏幕尺寸下的适配问题。isCrop这个参数更加实用它的作用是自动裁剪掉签名笔迹周围的空白区域。相当于你在白纸上签完名再拿剪刀沿着字体的边缘把多余的白边剪掉。这样导出的图片放进合同里的效果非常干净不会有一大块白底占着位置。但是要注意开启裁剪以后generate()方法返回的数据结构会发生变化返回的是一个数组里面包含了裁剪后的图片和裁剪区域的信息。具体细节后面我单独讲。3. 核心功能拆解与实战3.1 手写签名的实现原理与交互细节vue-esign 的核心是一个 canvas 画布它把鼠标事件和触摸事件统一封装成了画线逻辑。当用户按下鼠标或者手指触摸画布时组件开始记录轨迹移动时不断在新的点与上一个点之间绘制线段松开时结束一次绘制。这个交互过程中有一个细节特别影响签名效果线段之间的衔接是否平滑。如果只是简单地把相邻两个点用直线连起来当鼠标移动很快的时候画出来的笔迹就会呈现出明显的折角看起来非常生硬。vue-esign 在内部做了处理它会根据点的距离和方向对轨迹做必要的平滑处理让签名看起来更接近真实的手写效果。还有一个容易被忽视的地方是设备的像素比。现在很多手机屏幕的物理像素密度都超过了 CSS 像素的 1 倍比如 iPhone 的屏幕像素比是 2 或者 3。如果直接按照 CSS 像素的尺寸来设置 canvas 的宽高生成的图片在手机上看可能没问题但放到电脑屏幕上查看或者打印出来就会明显感觉模糊。vue-esign 内部处理了这个问题它会把画布的实际分辨率按照设备像素比进行放大保证导出的签名图片足够清晰。我在实际项目中还注意到一个交互细节签名过程中手指在移动端屏幕上滑动时页面可能会跟着滚动。vue-esign 在画布上处理了触摸事件的默认行为阻止了页面滚动但这个效果要求画布本身放置在一个正确配置的容器里。如果移动端项目需要锁定整个页面滚动还是要配合外层容器的overflow: hidden来使用。3.2 图片生成与裁剪逻辑generate()方法是 vue-esign 使用频率最高的方法。在默认情况下也就是isCrop为false时这个方法会直接把整个画布的内容导出成一张 base64 编码的 PNG 图片。const result this.$refs.esignRef.generate() // result 是一个 base64 字符串可以直接赋值给 img 标签的 src 属性 // 也可以放在表单数据里提交给后端当isCrop为true时逻辑就变得稍微复杂一点了。组件会先分析画布上哪些区域有笔迹然后计算出一个刚好包含所有笔迹的最小矩形区域再把这块区域导出成图片。这个功能做出来的效果非常干净尤其适合把签名放在合同、单据的固定位置上不会因为签名尺寸不统一导致排版错乱。开启裁剪后generate()方法的返回值会变成一个对象大概长这样const result this.$refs.esignRef.generate() // result 的结构类似 // { // base64: data:image/png;base64,..., // width: 100, // height: 50, // }这里需要注意的是裁剪模式下返回的数据结构和默认模式不同如果不加判断直接把返回值当作字符串来处理代码就会报错。这是我踩过的坑后面在常见问题部分会细说。清空操作相对简单直接调用reset()方法就会把画布恢复成初始状态。如果设置了背景色清空后背景色会保留用户可以在干净的画布上重新签名。3.3 表单联动与后端对接方案签名功能做出来了最终还是要落到业务上。最常见的场景是用户在表单里签名填写完其他信息然后一起提交给后端。这里我分享一下自己在项目里的完整做法。假设我们在做一个线上的审批确认页面用户在页面上确认某些条款后需要签名。签名的 base64 图片不能直接塞进普通的 JSON 请求里因为数据量比较大而且字符串里可能含有特殊字符。我的做法是先把 base64 图片传给一个文件上传接口后端返回一个图片 URL然后把 URL 作为字段值随表单提交。在实际编码中流程大概是这样async handleSubmit() { // 1. 先生成签名图片 const signResult this.$refs.esignRef.generate() if (!signResult) { alert(请先完成签名) return } // 2. 转换成文件对象 const blob this.dataURLtoBlob(signResult) const file new File([blob], signature.png, { type: image/png }) // 3. 构造 FormData 并上传 const formData new FormData() formData.append(file, file) const uploadRes await uploadFile(formData) // 4. 把返回的 URL 拼到表单数据里一起提交 await submitForm({ ...this.formData, signatureUrl: uploadRes.data.url }) } dataURLtoBlob(dataurl) { const arr dataurl.split(,) const mime arr[0].match(/:(.*?);/)[1] const bstr atob(arr[1]) let n bstr.length const u8arr new Uint8Array(n) while (n--) { u8arr[n] bstr.charCodeAt(n) } return new Blob([u8arr], { type: mime }) }多说一句如果后端接口直接接受 base64 字符串那也可以直接传省掉转文件这一步。不过大部分情况下走文件上传接口会更稳妥一点毕竟 base64 图片塞在 JSON 里不仅臃肿还可能遇到 JSON 解析的边界问题。一个比较重要的校验逻辑是用户没有在画布上写过任何内容时generate()方法的返回值是什么根据我的实测此时它返回的是一个空字符串或者说空结果所以可以通过判断返回值是否为空来决定是否允许提交表单。这个校验放在 function 的开头能很好地避免后端收到空签名图。4. 常见问题与排查技巧实录4.1 画布尺寸与显示尺寸不一致这个问题在移动端尤其是适配不同屏幕尺寸时非常常见。场景描述我在页面上给 vue-esign 设置了:width800但实际在手机上显示的时候画布超出了屏幕宽度只显示了一部分签名区域严重变形。这个问题的根源在于 vue-esign 的 width 和 height 属性直接决定了 canvas 内部的分辨率组件不会主动去适配外层容器的宽度。解决办法是在外层包裹一个容器用 CSS 控制容器的宽度然后让 canvas 按百分比缩放填满容器同时把内部的 width 设置成一个和设备像素比匹配的物理尺寸。一个比较稳妥的适配方案是用 JavaScript 动态计算容器宽度然后把算出来的值赋值给组件的 width 属性onMounted(() { const container document.querySelector(.sign-container) const width container.offsetWidth esignRef.value.width width })如果项目里用了 rem 适配方案直接按 rem 和 px 的转换关系算出合适的像素值也行。总之不能让 canvas 的宽度超出容器宽度这是最容易出问题的地方。4.2 生成的签名图片是空的或者缺笔画这个问题的典型表现是用户明明签了名但生成出来的图片上没有内容或者内容不全。排查思路分两步。第一步确认用户在画布上确实写下了内容。很多情况下用户以为自己签了名其实只是轻触了一下屏幕没有产生有效的移动轨迹。第二步确认调用generate()的时机。如果是在组件还没有完成内部初始化的时候就调用了很可能拿到的是空画布的导出结果。我遇到过的实际情况是同事在页面的onMounted里立刻调用了generate()方法这时候 canvas 还没来得及完成渲染导出的自然是空图片。解决办法是确保在用户完成签名动作之后再调用导出方法比如放在提交按钮的点击事件里或者通过监听组件的某个状态来确认签名是否有效。另外还要检查isCrop模式的返回值处理。如果开启了裁剪直接把这个对象当成字符串用页面大概率会报错或者拿到 undefined。4.3 常见问题速查表为了便于排查我把实际开发中遇到的典型问题整理成了一个速查表。现象可能原因解决方案签名时页面跟着滚动组件外层容器没有锁定页面滚动设置外层容器overflow: hidden或者给 body 添加滚动锁定样式生成的图片模糊canvas 尺寸设置过小和显示尺寸不匹配把 canvas 内部尺寸按设备像素比放大如乘以 2再用 CSS 缩放显示导出结果是空字符串用户没有实际签名或者调用时机太早判断返回结果如果是空给出提示确保用户签名后再调用开启裁剪后返回数据结构变了不理解 isCrop 对返回值的影响根据 isCrop 的值分别处理返回值页面在低端安卓机上卡顿画布尺寸设置得过大合理设置 width 和 height不要盲目追求大尺寸生成的图片带黑色背景bgColor 设置成了空字符串而部分浏览器对透明 canvas 兼容性不同按业务需要显式设置bg-color#ffffff或调整导出背景4.4 移动端签名区域的细节处理移动端使用 vue-esign有两个细节不得不说。第一个是签名区域需要设置足够的高度不能太矮不然用户签两个字就顶到边界了。我的经验是至少 200px 起步如果用户的签名习惯比较奔放甚至可以给到 300px。第二个细节是在 iOS 设备上手指快速滑动时可能出现笔迹中断也就是画出来的线不连续一段一段的。笔迹中断的问题原因在于触摸事件响应频率和 canvas 重绘性能之间存在一定的竞争关系。遇到这种情况可以先尝试调整 canvas 的尺寸把尺寸控制在合理的范围内。如果还是不行可以尝试禁用页面的缩放功能强制浏览器以 100% 的缩放比渲染页面有时候能明显改善触摸事件的采集效果。还有一个小技巧是如果项目里存在横向滑动返回手势和签名的手势操作有冲突可以考虑在签名区域内阻止默认的触摸操作让账号系统的返回手势只在非签名区域生效。这种问题需要在具体的业务场景里调试但知道有这个坑能省下不少排查时间。5. 多场景适配与项目实践5.1 移动端 H5 签署页面移动端 H5 是目前 vue-esign 用得最多的场景比如移动办公 App 里的审批流需要用户在手机上写签名确认。移动端页面有几个特点是必须考虑的屏幕宽度有限、触控精度不如鼠标、页面可以上下滚动。针对这些特点我的做法是把 vue-esign 的 width 设置成一个固定较大的值比如 750对应常见的设计稿宽度然后在外层用 CSS 做等比缩放适配。这样既保证了 canvas 内部的分辨率足够高生成出来的图片清晰又能在不同尺寸的屏幕上完整展示。还有一个细节移动端页面如果使用的 UI 组件库带了键盘弹出功能比如在签名前后有输入框要注意键盘弹出后签名区域被顶出可视区域的问题。常规做法是把页面滚动到签名区域的位置用scrollIntoView之类的 API 就可以实现。5.2 PC 端表单集成PC 端场景相对简单没有移动端那么多幺蛾子。marketing 系统、运营后台、内部审批系统这类项目用户用鼠标签名交互上基本不会出大问题。PC 端的重点在视觉呈现和业务联动上。PC 端页面通常比较宽vue-esign 默认的 800px 宽度在大多数场景下看着很协调。如果希望签名区域更紧凑可以调整 width 数值。另外PC 端用户习惯用鼠标鼠标的移动精度远高于手指所以 lineWidth 默认的 4px 是可以接受的如果觉得签名太细或者太粗可以微调。我在 PC 端项目里的一个经验是不要把签名功能做成一个独立跳转的页面而是在表单页面上以内嵌的方式呈现。用户在填写表单的过程中顺手完成签名这样流程顺畅用户心理负担也小。所以 vue-esign 这种轻量组件的优势就体现出来了——可以直接嵌入表单的任何位置不需要单独做页面路由。5.3 与图片服务及文件上传的配合前面提到了 base64 转文件上传的做法这里再补充一些我在对接过程中的经验。签名图片上传以后后端存的是 URL前端展示和历史回显就很方便了直接把 URL 赋值给img标签即可。不过有一点需要注意如果签名图片的 base64 字符串比较长上传时需要考虑接口超时问题。尤其是移动端网络环境不稳定的情况下一张签名图片的 base64 可能达到几十 KB 甚至上百 KB走 HTTP 上传时要设置合理超时时间。另外如果后端做了图片压缩或者格式转换前端拿到的 URL 可能对应的是压缩后的图片这有可能造成签名展示失真需要和后端约定好处理逻辑。还有一个小建议在上传前把 base64 数据转成 Blob/File 对象时最好通过atob做一次Uint8Array的转换直接以纯字符串上传会导致请求体积变大。这个细节看起来微不足道但在弱网环境下能明显改善上传成功率省得后端同学来找你排查文件格式问题。6. 踩坑记录与避坑指南6.1 必须特别关注的三个坑第一个坑是版本问题。前面提过vue-esign 的版本和 Vue 版本有对应关系Vue 2 项目要用 1.xVue 3 项目要用 2.x。装错版本不会立刻报错但到了运行时会出现组件不注册或者方法不存在的问题排查起来比较费劲。安装后可以迅速查看一下 package.json 里的版本号确认无误再继续写代码。第二个坑是isCrop的返回值陷阱。这个坑我在前面反复提过因为它实在是太隐蔽了。默认情况下isCrop是falsegenerate()返回的是字符串一旦你打开了裁剪功能返回值就变成了对象。如果项目里有多个地方调用了generate()比如一个用来预览、一个用来提交务必都要处理数据结构的问题。第三个坑是 canvas 画布尺寸过大导致的性能问题。有些同学为了追求图片清晰度把 width 设置成 2000、height 设置成 1000结果发现用户在低端安卓机上签名时卡顿严重。其实图片清晰度主要由设备像素比决定画布物理尺寸大并不直接等于清晰度更高。合理的做法是让 canvas 物理尺寸和显示尺寸匹配同时根据设备像素比去调整内部渲染比例。vue-esign 自身已经做了一部分处理过度调大尺寸反而会适得其反。6.2 从项目实践中总结的避坑建议十多个项目的实践教训我总结了几条很实在的建议。第一签名组件不要直接裸放在页面底部。给组件外面套一个有边框的容器视觉上明确告诉用户“这里是签名区域”。见过不少用户对着一个白底空白区域完全不知道从哪里动手的案例加上边框和提示文案后使用率明显提升。第二在页面加载时不要自动调用reset()或者generate()。vue-esign 组件初始化的过程涉及 canvas 的创建和事件绑定过早调用方法可能会拿到不完整的状态。正确的做法是把这些调用放在用户交互之后的业务逻辑里或者放在nextTick里运行。第三如果要支持用户重新签名记得在上一步操作完成后清空画布然后给个明显的“重签”入口。流程上保持“签名 → 确认 → 提交”三个步骤每一步都有明确反馈对用户体验的提升非常明显。第四提交表单时对签名结果做校验代码里要考虑到用户根本没写任何内容时generate()的返回值并给出友好的提示。不要依赖后端的非空校验前端直接拦下来体验会好很多。7. 扩展玩法与进阶用法7.1 多签名区域的实现现实中有些业务的表单不止一个签名位比如一个审批单可能需要经办人签字、部门负责人签字、分管领导签字三个签名位在一张单子上。vue-esign 是单画布组件怎么实现多签名区域呢我的做法是用数组动态渲染多个 vue-esign 组件每个组件绑定不同的数据索引。比如template div v-for(item, index) in signers :keyindex vue-esign :refsignRef index :width800 :height200 / button clickhandleSignConfirm(index)确认签名 {{ index 1 }}/button /div /template然后在业务方法里通过动态 ref 名拿到对应的组件实例handleSignConfirm(index) { const signRefName signRef${index} const result this.$refs[signRefName]?.generate() if (!result) { alert(请先签名) return } this.signers[index].signature result }这种做法的好处是各个签名区域互相独立不会相互干扰业务代码也不复杂就是动态 ref 名稍微绕一点注意别在循环里用同一个 ref 名称否则访问不到具体组件实例。7.2 签名图与合同/单据的合成处理有些场景下签名图片不能只是单独上传还要合成到一份 PDF 或者一张背景图里。比如在线签合同用户手写签名后前端要把签名“贴”到合同的指定位置然后再统一提交。这种场景下 vue-esign 生成透明背景 PNG 的能力就显得非常重要了。只要不设置bg-color生成的 PNG 就是透明背景的前端可以把这张图片放到合同截图对应坐标上再通过 canvas 或者一些图形处理库合成一张最终的效果图。考虑到在实际项目里直接操作 canvas 合成图片对大多数人来说比较麻烦我建议在后端做合成前端只负责提交签名 base64 和合同位置参数。后端拿到这些数据后用图像处理库把签名图贴到合同模板上。这样实现逻辑更清晰前端代码保持轻量。7.3 签名数据的存储与校验关于签名存储与使用最后再补充几点实务经验。签名图片文件建议单独存储不要塞进业务表的大字段里。单独的文件存储可以方便后续的图片压缩、防盗链、CDN 加速等操作。如果业务上有审计需求签名除了图片本身还要附带签名时间、IP 地址、设备信息等元数据方便后期追溯。签名在法律上能被认可的关键在于能否证明签署人身份和签署行为真实性。组件本身不负责身份认证前端层面可以结合短信验证码、身份信息核验等方式确认签名人身份。需要更高强度认证的场景还需要引入专门的电子签章服务这个就不是 vue-esign 这种纯前端组件能覆盖的了。我在实际项目里做的流程是用户在签名前需要先通过手机验证码验证身份然后签名签名图和验证凭证一起提交给后端。这样起码在业务流程上把“签名人是谁”这个关键问题做了确认。对于普通的内部审批、单据确认类业务这个链路已经够用了。8. 写在最后的几点体会vue-esign 这个组件技术复杂度不算高但它把“手写签名”这个业务需求做成了一个几乎无感的接入体验这是它最有价值的地方。接手过几个需要签名的项目以后我的一个明确感受是像签名组件这种看似小众、实则高频的业务能力能不重复造轮子就别造轮子把时间花在业务本身的打磨上更值。最后再分享一个小技巧二次封装的时候可以在 vue-esign 外面再包一层自己的业务组件把调用generate()、校验空签名、弹出确认框这一套流程固化下来业务方使用的时候只需要传一个保存回调函数就行。这样整个团队接签名功能的速度会非常快而且行为完全统一不会有一个人一个样。如果你正准备在自己的 Vue 项目里接电子签名建议你直接用 vue-esign 快速搭一版试试先跑通再到真机上验证移动端的细节。这东西用顺手了比自己去写一整套 canvas 签名逻辑省下的时间足够你多写好几个功能模块了。