根本原因是未适配window.devicePixelRatio,需三步解决:设canvas宽高为逻辑尺寸×dpr、CSS设回逻辑尺寸、ctx.scale(dpr,dpr)后再绘制。
根本原因是没适配 window.devicePixelRatio。浏览器默认用 1:1 绘制 canvas,但 Retina 或安卓高分屏物理像素密度是 2x/3x,导致 canvas 内容被插值拉伸,边缘模糊。
必须手动做三件事:
canvas.width 和 canvas.height 为逻辑尺寸 × window.devicePixelRatio
style="width: 200px; height: 200px;"
ctx.scale(dpr, dpr) 后再绘制,否则内容会变小漏掉任意一步,扫码成功率都会明显下降。别信 DevTools 模拟器——真机扫才作数。
每次 QRCode.toCanvas() 都清空并重绘整个 canvas,高频触发(比如 input 实时监听)会让低端 Android 直接卡死。
立即学习“前端免费学习笔记(深入)”;
关键对策是控制节奏和复用资源:
setTimeout + 标志位,确保两次调用间隔 ≥ 16msdocument.createElement('canvas'),缓存一个 <canvas></canvas> 元素反复用debounce,或只监听 change 和 blur
display 切换很多人以为“重绘快就行”,其实瓶颈常在 DOM 创建和垃圾回收上。
二维码本身生成正确,但被父容器裁剪、透明覆盖或 transform 形变,手机摄像头就无法识别——这种问题不抛 JS 错误,排查最耗时间。
必须逐项检查:
overflow: hidden 或 clip-path,哪怕只压住 1px 边框也会失败opacity、filter: blur()、transform: scale(0.99) 等任何影响像素精度的样式contenteditable 区域,iOS Safari 会自动聚焦弹键盘,遮挡扫码视图QRCode.clear()(如果库支持),否则旧数据残留污染新码样式干扰是真机测试前最容易忽略的一环,建议建个最小化测试页单独验证。
SVG 不受设备像素比影响,天生清晰,适合打印、缩放和无障碍场景(自带 <title> 和 <desc>)。但要注意兼容性断层:
foreignObject,中文会乱码QRCode.toString('svg') 返回字符串,得用 innerHTML 插入,注意 XSS 过滤canvas.drawImage() 场景(比如叠加到名片图上)选 Canvas 还是 SVG,不是看哪个“高级”,而是看你的最低兼容目标和是否需要后续图像操作。