能经受印刷的二维码:纠错、尺寸与现场测试
二维码是 1994 年由 Denso Wave 发明的,最初用来在丰田装配线上追踪汽车零部件。约束很具体——一支手持激光扫描仪要能在任意角度、隔着一层油污和发动机烟尘、还要快到不影响产线节奏地把它读出来。这个格式里几乎所有“聪明”的设计都来自这份需求书。
今天大多数人把二维码想成"一个 URL 被画成网格"。这是个有用的浅模型,但它解释不了二维码为什么真的能扫出来——也解释不了你上次会议胸牌背后那个为什么扫不出来。
它里面到底是什么
剥掉神秘感,二维码就是字节。黑白模块编码一段比特序列,扫描器把比特读出来,操作系统或 App 决定拿这串字节做什么。QR 标准定义了四种输入模式——数字、字母数字、字节、汉字(kanji)——但对"含义"完全不发表意见。一段 URL、一份 Wi-Fi 凭据、一张 vCard、一个支付意图,都是解码方代码加上去的约定。
正因如此,同一张图,相机扫开一个浏览器,Wi-Fi 设置里扫就连上一个网络。二维码本身没有任何立场。
为什么它扫起来这么稳
三个组件干了几乎全部的活:
- 定位标志(finder pattern)。 三个角上那三个"回"字方块。解码器在任意一条穿过它们的扫描线上找 1:1:3:1:1 的条纹比例。这个比例在旋转和大多数透视形变下都保持,所以你可以扫贴在弯曲咖啡杯上的码。
- 静默区(quiet zone)。 码周围那一圈空白。规范要求 4 个模块宽;很多解码器忍 2 到 3 个,但失败率会陡升。这一点经常被低估。
- Reed-Solomon 纠错。 内置的。哪怕在最低纠错等级下,丢失 7% 的模块也能恢复数据。
注意顺序。码扫不出来时,本能反应是把纠错等级调高。但实际上:静默区、对比度、物理尺寸,影响更大——而修这三项通常是免费的。
四种纠错等级
L、M、Q、H——分别能恢复约 7%、15%、25%、30% 的模块。
更高等级靠加冗余实现,意味着要画更多模块——固定物理尺寸下,每个模块就更小。坊间智慧是"用 H 等级,这样可以在中间放 logo",对了一半。H 容忍约 30% 遮挡,但同样信息量下 H 等级的码也比 M 密,每个模块在印刷上更小。低于某个临界尺寸 / 介质 / 摄像头,带 logo 的 H 等级码会比无 logo 的 M 等级码扫得更差。
没有干净的拇指规则。如果非要选一个:默认 M,有具体理由再往上调,并且在真实介质上测。
静态码 vs 动态码
静态码直接把最终 payload 编进去。你放进去什么,扫描器就永远看到什么。
动态码编进去的是一个跳转 URL,指向你控制的服务。这个服务决定返回什么——按地区分发不同目的地、A/B 变体、活动结束后的开关、扫描数据分析。代价是一份永久依赖:跳转域名续费失败或服务商倒闭,每一份印出去的副本都瞬间变成死链。
务实划分:
- 长期使用、离线场景、内容不会变 → 静态。
- 绑定运营活动、可能需要测量或修改的 → 动态。
有意思的失败模式发生在接缝处。一张静态码指向一个你五年内可能不续费的域名是定时炸弹。一张动态码做进了博物馆展品,服务商一次故障就让展品集体下线。
真实世界里二维码失败的原因
几乎每一份"这码扫不出来"的报告都能追溯到下面之一:
- payload 太长。模块太多,印刷尺寸下手机摄像头在正常扫描距离根本分辨不出来。
- 对比度不够。带品牌色的码放在带品牌色的背景上,显示器上看挺好,糟糕光照下直接消失。
- 没有静默区。设计师特别喜欢把码贴着边框或者另一张图。
- logo 太大。H 等级给你约 30% 的模块预算;居中正方形占面积 25% 通常没事,35% 就掷骰子。
- 码扫得对,但目的页面慢或者 404,用户怪到码头上。
最有用的发布前检查:在真实最终尺寸下、用一台不是你设计设备的手机、在比理想光照差一点的环境下扫一遍。 其他都是表演。
什么时候不该用二维码
二维码闪光在物理 / 数字边界上——海报、包装、设备配对、餐桌点菜、票务。它存在的价值是替用户省去打字。
桌面屏幕上摆一张二维码、对应一个用户五秒就能打完的短 URL,价值为零。如果目的地是一个本来用户在原处一次点击就能完成、却被改成"扫码-跳转-五步表单",那它价值是负的。
生成的工程实践
- 先压缩 payload。 URL 太长是无法读取的最大单一原因;做一次跳转或短链通常划得来。
- 挑能装下你设计需求的最低纠错等级。 M 是不错的默认。
- 保留 4 个模块的静默区。 不是 3 个,不是"看起来够了"。
- 至少在两台手机上测,最好一台不是你自己的。
- 印刷场景,在真实纸张、真实尺寸上测。 商场灯下的铜版纸表现和显示器完全不一样。
印刷前的发布检查清单
不要只凭肉眼确认二维码“看起来没问题”,应当把最终导出的图片当作发布制品测试。先扫描实际要分发的 PNG 或 SVG,再按最小预期尺寸打印,在正常光线和实际扫描距离下,用不止一部手机进行测试。高分辨率屏幕上的预览会掩盖纸张、覆膜反光、低质量打印和裁切误差造成的问题。
四周要保留完整静区,不要让文字、边框、Logo 或裁切线侵入。优先使用浅色背景和深色模块,保证足够对比度。如果目标地址经过短链或重定向,域名应由自己控制、启用 HTTPS,并且维护周期要覆盖印刷品的整个使用寿命。最后,把目标地址、测试日期和设计源文件一起记录,方便后续维护者验证,而不是靠猜测二维码原本应该跳到哪里。
主要参考资料
用于核对本文技术细节的标准与官方文档。
相关文章
继续阅读同一主题领域的实践指南。
Node 生产 Dockerfile 里到底该有什么,不该有什么
网上大多数 Node Dockerfile 都把 node_modules 直接拷进镜像、用 root 运行、最后产出一个 900 MB 的层。本文只讲那几个真正影响构建时间、镜像体积和运行时安全的决定:基础镜像、多阶段构建、依赖层缓存、NODE_ENV 陷阱,以及为什么你的 docker-compose 不该照搬生产。
在用户之前发现缺失的翻译键和插值参数不匹配
缺失的翻译键会把原始键路径直接渲染给用户,插值参数不匹配会渲染出空串或崩溃。这两者在评审中都容易漏,因为开发者的语言包永远有全部键。本文讲如何结构化比较 locale JSON 文件、找出缺失键,并在发布前抓住参数不匹配。
能真正压测 UI 的 mock 数据(而不是只把页面填满)
大多数 mock 数据是同一行复制十遍、只换个 id。它填满页面,却什么都测不到。本文讲如何生成能压测布局边界、长名字、缺失字段、空状态,以及会破坏格式化代码的日期和数字格式的 mock 数据,并通过字段推断让一个 JSON 样本一步变成贴近真实的数据集。