Scanner 二维码扫描器
高性能 QR 码扫描与解析
功能描述
使用 Web Worker 后台解码二维码,支持地址扫描、支付请求、深度链接和联系人协议。
架构设计
核心类
typescript
// QRScanner - 主扫描器类
class QRScanner {
constructor(options: QRScannerOptions)
start(source: FrameSource): void
stop(): void
on(event: 'scan' | 'error', callback: Function): void
}
// FrameSource - 帧来源接口
interface FrameSource {
getFrame(): Promise<ImageData | null>
dispose(): void
}Web Worker 解码
┌─────────────────┐ ┌─────────────────┐
│ 主线程 │ │ Web Worker │
│ - 获取帧 │ --> │ - jsQR 解码 │
│ - 回调处理 │ <-- │ - 返回结果 │
└─────────────────┘ └─────────────────┘解析类型
ParsedQRContent 类型定义
typescript
type ParsedQRContent =
| ParsedAddress // 纯地址
| ParsedPayment // 支付请求
| ParsedDeepLink // 深度链接
| ParsedContact // 联系人协议
| ParsedUnknown // 未知内容支持的格式
| 类型 | 格式示例 | 说明 |
|---|---|---|
| address | 0x1234... | 纯地址 |
| payment | ethereum:0x...?value=1000 | EIP-681 支付请求 |
| deeplink | keyapp://authorize?... | 应用深度链接 |
| contact | {"type":"contact",...} | 联系人协议 |
联系人协议 (Contact Protocol)
JSON 格式 (推荐)
json
{
"type": "contact",
"name": "张三",
"addresses": [
{ "chainType": "ethereum", "address": "0x742d35Cc..." },
{ "chainType": "bitcoin", "address": "bc1qar0..." }
],
"memo": "好友",
"avatar": "👨💼"
}URI 格式
contact://张三?eth=0x742d35Cc...&btc=bc1qar0...&memo=好友| 参数 | 说明 |
|---|---|
| eth | Ethereum 地址 |
| btc | Bitcoin 地址 |
| trx | Tron 地址 |
| memo | 备注 |
ScannerJob 组件
用途
Stackflow BottomSheet 模式的扫码器,用于在发送页面等场景扫描地址。
属性
| 属性 | 类型 | 说明 |
|---|---|---|
| chainType | 'ethereum' | 'bitcoin' | 'tron' | 'any' | 限制扫描的地址类型 |
使用示例
tsx
// 在 SendPage 中使用
const flow = useFlow()
const callbackRef = useRef<(addr: string) => void>()
// 打开扫码器
callbackRef.current = (address) => {
setRecipientAddress(address)
}
setScannerResultCallback(callbackRef)
flow.push('ScannerJob', { chainType: 'ethereum' })验证器规范
内置验证器
| 验证器 | 链类型 | 规则 |
|---|---|---|
| ethereumAddress | ethereum | ^0x[a-fA-F0-9]{40}$ |
| bitcoinAddress | bitcoin | legacy/segwit/bech32 |
| tronAddress | tron | ^T[a-zA-HJ-NP-Z1-9]{33}$ |
| anyAddress | any | 26-64 字符 |
自定义验证器
typescript
type ScanValidator = (
content: string,
parsed: ParsedQRContent
) => true | string // true = 通过, string = 错误消息 i18n key相关 Job 组件
ContactAddConfirmJob
扫码获取联系人后的确认添加界面。
┌─────────────────────────────────┐
│ 添加联系人 │
├─────────────────────────────────┤
│ 👤 张三 │
│ ──────────────────────────── │
│ ETH: 0x742d... │
│ BTC: bc1qar... │
│ ──────────────────────────── │
│ 备注: [输入框] │
├─────────────────────────────────┤
│ [取消] [保存] │
└─────────────────────────────────┘ContactShareJob
展示联系人名片二维码用于分享。
┌─────────────────────────────────┐
│ 分享名片 │
├─────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ QR Code │ │
│ │ (联系人) │ │
│ └─────────────┘ │
│ │
│ 张三 · ETH │
│ │
├─────────────────────────────────┤
│ [下载图片] [分享] │
└─────────────────────────────────┘测试覆盖
单元测试
qr-parser.test.ts: 38 个测试用例ScannerJob.test.ts: 31 个测试用例qr-scanner.test.ts: 10 个测试用例
Storybook Stories
ScannerJob.stories.tsx: ValidatorDemo, AddressFormats, UIPreviewContactJobs.stories.tsx: ContactProtocolDemo, ContactAddConfirmPreview, ContactSharePreview, EdgeCases
E2E 测试
scanner.spec.ts: 扫描页面基础测试contact-scanner.mock.spec.ts: 联系人协议集成测试
性能指标
| 指标 | 目标 | 说明 |
|---|---|---|
| 扫描帧率 | 6-7 FPS | 150ms 间隔 |
| 解码延迟 | < 100ms | Web Worker 后台处理 |
| 首次扫描 | < 500ms | 冷启动到首次成功 |