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| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
text | string | — | 要编码的文本,支持 UTF-8 |
ecLevel | ECLevel | ECLevel.M | 纠错级别,可选 L / M / Q / H |
version | number | 自动 | 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| 参数 | 类型 | 说明 |
|---|---|---|
qr | QRCode | generateQRCode 的返回结果 |
canvas | HTMLCanvasElement | 目标画布元素 |
options | RenderOptions | 渲染选项 |
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,
): stringts
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.data | Uint8ClampedArray | Uint8Array | number[] | RGBA 像素数据,每 4 个元素为一个像素 |
input.width | number | 图像宽度 |
input.height | number | 图像高度 |
从 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:1:3:1:1 比例定位三个定位图案
- 模块采样 — 双线性插值采样每个模块
- 格式信息解码 — BCH 解码获取纠错级别和掩码
- 去掩码 — 应用对应掩码恢复原始数据
- RS 纠错 — Reed-Solomon 解码修复数据错误
- 数据解码 — 从 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!'
}