ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

H5签名横屏适配:jSignature源码落地与避坑指南

H5签名横屏适配:jSignature源码落地与避坑指南 简介面向需要快速落地移动端电子签名功能的 H5 开发者这套基于 jSignature 的横屏签名源码提供了完整可运行示例。项目内置 jSignature 插件本体、配套 CSS 样式与前端交互逻辑无需额外配置即可运行支持手机横向屏手写签名保存为图片时可选择格式便于灵活接入各类业务场景。压缩包共 69 个文件其中 27 个 JavaScript 脚本覆盖插件核心逻辑与页面交互15 个 CSS 样式表方便直接调整视觉风格另有 3 个 HTML 示例页面以及 ttf、woff、eot、otf 字体资源、jpg/png 图片素材完整配套整体仅 2.98MB目录结构紧凑可以直接引入项目或按需二次改造。其中还附带 PSD 设计稿前端还原界面细节时更为省力。已有 1073 人学习/下载对希望快速上手 jSignature、理解移动端横屏签名界面搭建与图片导出流程的开发者具有直接参考价值也可以按需调整插件参数与样式快速复用到实际项目中。1. 先想清楚拿到 H5 签名横屏源码你要解决的不是“签名”而是“横屏”花半小时把 H5签名横屏jSignature源代码完整版可直接运行 跑起来然后被你自己的手机坑半天是这个方向最典型的开局。网上这类完整包通常能在桌面浏览器秒开画板顺滑、导出正常可一进微信内置浏览器横屏要求加上之后坐标偏移、画布错位、键盘弹起再落笔断线问题一个接一个。把 jSignature 完整源码落地成可上线的 H5 签名横屏页核心不是抄源码而是把三件事对齐运行环境、画布参数、横屏适配策略。这套做法写给要交付活动现场签到、审批留痕或合同确认页的前端和联调工程师按步骤跑跑完能复现、能排错也能直接拿去给后端碰接口。2. 源码落地用一条命令在本地跑通 jSignature 签名板2.1 拿到“完整版”先做什么按这三步检查依赖而不是双击 index.html很多人拿到源码习惯先打开 index.html 看效果这是第一个翻车点。jSignature 是经典的 jQuery 插件它在初始化时操作 DOM 生成 canvas任何一步依赖缺失页面都不会报明显的语法错误只会在控制台留一句 “jSignature is not a function”。这句话很有迷惑性新手往往以为是版本不兼容实际上只是 jQuery 没加载成功。拿到源码包后先不要双击 index.html按下面三步看文件结构。第一步确认包里有一个 jQuery 文件。不管叫 jquery.min.js 还是 jquery.js它是 jSignature 唯一的强依赖没有本地文件就换自己的 CDN 地址但要注意部署到内网或者政务网时 CDN 可能被安全策略挡掉。第二步确认 jSignature 主文件被页面正确引用。主文件在完整版里通常叫 jSignature.min.js 或 jSignature.js文件名可以变但 script 标签的引用顺序必须在 jQuery 之后。第三步找到初始化入口确认页面里有一个专门放签名画布的 div结构下面这个样子。div idsignature/div script srcjquery.min.js/script script srcjSignature.min.js/script script $(#signature).jSignature({ width: 600, height: 300 }); /script这段 HTML 的逻辑很直白div 负责放签名画布script 按顺序加载两个库最后调用 jSignature 完成布局。这里有两个新手常犯的错误一是把 jquery 的 script 标签放到 jSignature 后面二是在 DOM 还没 ready 的时候就执行初始化。第一个错误会直接报找不到函数第二个错误不会报错但画布会被渲染成 0 高度鼠标怎么画都只有一条细线这也是很多“为什么签名板没用”的隐藏真相。2.2 本地起服务为什么我不让你直接双击 index.html既然写着“可直接运行”为什么还要坚持用本地服务因为 jSignature 的导出依赖 canvas 读取像素数据在某些浏览器里用 file:// 协议打开页面时 canvas 会被视为“污染的画布”导出 PNG 时要么报安全错误要么输出空白图。这个现象不是每个浏览器都有但现场交付时只要遇到一次就够折腾半小时。我的习惯是拿到源码先放进一个独立目录然后在这个目录里起一个静态服务。cd /path/to/signature-demo python3 -m http.server 8080然后在浏览器访问 http://localhost:8080/机器的 python3 如果不在 PATH 里换成 python -m http.server 8080。这个命令的本质是启动当前目录的静态文件服务jSignature 的 html、js、css 全部通过 http 协议加载canvas 的读取限制就绕开了。为什么用 8080 而不是 80因为 80 端口经常被本机其他服务占用8080 冲突概率低而且不容易被防火墙优先拦截。启动之后打开浏览器的开发者工具切到 Console 面板看有没有红色报错。常见的红色报错有三类jQuery 404、jSignature 404、以及 “Refused to execute script ... MIME type”后一类多半是静态服务把 .js 文件响应类型发错了换一个干净的目录重新起服务就好。如果你拿到的是一个压缩包里面好几个 index.html 分别对应不同演示页不要嫌麻烦逐个打开看哪个页面能画出连续笔迹哪个页面就是真正可运行的主入口。2.3 跑通后两个验收点能画线、能导出 SVG 和 PNG服务起来、页面也出来了不等于签名板真的能用。我一般做两个验收点把鼠标当成手指先画一道然后分别看两种导出格式。第一个验收点是画线连续。用鼠标在签名区内快速画一个“S”不要慢慢画快速拖动才能测出事件连续性。如果笔迹断成一节一节通常是 jSignature 的事件绑定被页面里其他手势库拦截先去查全局有没有 touch-action 或 preventDefault 的代码。第二个验收点是导出数据。jSignature 的导出统一走 getData 方法常见做法是打开控制台把原始数据和 PNG 都打印出来。var signature $(#signature); var raw signature.jSignature(getData); var pngURL signature.jSignature(getData, image/png); console.log(raw[0]); // 原始格式名可能是 svg 或 compress:base30 console.log(raw[1]); // 具体数据SVG 是 XML 字符串base30 是压缩串 console.log(pngURL.slice(0, 50)); // 看一眼是不是 data:image/png;base64到这里我能靠三个输出判断签名板是否真的可用。raw[0] 告诉我们当前输出格式这决定了后端接口怎么对接raw[1] 是真正要传走的签名数据pngURL 是提交通用格式预览框的 img src 可以直接塞。如果 pngURL 打印出来不是 data:image/png 开头说明库没跑在干净的 canvas 环境里回到 2.2 重新检查本地服务。提示现场交付活动页时别只在界面上放“保存并可预览”联调阶段把这两个导出值在控制台打出来一次能帮你快速分清是前端数据问题还是后端接口问题。3. 横屏签名板的三个关键参数画布尺寸、输出格式与重新初始化3.1 画布尺寸别写死 600x300jSignature 依赖容器实际宽高前面验收用的 600x300 是桌面端经典尺寸在手机横屏场景直接用有两个问题宽度超过屏幕高度又不够。jSignature 初始化时会把 width、height 同时用在 canvas 属性和 CSS 尺寸上如果你写死 600x300在布局视口只有 700px 宽的安卓手机里画布会被压缩显示笔迹坐标跟着失真这就是很多横屏签名“签出来比手指细一圈”的根因。正确做法是让签名板跟随容器尺寸。先把容器渲染出来读取 clientWidth 和 clientHeight再传给 jSignature。var wrap document.getElementById(signatureWrap); $(#signature).jSignature({ width: wrap.clientWidth, height: wrap.clientHeight, lineWidth: 2, color: #171717, background: #ffffff, sigGuideline: false });为什么这样选有三个理由。第一width 和 height 写死成 px不同型号设备会出现两边留白或画布溢出大屏活动现场尤其明显。第二lineWidth 的单位是像素而不是毫米同样 2px 在一部 320dpi 手机和一台 220dpi 平板上手感不同这是正常现象不要为了“追求一致”去改全局缩放那样反而会让坐标对不上。第三sigGuideline 是 jSignature 自带的签名辅助线横屏场景特别碍眼建议显式关掉否则导出图片里会留一道灰色线后端打印时很难处理。初始化之后要验证容器宽高不是 0。如果你在容器还是 display:none 的时候就初始化读到的 clientWidth 是 0画布会被建成 0 高度因此签名页布局里要给签名容器一个 min-height比如 320px再放进页面流。这一步是横屏签名板在安卓 WebView 里最常见的隐性故障后面避坑章会再展开。3.2 输出格式选 SVG 还是 PNG别让后端一开始就接 base30第三个关键参数是输出格式。jSignature 的 getData 不带格式参数时返回的是数组第一个元素是格式名第二个元素是数据体。格式名可能是 svg也可能是 compress:base30取决于库内部的配置。很多前端在这里直接点“保存”就把 base30 传给后端后端一存 MySQL 就乱码联调现场直接变成甩锅现场。这两种格式的区别非常关键。SVG 是矢量路径文本体积小后端可以转 PDF适合合同回执缺点是包含 XML 结构后端解析必须支持 XML。compress:base30 是坐标增量压缩串体积最小但必须配套解码器后端没对接过这个格式十个有九个会当成普通字符串处理。image/png 是位图前端预览、上传接口、后端打印都通用缺点是放大打印会糊但绝大多数活动签到和审批留痕场景完全够用。通常我给后端联调时用下面这段统一封装。$(#saveBtn).on(click, function () { var pngBase64 $(#signature).jSignature(getData, image/png); var rawData $(#signature).jSignature(getData); window.__signPayload { svg: rawData[0] svg ? rawData[1] : null, png: pngBase64 }; });这里的原则是除非后端已经在项目里接好了 jSignature 的 base30 解码组件否则一律走 PNG。SVG 可以作为备份字段存起来用于以后打印高分辨率回执。先让 PNG 链路走通再谈压缩和矢量优化。后端一旦能正常收到图片联调就成功了一大半。3.3 orientationchange 后重新初始化把旧笔迹原样画回去横屏签名页最重要的参数是“旋转后重绘”。因为 jSignature 不监听 resize浏览器旋转之后画布尺寸和手指坐标系统就不同步不重绘就会出现“手指画在右边笔迹落在左边”的诡异现象。在讲重绘代码之前先明确 jSignature 的 API 边界它没有提供官方 resize 方法。常见做法是销毁画布 DOM再重新初始化然后把旋转前的签名数据用 setData 塞回去。下面这段代码可以直接放进你的横屏页面。var isSignatureReady false; function getSignatureConfig() { var wrap document.getElementById(signatureWrap); return { width: wrap.clientWidth, height: wrap.clientHeight, lineWidth: 2, color: #171717, background: #ffffff }; } function initSignature() { $(#signature).empty().jSignature(getSignatureConfig()); isSignatureReady true; } function restoreSignature() { if (!isSignatureReady) return; var oldData $(#signature).jSignature(getData); initSignature(); if (oldData oldData.length 2) { $(#signature).jSignature(setData, oldData); } } window.addEventListener(orientationchange, function () { setTimeout(restoreSignature, 300); });empty 之后重新 init是因为 jSignature 一旦实例化内部坐标系统就已经固定只改 CSS 尺寸不会更新坐标系重画必然错位。setData 会把旧笔迹按原格式画回新画布路径不丢坐标也还是原始签名的坐标只是在新的尺寸里等比重绘。延迟 300ms 是经验值覆盖 iOS Safari 和微信内置浏览器主流转场动画的时间。注意旋转发生时如果用户正好按住了画布restoreSignature 会打断正在画的这一笔。业务上建议在 orientationchange 回调里先禁用保存按钮等重绘完成再恢复避免用户误以为保存成功了。4. 横屏适配专项微信内置浏览器与系统旋转的取舍4.1 强制横屏三种做法为什么 CSS 旋转最坑做横屏 H5 签名页第一个绕不开的问题就是“怎么让页面变成横屏”。常见做法有三种适用场景完全不同。第一种是 CSS transform 强制旋转把整个页面容器 rotate(90deg)再把宽高对调。这种做法的视觉效果最“强制”打开页面就横过来了不需要用户转手机但它是三个方案里对 jSignature 最不友好的。原因在于 jSignature 在 touchmove 回调里读的是 clientX/clientY这是浏览器可视视口坐标系CSS transform 只改了渲染层事件坐标系纹丝不动。结果是手指在屏幕下方签名笔迹却出现在屏幕上方左右还可能镜像除非你在事件层自己做逆变换否则基本不可用。第二种是引导系统旋转。页面保持正常的响应式布局通过样式提示用户把手机横过来浏览器完成真实的旋转随后触发 orientationchange 和 resize 事件。这种方案事件坐标天然一致配合第 3 章的重绘逻辑是成本最低、最不容易翻车的做法。第三种是 Web Fullscreen orientation.lock。调用 screen.orientation.lock(landscape)可以在安卓现代浏览器里强制锁定横屏但 iOS Safari 不支持而且需要页面先进入全屏。活动大屏、展厅一体机这类封闭环境可以用普通 H5 页面不建议作为首选。做法浏览器兼容性对 jSignature 坐标影响适用场景CSS transform 强制旋转高破坏触摸坐标不推荐配合 jSignature引导系统旋转高无影响手机端默认选择Fullscreen orientation.lock安卓现代版需重新初始化展厅一体机、封闭设备4.2 visual viewport 与布局视口为什么旋转后签名错位理解横屏签名为什么错位需要分清两个视口。布局视口是页面布局参考的坐标系CSS 里的百分比、clientWidth 都基于它可视化视口是用户实际看到的窗口区域触摸事件里的 clientX/clientY 基于它。正常情况下二者原点重叠旋转动画结束后它们也基本对齐但问题出在中间态。orientationchange 触发时jSignature 已经按旧的布局视口尺寸初始化了而触摸事件用的是新视口坐标两个坐标系没对齐于是笔迹偏移。如果页面里有输入框安卓软键盘弹出时可视化视口高度会被压缩布局视口不一定变照样出现“画到一半错位”的现象。这就是为什么横屏签名页不能单纯依赖窗口 resize必须监听旋转和键盘两个维度。还有一种情况是视觉视口宽高变了但容器的高度还是旧的导致签名内容被截断或留下大块空白。解决思路是重绘前先读容器新尺寸再销毁重建 jSignature不要尝试手动缩放现有 canvas。4.3 一版可复用的横屏签名基线样式下面这份 CSS 是一版可以直接抄走的横屏签名页基线。它不强制旋转而是让页面配合系统旋转签名区域占满可用空间并阻止触摸手势打断画线。html, body { margin: 0; height: 100%; overflow: hidden; background: #f2f4f7; } .sig-stage { display: flex; flex-direction: column; width: 100%; height: 100%; } .sig-board { flex: 1; margin: 12px; min-height: 320px; touch-action: none; user-select: none; -webkit-user-select: none; background: #fff; } .sig-board canvas { display: block; width: 100%; height: 100%; }min-height: 320px 是给初始化用的兜底。如果容器高度是 flex 撑出来的而初始化时机太早clientHeight 可能拿不到真实值画布就会变成 0 高度。touch-action: none 很关键它禁止浏览器在签名区域接管手势否则 iOS Safari 会把第一笔当成滚动画到一半页面就动了。canvas 的 display: block 消除 inline 元素底部空隙顺手解决画布下方多出几像素白边的问题。这段样式配合 3.3 的 restoreSignature就能覆盖“用户从竖屏进页面转横后签名再转回竖屏”的完整流程。每次旋转后重绘一次签名数据保留位置不漂。5. 避坑记录直接把 jSignature 源码丢进业务最容易翻车的 5 个问题5.1 强制旋转后笔迹与手指大面积错位现象用 CSS transform 把页面 rotate(90deg) 做成强制横屏手指在屏幕下方签名落笔预览的笔迹出现在屏幕上方方向也反过来。原因jSignature 监听 touchmove 时读取的是 clientX/clientY属于可视视口坐标系而 CSS transform 只改变渲染结果不改变事件坐标来源。两个坐标系不一致画布自然对不上。解决不要对签名容器做 CSS 旋转。改用 4.1 的第二种做法引导系统旋转如果产品经理坚持强制横屏必须在 touchmove 层自己写坐标逆变换再喂给 jSignature改动量大且需要反复调设备不推荐新手碰。5.2 转横屏后签名区域只有一半宽现象用户竖屏进入页面后把手机转横签名区域没有占满整个横向宽度只占了原来竖屏时的宽度右侧一大块空白。原因orientationchange 触发时浏览器还没有完成新视口布局这时候读取 wrap.clientWidth 拿到的是旋转前的宽度。jSignature 按旧尺寸初始化画布自然只有一半。解决在 orientationchange 回调里加 300ms 延迟再重绘参考第 3 章的 restoreSignature。如果延迟后还是偏窄就在初始化前等一个 requestAnimationFrame确保布局已经收敛。5.3 安卓微信软键盘把签名区域顶上去画到一半断线现象签名页里放了姓名输入框用户填完名字接着签名签着签着画布突然跳动笔迹断线或者保存出来的图上下被截掉一段。原因软键盘弹出后可视化视口高度变化页面重排签名区域被移动或压缩。jSignature 内部不监听 resize旧坐标系统和新可视区对不上。解决把输入框从签名页拿掉签名完成后再进入填写信息的页面。必须放在同一页时监听 visualViewport 的 resize 事件回调里做同样的保存数据、销毁重建、恢复笔迹操作。if (window.visualViewport) { window.visualViewport.addEventListener(resize, function () { clearTimeout(window.__kbTimer); window.__kbTimer setTimeout(restoreSignature, 200); }); }5.4 后端收到 base30 字符串直接存库读出来是乱码现象前端用 getData 不带参数时的默认格式压缩串传给后端后端原样存进 MySQL再次读取展示时是一堆看不懂的字符。原因compress:base30 是 jSignature 自定义的坐标压缩格式不是普通 JSON也不是 UTF-8 文本必须配套解码算法才能还原成 SVG 路径。后端没有对应实现就会把二进制安全字符串当普通文本处理。解决后端没对接过 jSignature 解码组件时前端统一导出 image/png想要高分辨率回执再附加 SVG 字段。base30 压缩率虽好但需要后端重新开发解码模块放到二期再商量。5.5 iOS Safari 第一笔画到一半突然变成滚动页面现象iPhone 上进入签名页第一笔刚落下还没画完整个页面就跟着手指滚动了或者长按画布弹出放大镜后续笔画断断续续。原因iOS 默认的触摸行为会在部分手势下接管页面touch-action 默认值允许浏览器处理双指缩放和滚动长按也触发文本选择这些都会截断 jSignature 的画笔事件。解决给签名容器设置 touch-action: none、user-select: none、-webkit-user-select: none再加 -webkit-touch-callout: none禁止长按唤出菜单。这样把画布区域完全留给画笔事件滚动交给容器之外的空间。6. 进阶改造一键清空、90 度旋转与压缩输出一次到位真实交付时保存按钮最后往往还要做三件事清空重签、把横屏签名的图片旋转成竖版、压缩体积方便上传。清空可以直接用 jSignature 自带的方法旋转和压缩放到 canvas 输出层处理不碰库内部逻辑这是避开前面所有坐标坑的最稳思路。$(function () { $(#signed).jSignature({ width: 720, height: 380, lineWidth: 2, color: #171717, background: #ffffff }); $(#clearBtn).on(click, function () { $(#signed).jSignature(clear); }); function rotateImage(dataUrl, angle) { return new Promise(function (resolve) { var img new Image(); img.onload function () { var canvas document.createElement(canvas); canvas.width img.height; canvas.height img.width; var ctx canvas.getContext(2d); if (angle 90) { ctx.translate(canvas.width, 0); ctx.rotate(Math.PI / 2); } else { ctx.translate(0, canvas.height); ctx.rotate(-Math.PI / 2); } ctx.drawImage(img, 0, 0, img.width, img.height); resolve(canvas.toDataURL(image/jpeg, 0.85)); }; img.src dataUrl; }); } $(#saveBtn).on(click, function () { var pngUrl $(#signed).jSignature(getData, image/png); rotateImage(pngUrl, -90).then(function (jpegUrl) { $(#preview).attr(src, jpegUrl); // 这里把 jpegUrl 交给上传接口或隐藏表单字段 }); }); });几个参数说明。clear 是 jSignature 自带方法不需要销毁重建适合做“重签”按钮。angle 传 -90 表示逆时针旋转 90 度横屏签名板导出的是横向图片旋转后变成竖向构图更符合 A4 回执和审批流的展示习惯如果需要顺时针把 angle 改成 90走 if 分支里另一套 translate 逻辑。toDataURL(image/jpeg, 0.85) 的第二个参数是 JPEG 压缩质量签名是黑白线条为主0.75 到 0.85 之间肉眼几乎无差别体积通常只有 PNG 的三分之一甚至更少。这段代码把清空、旋转、压缩全部收敛在 UI 层后端看到的是一张标准 JPEG和任何签名库都解耦。我接手类似项目时不太会去硬改 jSignature 内部坐标旋转和压缩都放在输出层做既避免破坏库内部状态也让后端只认一张图。如果你也是第一次把 jSignature 接进横屏 H5先让 PNG 链路走通再考虑旋转和压缩这个顺序是我几次现场交付换来的习惯希望帮到你。本文还有配套的精品资源点击获取
返回列表