资讯动态

纯静态HTML+JS生成条形码与二维码:离线可用的完整方案

发布时间:2026/10/13 11:38:02 来源:尧图企业网站定制
简介面向需要在前端页面直接生成可扫描条码的开发者这是一套基于原生 HTMLJavaScript 的纯静态实现可在无服务器环境下同时生成条形码与二维码。包内共 8 个文件压缩包约 79KB4 个 JS 文件分别负责 JsBarcode 与二维码核心逻辑HTML 文件搭建页面交互CSS 控制展示样式另有 License 和 Markdown 文档便于合规使用与快速阅读。已有 1398 人学习下载。使用时输入数据并触发按钮即可在同一页面实时渲染两种码覆盖 EAN、CODE128 等常见条形码类型并支持二维码纠错级别等配置。完整示例代码可直接嵌入物流、零售、信息登记等业务场景适合希望快速集成或二次改造的前端开发者。1. 纯静态页面同时生成条形码和二维码一个 HTML 文件就能跑通的离线方案标题里“htmljs生成条形码和二维码 纯静态”这句话回答的是这类需求最核心的疑问到底要不要后端答案是不用。条形码和二维码本质上是把一段文本按指定编码规则画成黑白图案这个运算在浏览器里就能完成。仓库打临时物料标签、活动方现场生成签到码、小团队在离线环境给产品贴标这些场景都适合用纯静态方案U 盘里放一个 HTML 文件和几个 JS 库双击即用数据不出本机部署到内网也不需要安装任何服务。这篇文章按我实际做过的路径来讲库怎么选、参数怎么调、条码和二维码怎么拼在一张图里、哪些地方最容易翻车以及怎么封装成能批量干活的小工具。2. 条形码与二维码的选型逻辑先别写代码把制式和容错定下来2.1 条形码选 CODE128 还是 EAN-13字符集决定你的应用场景条形码不是只有一种。EAN-13 是商品流通领域的事实标准只能编码 13 位数字最后一位是校验位不允许字母、不允许随意长度。如果场景是正规零售货架、供应链流通那基本没得选EAN-13 或 EAN-8 才可能被外部系统识别。但如果只是给内部零件编号、给文档打归档码、给固定资产贴标签内容里混了字母和横线或者长度不固定那就得用 CODE128。CODE128 支持 ASCII 全字符集长度灵活密度比 EAN-13 高是“内部使用”场景里最不容易踩坑的制式。我在某公司的模拟项目里踩过一次用 CODE128 生成了 12 位纯数字标签发给供应商时没指定制式结果对方的老式扫码枪部分能扫、部分扫不出。查了半天才发现是子集问题——CODE128 对纯数字内容会自动选择最紧凑的 CODE128C 子集某些老设备对 C 子集支持不完整。解决方法是强制指定 CODE128B或者干脆换 EAN-13。这引出一条经验内部系统优先 CODE128但先确认扫码枪兼容性外部流通优先 EAN-13别挑战生态。JsBarcode 的配置里通过 format 字段指定制式。常见制式对比如下制式字符范围典型长度用途校验位CODE128全 ASCII可变建议不超过 20 字符内部编号、档案、资产标签自动EAN-13数字13 位固定商品零售、供应链强制Code39大写字母数字少量符号可变工业旧系统可选EAN-8数字8 位固定小体积商品强制2.2 二维码容错级别与尺寸扫码距离和数据长度怎么权衡二维码有四个容错级别 L、M、Q、H分别对应约 7%、15%、25%、30% 的码字可被污损后仍能识别。长期贴在金属设备或货架上的标签容易被划伤、沾油污这类场景我一般选 Q 或 H临时用的签到码、短链接码选 M 就够。容错级别越高同样数据量下图形越密反而增加近距离识别的难度不能盲目追高。长度方面很多人没概念。二维码理论能塞 7089 个数字或 4296 个字母但那是极限值实际图形会非常密集手机在正常距离根本扫不动。我的经验是二维码内容控制在 200 字符以内超过 300 字符的要么内容该压缩要么该换承载方式。在“同时生成条形码和二维码”的场景里通常条形码承载短编号二维码承载同一编号加跳转链接或更多属性信息二者内容长度差异明显这会直接影响二维码尺寸设置。尺寸选型要以“实际印刷或展示大小”来判断而不是看像素值。二维码版本越高模块数越多每模块物理尺寸固定后图形更大。JsBarcode 侧条形码宽度由内容和模块宽度自动决定高度可调qrcode.js 侧二维码整体尺寸由 width/height 控制。给标签打印机出图时一般二维码边长做到 25-35mm条形码高度做到 15-20mm这些物理尺寸再按打印 DPI 换算成像素。300DPI 下 30mm 大约是 355 像素我通常直接设 360 或 512 取整。3. 用 JsBarcode 在 Canvas 上输出条形码最小可运行代码与参数调优3.1 引入脚本与首个条形码三行代码跑通的最小页面先去把 JsBarcode 的源码文件下载到本地不要用 CDN。纯静态的“纯”字意味着离线可用内网部署时 CDN 不可达整页就废了。下载后放在 js 子目录。最小页面只需一个 canvas 标签、一个 script 引入、一句调用!DOCTYPE html html langzh-CN head meta charsetUTF-8 title条形码生成最小页面/title /head body canvas idbarcodeCanvas/canvas script srcjs/JsBarcode.all.min.js/script script JsBarcode(#barcodeCanvas, A20240501-CN-001, { format: CODE128, lineColor: #000000, background: #ffffff, width: 2, height: 80, displayValue: true }); /script /body /html这句调用的第一个参数是选择器或 canvas 元素本身第二个参数是要编码的内容。这里故意用带字母和横线的编号不走纯数字就是为了让 CODE128 的全字符集能力生效。format 指定制式lineColor 和 background 控制条色与底色。注意条码识别对黑白对比有硬性要求不要用灰色条或浅色底。width 是每个条模块的宽度像素取值 1-3 之间height 是条总高度。displayValue 设为 true 会把内容文字印在条下方仓库贴标时人眼核对能省很多事。JsBarcode 会自己计算条形码总宽度并重设 canvas 尺寸不需要手动指定 canvas 宽度。但调用时机必须在 DOM 加载之后脚本放在 body 末尾最稳。如果页面结构里有动态加载的内容要保证目标元素已经插入 DOM否则控制台直接报错这是新手最常见的翻车点。3.2 必调参数模块宽度、静区边距、文字区对齐条形码“扫不出来”大多不是库的锅而是参数没配合好。最容易出问题的两个参数是 width模块宽度和 margin左右边距。width 低于 1 时条太细普通打印机会糊成一片高于 3 时总宽度迅速膨胀标签纸放不下。margin 默认 10意思是左右至少 10 像素空白这个留白叫静区quiet zone扫码枪识别前需要一段空白作为起止标记。很多扫不出来的标签就是内容贴得太满、静区被侵犯。displayValue 为 true 时文字大小和条宽度不匹配也常见。字太大会挤压两侧静区字号太小则人眼看不清。我把一组常用配置整理成模板JsBarcode(#barcodeCanvas, value, { format: CODE128, width: 2, height: 70, displayValue: true, text: value, font: monospace, fontSize: 16, textMargin: 4, margin: 12, textAlign: center, valid: function (isValid) { if (!isValid) console.warn(条形码内容不合法); } });valid 回调是很多人忽略的检查点。JsBarcode 遇到不合法内容比如 EAN-13 位数不足不会抛异常而是静默失败或画出空图案。加这个回调至少能在控制台给出明确警告。text 字段可以覆盖条下方显示的文字比如编号太长时只显示后几位或在中文字段旁加注。3.3 SVG 与 Canvas 输出切换及适用差异JsBarcode 的输入除了 canvas还可以是 SVG 元素或 img 标签。选哪个取决于你要干什么只在屏幕上预览img 最简单要导出 PNG 再打印或存档canvas 是正路后续要把条形码嵌入 SVG 矢量图直接传 SVG 元素最省事。三种方式的对象类型不同// canvas 输出 JsBarcode(document.getElementById(bc_canvas), SVG-VS-CANVAS, { format: CODE128 }); // svg 输出 JsBarcode(document.getElementById(bc_svg), SVG-VS-CANVAS, { format: CODE128 }); // img 输出 JsBarcode(document.getElementById(bc_img), SVG-VS-CANVAS, { format: CODE128 });img 方式有陷阱JsBarcode 实际上是生成 dataURL 再塞给 img 的 src没有矢量信息放大后边缘会模糊。SVG 方式适合高清打印因为矢量放大不损失锐度但要注意 SVG 里中文字体依赖用户系统部署到不同操作系统的内网机器时字体可能变化。canvas 导出最直接后续拼接二维码也方便所以本文后面的方案统一用 canvas。4. 用 qrcode.js 生成二维码并与条形码同框展示4.1 二维码生成器实例化与参数解释qrcode.js 是生成二维码的常用静态库纯浏览器实现不依赖后端接口。它有两种用法一是用 QRCode 类在指定 DOM 里创建实例二是用仅供输出的函数生成 dataURL。前者带初始动画逻辑更适合临时预览后者更适合我们这种标签场景。类方式的最小示例如下// 引入脚本script srcjs/qrcode.min.js/script new QRCode(document.getElementById(qrcodeBox), { text: A20240501-CN-001, width: 256, height: 256, colorDark: #000000, colorLight: #ffffff, correctLevel: QRCode.CorrectLevel.M });这里的 text 是二维码内容width/height 是输出像素尺寸。colorDark/colorLight 是前景与背景色qrcode.js 默认 colorLight 为白色官方注释强调不要改成透明否则识别率下降。correctLevel 四个档位里按上一章的判断选 M 或 Q。内容里含长 URL 或要长期暴露在恶劣环境下时才考虑 H。类方式的坑在于每次 new 都会往容器里塞一个 canvas 或 img多次调用时旧内容还在需要先清空容器。另一个常见坑text 过长时库会自动提高二维码版本同尺寸下模块数变多单模块物理尺寸变小手机对焦吃力。我的建议是宽高固定 256 或 512内容保持简短不要靠把宽高调到 2000 去追求“清晰”——二维码是黑点矩阵跟位图分辨率完全不是一回事。4.2 把两码合成一张标签Canvas 拼接与位图导出“同时生成”的最终效果要落在同一张标签上。做法是先把条形码画到一个 canvas把二维码画到另一个 canvas再用大 canvas 按坐标拼起来。我常用的标签比例是 60mm x 30mm二维码放右侧条形码放左侧中间留几毫米间隔。function composeLabel(barcodeValue, qrValue, containerId) { const barcodeCanvas document.createElement(canvas); // 先画条形码 JsBarcode(barcodeCanvas, barcodeValue, { format: CODE128, width: 2, height: 48, displayValue: true, fontSize: 12, margin: 8 }); // 再画二维码 const qrCanvas document.createElement(canvas); qrCanvas.width 180; qrCanvas.height 180; const qr new QRCode(qrCanvas, { text: qrValue, width: 180, height: 180, correctLevel: QRCode.CorrectLevel.M }); // 拼接左侧条形码右侧二维码 const labelCanvas document.getElementById(containerId); const ctx labelCanvas.getContext(2d); labelCanvas.width 360; labelCanvas.height 200; ctx.fillStyle #ffffff; ctx.fillRect(0, 0, labelCanvas.width, labelCanvas.height); ctx.drawImage(barcodeCanvas, 10, 10, 160, 160); ctx.drawImage(qrCanvas, 190, 8, 190, 190); }这段逻辑里有个关键点QRCode 类传入 canvas 元素时会直接操作这个 canvas 的宽高所以不用预先设尺寸库内部会重设。拼接时用 drawImage 的五参重载把源 canvas 缩放绘制到目标区域。坐标是调过的条形码画在左侧二维码主体基本方正。要改成上下布局只改 drawImage 的坐标参数即可。导出位图时标签 canvas 是页面内独立 canvas可以直接转 dataURLfunction exportPNG(labelCanvasId) { const canvas document.getElementById(labelCanvasId); const link document.createElement(a); link.download label_ Date.now() .png; link.href canvas.toDataURL(image/png); link.click(); }toDataURL 默认导出分辨率就是 canvas 当前像素尺寸。实测如果标签要拿去印刷360x200 像素偏小我通常把 labelCanvas 按 3 倍设到 1080x600绘制时把条码和二维码也各按比例放大。注意 toDataURL 受跨域限制纯本地生成没有跨域问题但如果页面放在内网且 canvas 里绘入了跨域图片导出时画布会被污染。5. 纯静态场景的避坑与排查现象、原因、解决5.1 中文内容生成二维码变成乱码或白屏现象把“设备编号A001”这种含中文的字符串作为二维码 text扫码出来是乱码或干脆扫不出内容。原因二维码内容编码取决于扫码端怎么解码多数手机扫码 App 默认 UTF-8但部分工业扫码枪出厂默认 GBK。纯前端生成时库本身对中文支持没问题问题出在两端字符集不一致。解决生成前确认页面文件是 UTF-8 无 BOM 保存meta 声明 charsetUTF-8如果扫码枪是早期工业型号且不可调编码就让二维码内容只用 ASCII中文拆到旁边的文本标签上。我遇到过一次更隐蔽的情况页面文件用记事本另存为时被存成 GBK导致库读到的字符串已经损坏生成出来的二维码内容完全对不上。这个不属于库的锅但很常见。规范做法是编辑器里明确以 UTF-8 保存并在部署后用文本编辑器或命令行 file 命令确认编码。5.2 条形码太长扫不出来数据位数与宽度关系现象内容 30 个字符JsBarcode 不报错画出来很窄手机勉强能扫激光枪扫不出来。原因条形码总宽度等于模块宽度乘以模块数。内容越长模块数越多要把总宽度控制在标签范围内只能减小 width但 width 小于 1 时条太细低分辨率打印机打出来各条粘连。解决不要试图把长内容塞进短标签。CODE128 在 width2 时20 字符大约要 260 像素30 字符约 380 像素标签放不下就分两行或缩短编号。displayValue 的字体调小能减少文字区占位压力。经验上有个估算宽度 2、高度 60 时条形码总宽度约等于 16 内容字符数 x 16 像素含静区。内容超过 20 字符建议换二维码承载这也是“同时生成”这个方案的另一层价值长数据走二维码短编号走条形码。5.3 导出的图片发白模糊Canvas 高分屏缩放现象页面在 2K 或 4K 屏上显示正常但导出的 PNG 放大后模糊条形码边缘出现灰度过渡而不是锐利黑白。原因浏览器在高分屏下按 devicePixelRatio 做 CSS 缩放canvas 的物理像素和 CSS 像素不一致。导出的图像素值没变但视觉上被缩放产生的锯齿误导。解决生成 canvas 时按 devicePixelRatio 放大绘制导出图自然高清。const dpr window.devicePixelRatio || 1; labelCanvas.width targetWidth * dpr; labelCanvas.height targetHeight * dpr; ctx.scale(dpr, dpr);注意 ctx.scale 之后后续所有 drawImage 的坐标和尺寸参数都按 CSS 像素写实际落点自动放大。这个坑在拼接方案里尤其常见因为坐标计算多稍不一致就会错位。条形码的锐度直接决定识别率导出图一定要检查条边缘没有灰色过渡像素。5.4 本地双击正常部署到内网后扫码失效现象本地 HTML 预览时条码和二维码都很清晰但放到内网服务器后手机扫二维码却打不开页面。原因这个“扫码失效”根本不是图形问题而是二维码内容里写了本地地址或相对路径扫码端无法解析。常见的是把二维码内容设成 http://localhost:8080/label/123电脑上没问题手机一扫自然指向自己的 localhost。解决二维码内容在生成前必须是扫码端可达的绝对地址或者干脆是纯数据文本。内网部署时注意扫码 App 对 http 非加密链接可能有拦截提示需要在扫码端设置里放开。另一类问题二维码内容里带中文参数拼成 URL扫码端解出的地址里中文变成百分号编码或乱码导致服务端 404。解决是生成前用 encodeURIComponent 对参数编码扫码端拿到的 URL 是合法转义后的字符串服务端解码还原就不会 404 了。这类问题本地测试时几乎不可能触发因为本地没服务端部署到内网后才会暴露所以内网联调时必须真机扫码测试完整链路。6. 进阶把生成器封装成可复用组件批量导出与高 DPI 印刷技巧6.1 封装生成入口一套配置多处复用给业务人员用的页面不能让他们开控制台改参数。我把生成逻辑收进一个带默认配置的对象页面上只留输入框和按钮。结构如下const LabelGenerator { defaults: { barcode: { format: CODE128, width: 2, height: 60, displayValue: true }, qr: { size: 180, correctLevel: QRCode.CorrectLevel.M } }, generate(value) { if (!value || !value.trim()) { alert(编号不能为空); return; } const barcodeValue value.trim(); const qrValue value.trim(); this.render(barcodeValue, qrValue); }, render(barcodeValue, qrValue) { // 复用 composeLabel 内部逻辑这里只做调用 composeLabel(barcodeValue, qrValue, labelCanvas); } };这层封装的价值在于业务人员接触不到库的 API所有参数改动都集中在 defaults 里。后续要换制式、调容错级别、改标签比例只动一处。我见过有人把生成逻辑散写在多个按钮的 onclick 里后来要加序列号自动递增时每个 onclick 都要改那才是真痛苦。封装的好处不只是代码量更是让“生成标签”成为一个可测试的原子操作。合法性检查也放在 generate 里空值直接提示内容超长提示“请改用编号”而不是等库画完才发现图形不对。输入框里粘贴外部数据时顺手做 trim 和替换换行符能提前排除很多脏数据问题。6.2 批量导出 PNG 与 zip 打包批量场景是表格里有 100 个编号每行生成一对条码和二维码最后全部导出。逐张下载不现实常见做法是用 JSZip 聚合所有 PNG再一次性下载。JSZip 也是静态库下载后本地引入。注意浏览器对文件下载的兼容性核心代码async function exportBatch(records) { const zip new JSZip(); const folder zip.folder(labels); for (let i 0; i records.length; i) { const canvas document.createElement(canvas); canvas.width 1080; canvas.height 600; // 按 records[i].value 绘制条码和二维码复用 composeLabel 的逻辑 // 注意这里按 3 倍尺寸绘制360x200 的 3 倍 const blob await new Promise(resolve { canvas.toBlob(resolve, image/png); }); folder.file(label_${records[i].id}.png, blob); } const blob await zip.generateAsync({ type: blob }); const link document.createElement(a); link.href URL.createObjectURL(blob); link.download labels_batch.zip; link.click(); URL.revokeObjectURL(link.href); }JSZip 的 folder 方法创建目录file 方法往目录里塞文件。toBlob 比 toDataURL 更省内存批量场景下优势明显数据量大时 toDataURL 的长字符串拼接会让页面明显卡顿。zip.generateAsync 是异步的数据一多耗时十几秒很正常界面上要加“生成中”状态否则用户以为卡死了。我吃过这个亏80 个标签的批次第一次没加状态提示用户连点了三次导出下来三个同样的 zip还抱怨页面慢。批量导出时还要注意文件名规范不要用记录里的原始 id 直接当文件名先做净化把斜杠、冒号、星号等字符替换成下划线否则 Windows 下载时会命中非法字符导致静默失败。这在演示阶段看不见真正大批量生产时全是坑。6.3 高 DPI 印刷的最后一公里物理尺寸换算实际印刷时“清晰”只是第一关尺寸正确是第二关。我最后一版复核流程固定是三步先在页面上把标签预览到 1:1 的 CSS 尺寸再打印成 PDF 或直接走标签打印机驱动然后用尺子量实际出纸尺寸。换算公式很简单像素值 物理毫米 x DPI / 25.4。比如打印 300DPI二维码边长 30mm那就是 30 x 300 / 25.4 ≈ 354 像素。如果我的 canvas 设计基准是 360 像素那么 3 倍高清就是 1080 像素正好一一对应导出后丢给打印机不会变形。如果是激光打印机或普通喷墨条形码模块宽度建议不低于 2 像素300DPI对应物理宽度约 0.17mm低于这个值容易糊。如果打印后扫码枪偶尔扫不出第一件事不是调库参数而是用放大镜看条边缘是否整齐——边缘发毛是打印机的墨量和纸张问题不是生成代码问题。这是我反复强调的排查顺序先确认图形本身的人眼可读性再怀疑代码。这个纯静态方向值不值得投入我的看法是在线生成网站一大堆但数据不出内网、样式完全可定制、批量操作可脚本化这三个点只有本地方案能给。做一次封装后续任何项目往里填数据就能复用。我现在的习惯是每接到一个打标签需求先打开这套工具把模板调好再决定要不要为它写自动化脚本。希望这些设计和踩坑记录帮到你让你在条码和二维码这条路上少走几趟弯路。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑