Ajiu9

将二维码隐藏在猫头鹰图案中:艺术二维码的技术实现

Aug 26 · 15min

动机

传统二维码都是黑白方块,虽然实用但缺乏美感。我一直在思考:能不能让二维码变得更有艺术感,同时保证可扫描性?

这个想法催生了 猫头鹰创意二维码生成器 项目 —— 将二维码定位点伪装成猫头鹰的眼睛,数据点设计为羽毛纹理,实现艺术性与扫码功能的完美融合。

猫头鹰二维码效果
猫头鹰二维码效果 - 微信搜"阿九二维码"可在线体验
艺术二维码 1艺术二维码 2艺术二维码 3艺术二维码 4艺术二维码 5艺术二维码 6艺术二维码 7艺术二维码 8
更多艺术二维码

核心创新

这个项目的核心挑战在于:如何在改变二维码外观的同时,保持扫码器的识别能力?

我们从三个方面解决了这个问题:

1. 眼睛锚点技术(Eye Anchor)

二维码的定位点(Finder Pattern)是三个 7×7 的方块,位于左上、右上、左下三个角落。扫码器通过检测这些定位点来确定二维码的位置和方向。

创新点在于:将左上、右上定位点伪装成猫头鹰的眼睛

具体实现采用了径向分层结构

// 眼睛定位点绘制逻辑(简化版)
function drawEyePositioning(
  centerX: number,
  centerY: number,
  unitSize: number,
  ctx: CanvasRenderingContext2D
) {
  // 1. 白色护圈(保证扫码器的安静区)
  ctx.fillStyle = '#ffffff'
  ctx.beginPath()
  ctx.arc(centerX, centerY, 4 * unitSize, 0, Math.PI * 2)
  ctx.fill()

  // 2. 黑色眼线外环(深色外环)
  ctx.strokeStyle = '#000000'
  ctx.lineWidth = 1 * unitSize
  ctx.beginPath()
  ctx.arc(centerX, centerY, 3 * unitSize, 0, Math.PI * 2)
  ctx.stroke()

  // 3. 黄色虹膜(浅色中环)
  ctx.fillStyle = '#FFDA8A'
  ctx.beginPath()
  ctx.arc(centerX, centerY, 2.5 * unitSize, 0, Math.PI * 2)
  ctx.fill()

  // 4. 黑色瞳孔(深色中心)
  ctx.fillStyle = '#000000'
  ctx.beginPath()
  ctx.arc(centerX, centerY, 1.5 * unitSize, 0, Math.PI * 2)
  ctx.fill()

  // 5. 白色高光(增加真实感)
  ctx.fillStyle = '#ffffff'
  ctx.beginPath()
  ctx.arc(centerX - 0.55 * unitSize, centerY - 0.55 * unitSize, 0.4 * unitSize, 0, Math.PI * 2)
  ctx.fill()
}

关键技术点:

  1. 满足 1:1:3:1:1 扫描比例:这是二维码定位点的标准比例要求,扫码器通过这个比例来识别定位点
  2. 径向对比度:中心黑 → 中环黄 → 外环黑 → 护圈白,满足扫码器的明暗对比要求
  3. 几何对齐:通过 eyeAnchor 参数指定眼睛中心坐标,确保二维码矩阵与猫头鹰眼睛严格对齐

2. 羽毛数据点

二维码的数据点通常是方块或圆点,但为了融入猫头鹰图案,我们设计了羽毛样式

// 羽毛数据点绘制
function drawFeatherContent(
  x: number,
  y: number,
  moduleSize: number,
  ctx: CanvasRenderingContext2D
) {
  // 扇形羽毛,带有渐变和旋转
  ctx.beginPath()
  ctx.arc(x, y, 0.48 * moduleSize, 0, Math.PI * 0.8)
  ctx.fillStyle = 'rgba(0, 0, 0, 0.85)'
  ctx.fill()
}

优势:

  • 视觉上与猫头鹰羽毛融为一体
  • 保持足够的深色面积,确保扫码器采样正确
  • 支持随机旋转,增加自然感

3. 几何推导算法

如何将二维码矩阵精确覆盖到猫头鹰图案上?关键在于几何推导

// 从眼睛锚点推导二维码几何参数
const eyeDistX = Math.abs(eyeAnchor.rightX - eyeAnchor.leftX) * canvasSize

// 模块大小(取整,避免抗锯齿缝隙)
const moduleSize = Math.round(eyeDistX / (nCount - 7))

// 二维码原点坐标
const qrOriginX = leftCenterX - 3.5 * moduleSize
const qrOriginY = leftCenterY - 3.5 * moduleSize

公式解释:

  • 二维码左上定位点中心到右上定位点中心 = nCount - 7 个模块(nCount 是二维码矩阵边长)
  • 二维码原点 = 左眼中心 - 3.5 个模块(定位点是 7×7,中心在 (3, 3))

技术实现

技术栈

框架:Taro 4.x(跨平台小程序)
前端:Vue 3 Composition API
图表:Canvas 2D API
样式:Tailwind CSS + weapp-tailwindcss
二维码编码:自定义 encoder(支持版本控制)

渲染流程

核心渲染流程分为五个步骤:

// 渲染主流程
export function renderCt1(
  ctx: CanvasRenderingContext2D,
  options: RenderOptions,
  templateImage: HTMLImageElement,
  canvasSize: number,
  dpr: number
) {
  const { url, correctLevel, qrVersion, bgFadeOpacity } = options
  const physicalSize = canvasSize * dpr

  // 1. 编码二维码数据
  const { table, typeTable, nCount } = encode(url, correctLevel, qrVersion)

  // 2. 绘制白色背景
  ctx.fillStyle = '#ffffff'
  ctx.fillRect(0, 0, physicalSize, physicalSize)

  // 3. 绘制猫头鹰模板图
  ctx.drawImage(templateImage, 0, 0, physicalSize, physicalSize)

  // 4. 可选:背景淡化层
  if (bgFadeOpacity > 0) {
    ctx.fillStyle = `rgba(255, 255, 255, ${bgFadeOpacity})`
    ctx.fillRect(0, 0, physicalSize, physicalSize)
  }

  // 5. 遍历二维码矩阵,绘制数据点
  for (let y = 0; y < nCount; y++) {
    for (let x = 0; x < nCount; x++) {
      const moduleType = getModuleType(x, y, typeTable, nCount)

      if (moduleType === 'finder') {
        // 定位点 → 眼睛样式
        drawEyePositioning(x, y, moduleSize, ctx)
      }
      else if (moduleType === 'timing') {
        // 时序线 → 宽体羽毛
        drawWideFeatherContent(x, y, moduleSize, ctx)
      }
      else if (table[y][x]) {
        // 数据点 → 羽毛/圆/方样式
        drawFeatherContent(x, y, moduleSize, ctx)
      }
    }
  }
}

容错优化

为了保证扫码成功率,我们做了多重优化:

  1. 高容错等级:使用 High 级别(30% 容错率),即使部分数据点被遮挡也能解码
  2. 白色护圈:定位点外围绘制白色圆环,隔开猫头鹰的黑色轮廓线
  3. 背景淡化:可选的 bgFadeOpacity 参数,降低猫头鹰背景的对比度,减少干扰
  4. QR 区域白底qrBgOpacity 参数,在二维码区域增加白色背景,提升明暗对比

技术难点与解决方案

难点 1:定位点伪装 vs 扫码器检测

问题:定位点需要满足 1:1:3:1:1 的比例才能被扫码器识别,但猫头鹰眼睛需要自然美观。

解决方案

  • 使用径向分层结构:白色护圈(4 模块)→ 黄色虹膜(2.5 模块)→ 黑色瞳孔(1.5 模块)
  • 黄色虹膜作为浅色中环,黑色瞳孔作为深色中心,满足明暗对比
  • 白色护圈保证安静区干净,避免猫头鹰轮廓线干扰

难点 2:数据点融入背景 vs 采样准确性

问题:羽毛样式的数据点面积不均,可能导致采样偏移。

解决方案

  • 使用 contentPointScale 参数控制羽毛大小(默认 0.85)
  • 羽毛中心保持足够的深色面积
  • 时序线使用宽体羽毛drawWideFeatherContent),宽度接近方块

难点 3:几何对齐精度

问题:猫头鹰眼睛位置固定,二维码矩阵如何精确对齐?

解决方案

  • 使用 eyeAnchor 参数指定眼睛中心坐标(相对位置,范围 0~1)
  • 所有绘制坐标取整,避免浮点坐标产生的抗锯齿缝隙
  • 二维码原点通过几何公式推导:qrOriginX = leftEyeCenterX - 3.5 * moduleSize

实际效果

生成效果

  • ✅ 内容越短,识别率越高(推荐链接长度 < 50 字符)
  • ✅ 支持 URL、文本、联系方式等多种内容
  • ✅ 支持自定义配色(虹膜颜色、羽毛颜色)
  • ✅ 支持实时预览和参数调整

识别测试

经测试,在主流扫码器(微信扫一扫、支付宝、Chrome 浏览器)中的识别率达到 95%+

总结

这个项目探索了艺术二维码的可能性,核心技术包括:

  • 眼睛锚点技术:将定位点伪装成猫头鹰眼睛,兼顾美观与功能性
  • 羽毛数据点:数据点融入背景图案,提升艺术感
  • 几何推导算法:精确对齐二维码矩阵与背景图案
  • 容错优化:多重策略提升识别率

未来规划

  • 支持更多模板(熊猫、独角兽、龙等)
  • 支持动态二维码(GIF/视频)
  • AI 自动生成创意模板

扫码体验

微信扫一扫下方小程序码,体验"会隐藏"的二维码生成器:

阿九二维码小程序码
微信扫码体验「阿九二维码」小程序
> comment on mastodon / twitter
>
@2024-2025 湘ICP备2024048835号