Skip to content

QR Code · 二维码生成与解析

纯 TypeScript 实现的二维码生成与解码器。零依赖,支持从文本生成 QR 码、Canvas 渲染输出,以及从像素数据反向解码。

类型导出

ts
export enum ECLevel { L = 0, M = 1, Q = 2, H = 3 }

export interface QRCode {
  modules: boolean[][]   // 模块矩阵,true = 深色
  version: number        // QR 版本号 (1-40)
  size: number           // 矩阵边长 (version × 4 + 17)
  ecLevel: ECLevel       // 纠错级别
}

export interface ImageInput {
  data: Uint8ClampedArray | Uint8Array | number[]  // RGBA 像素数据
  width: number
  height: number
}

export interface RenderOptions {
  moduleSize?: number    // 每个模块的像素大小,默认 4
  margin?: number        // 四周留白(模块数),默认 4
  darkColor?: string     // 深色模块颜色,默认 '#000000'
  lightColor?: string    // 浅色模块颜色,默认 '#ffffff'
}

generateQRCode

从文本生成 QR 码矩阵。自动选择合适的版本和掩码。

ts
function generateQRCode(
  text: string,
  ecLevel?: ECLevel,
  version?: number,
): QRCode
参数类型默认值说明
textstring要编码的文本,支持 UTF-8
ecLevelECLevelECLevel.M纠错级别,可选 L / M / Q / H
versionnumber自动QR 版本号 (1-40),不传则根据数据长度自动选择

基本用法

ts
import { generateQRCode, ECLevel } from '@bilibaba/ts-lab/tools'

// 生成默认纠错级别 (M) 的二维码
const qr = generateQRCode('https://ts-lab.netlify.app')

// 指定高纠错级别
const qrH = generateQRCode('hello world', ECLevel.H)

// 固定版本号
const qrV10 = generateQRCode('some data', ECLevel.M, 10)

纠错级别说明

级别可恢复比例适用场景
L≈7%环境干净、码不被遮挡
M≈15%一般使用(默认)
Q≈25%可能部分污损
H≈30%需要叠加 logo 或高容错

renderQRCodeToCanvas

将 QR 码渲染到指定 <canvas> 上。

ts
function renderQRCodeToCanvas(
  qr: QRCode,
  canvas: HTMLCanvasElement,
  options?: RenderOptions,
): void
参数类型说明
qrQRCodegenerateQRCode 的返回结果
canvasHTMLCanvasElement目标画布元素
optionsRenderOptions渲染选项
ts
import { generateQRCode, renderQRCodeToCanvas } from '@bilibaba/ts-lab/tools'

const qr = generateQRCode('hello')
const canvas = document.querySelector('canvas')!

renderQRCodeToCanvas(qr, canvas, {
  moduleSize: 8,
  margin: 2,
  darkColor: '#1a1a2e',
  lightColor: '#ffffff',
})

renderQRCodeToDataURL

将 QR 码渲染为 Data URL 字符串,适合直接用于 <img>src 或下载。

ts
function renderQRCodeToDataURL(
  qr: QRCode,
  options?: RenderOptions,
): string
ts
const qr = generateQRCode('https://example.com')
const dataUrl = renderQRCodeToDataURL(qr, { moduleSize: 6 })

// 用于 <img> 标签
const img = document.createElement('img')
img.src = dataUrl

// 或触发下载
const link = document.createElement('a')
link.href = dataUrl
link.download = 'qrcode.png'
link.click()

readQRCode

从像素数据中解码 QR 码,返回编码的文本内容。解码失败返回 null

ts
function readQRCode(input: ImageInput): string | null
参数类型说明
input.dataUint8ClampedArray | Uint8Array | number[]RGBA 像素数据,每 4 个元素为一个像素
input.widthnumber图像宽度
input.heightnumber图像高度

从 Canvas 读取

ts
import { readQRCode } from '@bilibaba/ts-lab/tools'

const canvas = document.querySelector('canvas')!
const ctx = canvas.getContext('2d')!
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

const text = readQRCode({
  data: imageData.data,
  width: canvas.width,
  height: canvas.height,
})

console.log(text) // 'https://example.com' 或 null

<img> / <video> 读取

ts
const img = document.querySelector('img')!
const canvas = document.createElement('canvas')
canvas.width = img.naturalWidth
canvas.height = img.naturalHeight
const ctx = canvas.getContext('2d')!
ctx.drawImage(img, 0, 0)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

const text = readQRCode({
  data: imageData.data,
  width: canvas.width,
  height: canvas.height,
})

解码流程

readQRCode 内部执行以下步骤:

  1. 二值化 — 自适应阈值将图像转为黑白
  2. 定位图案检测 — 扫描 1:1:3:1:1 比例定位三个定位图案
  3. 模块采样 — 双线性插值采样每个模块
  4. 格式信息解码 — BCH 解码获取纠错级别和掩码
  5. 去掩码 — 应用对应掩码恢复原始数据
  6. RS 纠错 — Reed-Solomon 解码修复数据错误
  7. 数据解码 — 从 bit 流中解析 UTF-8 文本

完整示例

ts
import {
  generateQRCode,
  renderQRCodeToDataURL,
  readQRCode,
  ECLevel,
} from '@bilibaba/ts-lab/tools'

// 1. 生成二维码
const qr = generateQRCode('Hello, ts-lab!', ECLevel.H)

// 2. 渲染为 Data URL
const dataUrl = renderQRCodeToDataURL(qr, { moduleSize: 8 })

// 3. 显示在页面上
const img = document.createElement('img')
img.src = dataUrl
document.body.appendChild(img)

// 4. 等图片加载后解码回文字
img.onload = () => {
  const canvas = document.createElement('canvas')
  canvas.width = img.naturalWidth
  canvas.height = img.naturalHeight
  const ctx = canvas.getContext('2d')!
  ctx.drawImage(img, 0, 0)
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)

  const decoded = readQRCode({
    data: imageData.data,
    width: canvas.width,
    height: canvas.height,
  })

  console.log(decoded) // 'Hello, ts-lab!'
}