浏览器原生不支持二维码识别,getContext("2d") 仅提供绘图能力,无法解析二维码;需引入 @zxing/library 等成熟解码库,配合 HTMLImageElement 或 HTMLCanvasElement 输入,并确保图片加载完成、处理跨域及编码问题。

为什么直接用 HTMLCanvasElement.getContext("2d") 读取二维码图片会失败
浏览器原生不支持二维码识别,canvas.drawImage() 后调用 ctx.getImageData() 只能得到像素数据,但没解析逻辑。强行自己写解码器不现实——QR码有掩码、格式信息、纠错码(Reed-Solomon)、版本结构等多层规则。
真正可行的路径只有一条:引入成熟 JS 解码库,且必须支持浏览器环境、能处理本地图片或 <img> / <canvas> 输入。
- 优先选
@zxing/library(官方维护,TypeScript 友好,支持 ESM) - 避免用已停止更新的
jsqrcode或依赖 Flash 的老方案 - 注意:它不能直接解析 base64 字符串,必须转成
HTMLImageElement或送入HTMLCanvasElement
如何用 @zxing/library 解析用户上传的二维码图片
核心是把 <input type="file"> 选中的文件转为图像对象,再喂给解码器。关键在时机控制:必须等图片 load 完再送入解码流程,否则 decodeFromImage() 会报 InvalidStateError: The image is not loaded yet。
const reader = new FileReader();
reader.onload = e => {
const img = new Image();
img.onload = () => {
codeReader.decodeFromImage(img)
.then(result => console.log(result.getText()))
.catch(err => console.error('No QR code found', err));
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
-
file来自input.files[0],需校验是否为图片(file.type.startsWith("image/")) - 不要用
URL.createObjectURL(file)后直接传地址给decodeFromImage()—— 它不接受 URL 字符串,只接受HTMLImageElement实例 - 移动端 iOS Safari 对大图解码易超时,建议限制上传尺寸(如宽高 ≤ 1200px)并加 loading 状态
为什么用 VideoInputDevice 实时扫码时总提示 “No video input devices found”
这是权限和上下文问题,不是代码缺陷。Chrome/Firefox 要求:① 页面必须通过 HTTPS(localhost 除外);② 用户必须主动触发(比如点击按钮),不能 onload 自动调用 navigator.mediaDevices.getUserMedia();③ 需显式请求 { video: true } 权限。
立即学习“前端免费学习笔记(深入)”;
- 错误写法:
onload里直接调用getUserMedia→ 浏览器静默拒绝 - 正确写法:绑定按钮
onclick,点击后调用,并用try/catch捕获NotAllowedError或NotFoundError - 拿到流后,必须赋给
<video>元素并调用play(),再用codeReader.decodeFromVideoDevice()指定设备 ID(否则可能选到无效设备) - 频繁调用
decodeFromVideoDevice()会吃 CPU,建议设 500ms 以上间隔,或用decodeOnce()手动控制
解析结果乱码?检查 result.getCharset() 和编码声明
二维码内容本身无编码标记,@zxing/library 默认按 UTF-8 解,但若原内容是 GBK 编码(比如某些国产扫码枪生成的中文),就会显示方块或问号。此时不能改库源码,而应靠前端预处理。
- 先用
result.getRawBytes()获取原始字节数组(Uint8Array) - 用
TextDecoder显式指定编码:new TextDecoder("gbk").decode(rawBytes) - 注意:浏览器原生
TextDecoder不支持 GBK,需引入 polyfill(如encoding-indexes+text-encoding) - 更稳妥的做法是服务端统一用 UTF-8 生成二维码,避免前端编码猜测
img.crossOrigin = "anonymous" 必须设置,否则 canvas 读取会触发 SecurityError。这个细节没有错误提示,只会静默失败。

















