引言
还记得 Chrome 浏览器曾经内置的二维码生成器功能吗?那个简洁优雅的界面,带有标志性 Chrome 恐龙图标的二维码,曾经是我们分享网页链接的得力助手。虽然 Google 在某个版本中移除了这个功能,但今天我要介绍一个令人兴奋的开源项目------它不仅重新带回了这个功能,更是在技术实现上达到了像素级的完美复刻。
这个名为"Add QR Code Generator Icon Back To Address Bar"的 Chrome 扩展,使用 WebAssembly 技术完美复现了 Chromium 的原生二维码生成算法,让我们能够重新享受这个经典功能。
项目概览
核心特性
- Chromium 风格二维码:与 Chrome 原生实现完全相同的视觉外观
- Chrome 恐龙中心图案:经典的 Chrome 恐龙图标作为二维码中心图案
- 即时生成:自动为当前页面 URL 生成二维码
- 自定义文本支持:为任何文本或 URL 创建二维码
- 一键复制与下载:支持高质量 PNG 图像的复制和下载
技术栈
这个项目采用了现代化的技术栈:
json
{
"框架": "WXT - 现代 Web 扩展框架",
"前端": "React 19 + TypeScript",
"样式": "Tailwind CSS 4.0 + Radix UI 组件",
"二维码生成": "WebAssembly 模块",
"构建系统": "Vite + Rollup",
"包管理器": "pnpm"
}
技术深度解析
1. WebAssembly 驱动的二维码生成
项目的核心亮点是使用 WebAssembly 模块来复制 Chrome 的原生二维码生成算法。让我们看看加载器的实现:
typescript
// src/lib/wasm-loader.ts
export interface WasmQRGenerator {
QuietZone: {
WillBeAddedByClient: number
Included: number
}
CenterImage: {
Dino: number
None: number
}
ModuleStyle: {
Circles: number // 圆形模块
Squares: number // 方形模块
}
LocatorStyle: {
Rounded: number // 圆角定位符
Square: number // 方形定位符
}
generate_qr_code_with_options: (
text: string,
moduleStyle: number,
locatorStyle: number,
centerImage: number,
quietZone: number
) => QRCodeResult
}
2. 精确的常量定义
为了确保与 Chromium 完全一致,项目定义了精确的常量:
typescript
// src/hooks/use-qrcode.ts
const MODULE_SIZE_PIXELS = 10 // 每个模块 10 像素
const DINO_TILE_SIZE_PIXELS = 4 // 恐龙图块 4 像素
const LOCATOR_SIZE_MODULES = 7 // 定位符 7x7 模块
const QUIET_ZONE_SIZE_PIXELS = MODULE_SIZE_PIXELS * 4 // 40 像素边距
const MAX_INPUT_LENGTH = 2000 // 最大输入长度
3. Chrome 恐龙图案的像素级复制
最令人印象深刻的是,项目直接从 Chromium 源码复制了恐龙图案的像素数据:
typescript
// 恐龙尺寸定义
const kDinoWidth = 20
const kDinoHeight = 22
const kDinoHeadHeight = 8
const kDinoBodyHeight = 14
// 恐龙头部像素数据(面向右侧)- 直接从 Chromium 复制
const kDinoHeadRight = [
0b00000000, 0b00011111, 0b11100000,
0b00000000, 0b00111111, 0b11110000,
0b00000000, 0b00110111, 0b11110000,
// ... 更多像素数据
]
// 恐龙身体像素数据
const kDinoBody = [
0b10000000, 0b01111100, 0b00000000,
0b10000001, 0b11111100, 0b00000000,
// ... 更多像素数据
]
4. 自定义 Hook:useQRCode
项目使用 React Hook 模式封装二维码生成逻辑:
typescript
export const useQRCode = (): [QRCodeState, QRCodeActions] => {
const [state, setState] = useState<QRCodeState>({
isLoading: false,
error: null,
qrData: null,
qrSize: 0,
originalSize: 0
})
const generateQRCode = useCallback(async (inputText: string) => {
// 验证输入长度
if (inputText.length > MAX_INPUT_LENGTH) {
setState(prev => ({
...prev,
error: `Input is too long. Please shorten the text to ${MAX_INPUT_LENGTH} characters or less.`
}))
return
}
// 调用 WASM 模块生成二维码
const result = wasmModuleRef.current.generate_qr_code_with_options(
inputText,
wasmModuleRef.current.ModuleStyle.Circles, // 圆形数据模块
wasmModuleRef.current.LocatorStyle.Rounded, // 圆角定位符
wasmModuleRef.current.CenterImage.Dino, // 恐龙中心图案
wasmModuleRef.current.QuietZone.WillBeAddedByClient
)
}, [])
return [state, { generateQRCode, copyQRCode, downloadQRCode, clearError }]
}
5. 画布渲染:精确复制 Chromium 的绘制算法
圆角定位符绘制
typescript
const drawLocators = (
ctx: CanvasRenderingContext2D,
dataSize: { width: number; height: number },
paintForeground: { color: string },
paintBackground: { color: string },
margin: number,
modulePixelSize: number
) => {
const chromiumModuleSize = 10
const scaleFactor = modulePixelSize / chromiumModuleSize
const radius = 10 * scaleFactor
const drawOneLocator = (leftXModules: number, topYModules: number) => {
// 最外层正方形,7x7 模块
let leftXPixels = leftXModules * modulePixelSize
let topYPixels = topYModules * modulePixelSize
let dimPixels = modulePixelSize * LOCATOR_SIZE_MODULES
drawRoundRect(ctx, margin + leftXPixels, margin + topYPixels,
dimPixels, dimPixels, radius, paintForeground.color)
// 中间正方形,5x5 模块
leftXPixels += modulePixelSize
topYPixels += modulePixelSize
dimPixels -= 2 * modulePixelSize
drawRoundRect(ctx, margin + leftXPixels, margin + topYPixels,
dimPixels, dimPixels, radius, paintBackground.color)
// 内层正方形,3x3 模块
leftXPixels += modulePixelSize
topYPixels += modulePixelSize
dimPixels -= 2 * modulePixelSize
drawRoundRect(ctx, margin + leftXPixels, margin + topYPixels,
dimPixels, dimPixels, radius, paintForeground.color)
}
// 绘制三个定位符:左上、右上、左下
drawOneLocator(0, 0)
drawOneLocator(dataSize.width - LOCATOR_SIZE_MODULES, 0)
drawOneLocator(0, dataSize.height - LOCATOR_SIZE_MODULES)
}
恐龙图案的像素级绘制
typescript
const drawDinoPixelByPixel = (
ctx: CanvasRenderingContext2D,
destX: number,
destY: number,
destWidth: number,
destHeight: number
) => {
const scaleX = destWidth / kDinoWidth
const scaleY = destHeight / kDinoHeight
ctx.fillStyle = moduleColor
const drawPixelData = (srcArray: number[], srcNumRows: number, startRow: number) => {
for (let row = 0; row < srcNumRows; row++) {
for (let bit = 0; bit < kDinoWidth; bit++) {
const byteIndex = row * kDinoWidthBytes + Math.floor(bit / 8)
const bitIndex = 7 - (bit % 8)
if (srcArray[byteIndex] & (1 << bitIndex)) {
const pixelX = destX + bit * scaleX
const pixelY = destY + (startRow + row) * scaleY
ctx.fillRect(pixelX, pixelY, scaleX, scaleY)
}
}
}
}
// 绘制恐龙头部和身体
drawPixelData(kDinoHeadRight, kDinoHeadHeight, 0)
drawPixelData(kDinoBody, kDinoBodyHeight, kDinoHeadHeight)
}
6. 用户界面:复制 Chrome 的设计语言
主界面组件完美复制了 Chrome 的设计风格:
tsx
// src/popup/App.tsx
function App() {
const [qrState, qrActions] = useQRCode()
const [inputValue, setInputValue] = useState('')
// 自动获取当前标签页 URL
useEffect(() => {
if (typeof window !== 'undefined' && window.chrome?.tabs) {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]?.url) {
setInputValue(tabs[0].url)
}
})
}
}, [])
// 智能文件名生成
const handleDownload = () => {
if (qrState.qrData) {
let filename = 'qrcode_chrome.png'
try {
const url = new URL(inputValue)
if (url.hostname && !/^\d{1,3}(\.\d{1,3}){3}$/.test(url.hostname)) {
const safeHostname = url.hostname.replace(/[^a-zA-Z0-9.-]/g, '_')
filename = `qrcode_${safeHostname}.png`
}
} catch (e) {
// 使用默认文件名
}
qrActions.downloadQRCode(filename)
}
}
return (
<div className="chromium-bubble flex w-80 flex-col p-0 leading-5">
{/* 标题栏 - 匹配 Chromium 样式 */}
<div className="chromium-title-bar flex min-h-10 items-center justify-between">
<h2 className="chromium-title text-sm font-medium">Scan QR Code</h2>
<button onClick={() => window.close()}>×</button>
</div>
{/* 二维码显示区域 - 精确的 240px 尺寸 */}
<div className="chromium-qr-container relative flex items-center justify-center">
<QRCodeCanvas qrData={qrData} qrSize={qrSize} originalSize={originalSize} />
</div>
{/* 输入框 - 匹配 Chromium 的可访问性标准 */}
<Input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
aria-label="URL or text to encode"
placeholder="Enter URL or text"
maxLength={2000}
/>
{/* 按钮容器 */}
<div className="chromium-button-container flex items-center gap-2">
<Button onClick={handleCopy}>Copy</Button>
<Button onClick={handleDownload}>Download</Button>
</div>
</div>
)
}
工作原理图解
scss
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 用户输入URL │───▶│ WASM二维码生成 │───▶│ Canvas渲染 │
│ 或文本内容 │ │ │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ 二维码数据 │ │ 绘制定位符 │
│ (Uint8Array) │ │ (圆角方形) │
└──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ 绘制数据模块 │
│ (圆形) │
└─────────────────┘
│
▼
┌─────────────────┐
│ 绘制恐龙图案 │
│ (像素级精确) │
└─────────────────┘
安装和使用
从 Chrome 网上应用店安装
bash
# 直接访问Chrome网上应用店
https://chromewebstore.google.com/detail/add-qr-code-generator-ico/kacblhilkacgfnkjfodalohcnllcgmjd
开发环境搭建
bash
# 克隆仓库
git clone https://github.com/chromium-style-qrcode/add-qrcode-generator-icon-back-to-address-bar.git
cd add-qrcode-generator-icon-back-to-address-bar
# 安装依赖(需要Node.js ≥ 24.3.0)
pnpm install
# 启动开发服务器
pnpm dev
# 生产环境构建
pnpm build
# 创建分发压缩包
pnpm zip
加载到 Chrome
- 打开 Chrome 并导航到
chrome://extensions/
- 在右上角启用"开发者模式"
- 点击"加载已解压的扩展程序"
- 选择
.output/chrome-mv3
文件夹
技术创新点
1. WebAssembly 的高性能应用
通过 WebAssembly 模块,项目实现了:
- 高性能:接近原生的二维码生成速度
- 精确性:与 Chromium 算法完全一致的结果
- 跨平台:在所有支持 WebAssembly 的浏览器中运行
2. 像素级完美的恐龙图案
项目直接从 Chromium 源码提取恐龙图案的二进制数据,确保了:
- 100% 相同的视觉效果
- 精确的像素对齐
- 正确的比例缩放
3. 现代 React 架构
使用 React 19 和 TypeScript 构建,提供了:
- 类型安全:完整的 TypeScript 类型定义
- 组件化设计:可复用的 UI 组件
- 状态管理:清晰的 Hook 模式
4. Chromium 风格的用户界面
通过 Tailwind CSS 精心重现了 Chrome 的设计语言:
- 精确的尺寸:240px 的二维码显示区域
- 原生的颜色方案:与 Chrome 一致的颜色
- 可访问性支持:完整的 ARIA 标签和键盘导航
性能优化策略
1. 防抖输入处理
typescript
useEffect(() => {
const timer = setTimeout(() => {
qrActions.generateQRCode(inputValue)
}, 300) // 300ms 防抖
return () => clearTimeout(timer)
}, [inputValue, qrActions])
2. Canvas 优化
typescript
// 启用高质量缩放
ctx.imageSmoothingEnabled = false
ctx.imageSmoothingQuality = 'high'
// 使用离屏 Canvas 进行复制和下载
const clipboardCanvas = document.createElement('canvas')
3. 内存管理
typescript
// 及时清理 WASM 模块资源
const wasmModuleRef = useRef<WasmQRGenerator>(null)
useEffect(() => {
return () => {
// 组件卸载时清理资源
if (wasmModuleRef.current) {
// 清理逻辑
}
}
}, [])
项目意义与价值
这个项目不仅仅是一个简单的 Chrome 扩展,它代表了:
- 开源精神的体现:通过开源让优秀功能得以延续
- 技术精益求精:对细节的极致追求和像素级的完美重现
- 现代化技术栈的应用:WebAssembly、React 19、TypeScript 等前沿技术的综合运用
- 用户体验的重视:完整的可访问性支持和流畅的交互体验
总结
"Add QR Code Generator Icon Back To Address Bar"项目是一个技术与情怀并重的优秀作品。它不仅成功地重现了 Chrome 经典的二维码生成功能,更在技术实现上达到了令人赞叹的精确度。
通过 WebAssembly 技术实现的高性能二维码生成,像素级完美的恐龙图案复制,以及现代化的 React 架构,这个项目为我们展示了如何用最新的技术栈重现经典功能的完美范例。
对于开发者而言,这个项目提供了丰富的学习价值:从 WebAssembly 的实际应用,到 Canvas 的高级渲染技巧,从 React Hook 的优雅设计,到 Chrome 扩展的开发最佳实践。
如果你也怀念 Chrome 的经典二维码功能,或者对前端技术的深度应用感兴趣,不妨尝试这个扩展,或者深入研究其源码。相信你会像我一样,被其技术实现的精妙和对细节的极致追求所震撼。
项目地址 :github.com/chromium-st...
Chrome 网上应用店 :chromewebstore.google.com/detail/add-...
许可证:MIT License