uniapp H5 扫码功能实战:从相册到拍照的完整解决方案

张开发
2026/4/10 12:31:57 15 分钟阅读

分享文章

uniapp H5 扫码功能实战:从相册到拍照的完整解决方案
1. 为什么H5扫码功能这么难搞最近在做一个uniapp的H5项目产品经理突然说要加个扫码功能。我当时第一反应就是直接调用uni.scanCode()不就完事了结果一查文档傻眼了——这个API在H5环境下根本不支持后来才知道H5环境下调用摄像头涉及到浏览器安全策略各家浏览器实现还不一样坑特别多。我试过好几个方案最开始用qrcode-decoder这个npm包识别率低得感人稍微模糊点的二维码就歇菜。后来换成qrcode.js效果好了不少但还是要处理一堆兼容性问题。实测下来这套方案在微信内置浏览器、Chrome、Safari上都能跑算是比较稳的选择。2. 手把手教你引入qrcode.js2.1 获取qrcode.js文件首先得搞到qrcode.js这个库。推荐直接从GitHub或者Gitee下载我用的这个版本// 新建一个空js文件把下面代码复制进去 (function(r){r.qrcodefunction(...){...}})(window);这个库体积小才几十KB不依赖其他库特别适合H5项目。下载后放到项目的static/js目录下或者任何你觉得合适的位置。2.2 在uniapp中引入在vue页面的script部分这样引入// #ifdef H5 import /static/js/qrcode.js // #endif注意要用条件编译因为只有H5环境需要这个库。小程序和App端可以直接用uni.scanCode()。3. 实现相册扫码功能3.1 选择图片的实现uniapp的uni.chooseImage()方法用起来特别方便uni.chooseImage({ count: 1, // 只选一张 sourceType: [album], // 只从相册选 success: (res) { this.scanQRCode(res.tempFilePaths[0]) } })这里有个坑要注意res.tempFiles和res.tempFilePaths的区别。在H5环境下tempFiles是File对象tempFilePaths是blob URL我们解码需要的是后者。3.2 图片转URL的兼容写法不同浏览器创建对象URL的方式不一样得写个兼容方法getObjectURL(file) { if (window.URL window.URL.createObjectURL) { return window.URL.createObjectURL(file) } else if (window.webkitURL) { return window.webkitURL.createObjectURL(file) } return null }这个方法会把File对象转成blob://开头的临时URLqrcode.js需要这个URL来解码。3.3 调用解码功能拿到图片URL后就可以解码了scanQRCode(imageUrl) { const qr new Qrcode() qr.decode(imageUrl) qr.callback (result) { if (result.includes(error)) { uni.showToast({ title: 识别失败, icon: none }) } else { this.handleScanResult(result) } } }实测发现如果图片不是二维码callback会返回error decoding QR Code这样的字符串所以用includes判断比较稳妥。4. 实现拍照扫码功能4.1 调用相机拍照和相册选择类似改个参数就行uni.chooseImage({ count: 1, sourceType: [camera], // 调起相机 success: (res) { this.scanQRCode(res.tempFilePaths[0]) } })4.2 解决H5调用摄像头的限制这里有个大坑H5调用摄像头必须满足以下条件之一网站是https协议本地开发用localhost或127.0.0.1iOS 15.4支持http调用摄像头如果是在微信内置浏览器还需要配置JS-SDK的白名单。建议开发阶段用localhost测试上线一定要用https。4.3 提升拍照识别率的技巧拍照时让用户保持手机稳定建议用户让二维码占满取景框的60%以上光线要充足避免反光可以加个重拍按钮识别失败时方便重试5. 常见问题解决方案5.1 识别率低的优化方案如果发现识别率不理想可以试试这些方法预处理图片// 先创建Image对象压缩图片 const img new Image() img.onload () { const canvas document.createElement(canvas) // 控制图片大小在1000px以内 const scale Math.min(1, 1000/Math.max(img.width, img.height)) canvas.width img.width * scale canvas.height img.height * scale const ctx canvas.getContext(2d) ctx.drawImage(img, 0, 0, canvas.width, canvas.height) this.scanQRCode(canvas.toDataURL(image/jpeg)) } img.src imageUrl尝试不同的解码参数// qrcode.js支持配置解码参数 const qr new Qrcode({ inversionAttempts: attemptBoth, // 尝试识别反色二维码 canOverwriteImage: true // 允许覆盖图片 })5.2 浏览器兼容性问题iOS Safari的问题需要用户主动触发事件比如点击按钮才能调起相机第一次使用会弹出权限询问要做好引导安卓微信浏览器可能需要配置JS-SDK部分机型有兼容性问题建议真机测试PC端浏览器Chrome和Edge支持最好Firefox需要额外配置IE就别想了直接提示用户换浏览器5.3 性能优化建议大图片处理// 限制图片大小 if (file.size 2 * 1024 * 1024) { uni.showToast({ title: 图片太大请选择2M以内的图片, icon: none }) return }内存释放// 用完后释放URL对象 const url this.getObjectURL(file) this.scanQRCode(url) // 解码完成后 window.URL.revokeObjectURL(url)避免重复实例化// 可以在created生命周期创建实例 created() { this.qrDecoder new Qrcode() }6. 完整代码示例6.1 页面模板部分template view classcontainer button clickchooseImage(album)从相册选择二维码/button button clickchooseImage(camera)拍照识别二维码/button view v-ifresult classresult识别结果{{result}}/view /view /template6.2 脚本部分script // #ifdef H5 import /static/js/qrcode.js // #endif export default { data() { return { result: } }, methods: { chooseImage(sourceType) { uni.chooseImage({ count: 1, sourceType: [sourceType], success: res { const url this.getObjectURL(res.tempFiles[0]) this.scanQRCode(url) } }) }, getObjectURL(file) { if (window.URL window.URL.createObjectURL) { return window.URL.createObjectURL(file) } else if (window.webkitURL) { return window.webkitURL.createObjectURL(file) } return null }, scanQRCode(imageUrl) { uni.showLoading({ title: 识别中..., mask: true }) // #ifdef H5 const qr new Qrcode() qr.decode(imageUrl) qr.callback (result) { uni.hideLoading() if (result.includes(error)) { uni.showToast({ title: 识别失败请重试, icon: none }) } else { this.result result // 释放URL对象 window.URL.revokeObjectURL(imageUrl) } } // #endif // #ifndef H5 uni.scanCode({ success: res this.result res.result }) // #endif } } } /script6.3 样式部分style .container { padding: 20px; } button { margin-bottom: 15px; } .result { margin-top: 20px; word-break: break-all; } /style7. 进阶方案使用html5-qrcode如果项目对扫码体验要求更高可以试试html5-qrcode这个库。它支持实时摄像头扫码体验接近原生。安装npm install html5-qrcode使用示例import { Html5Qrcode } from html5-qrcode // 创建实例 const html5QrCode new Html5Qrcode(reader) // 开始扫码 html5QrCode.start( { facingMode: environment }, { fps: 10, qrbox: 250 }, (decodedText) { console.log(decodedText) }, (error) { console.warn(error) } ) // 停止扫码 html5QrCode.stop()这个方案的优点是体验好缺点是包体积大约200KB而且必须https环境。适合对扫码体验要求高的项目。8. 项目实战经验分享在最近的一个电商项目中我们遇到了这样的需求H5页面需要支持扫描商品条形码。经过多次尝试最终方案是优先使用html5-qrcode实现实时扫码在不支持的环境下降级到qrcode.js相册选择针对条形码做了特别优化// 配置识别条形码 const qr new Qrcode({ readers: [qrcode, ean_reader] // 增加条形码识别器 })遇到的坑安卓微信浏览器下第一次调用相机可能会卡顿iOS 15需要处理新的权限弹窗低端手机上大图片解码会卡死优化后的效果主流机型识别率提升到90%平均识别时间控制在1秒内内存占用减少30%

更多文章