前端

Odoo 前端扫码么把框内识别做成真的:BarcodeDialog裁剪几何与 detector 回讲

很多业务系统说自己支持扫码,实际做法只是调起摄像头后不断扫整张画面Odoo Web Client 这套实现更讲究:`barcode_dialog.js` 负责能力入口,`barcode_video_scanner.js` 负责测器装配媒体流生命周期与流扫描,`crop_overlay.js` 则把可视裁剪区变成真实测边界本文重讲清框内识别与测回是么落地的

前端
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

支持扫码这件事,说起来很单,真做到稳定好用却不容易

果只是粗暴的实现,前端常会这样做:

  • 打摄像头;
  • 不停抓当前帧;
  • 识别到任何像条码的东西就返回

问题也随之来:

  • 权限报错么提示?
  • 不同浏览器的测能力么兼容?
  • 为么用户明明把码放在框里,却还扫到背景里的别的码?
  • 连续扫描场景下,么避免同帧疯触发结果?

Odoo 这套条码前端实现,恰好就是围绕这些真实问题展的

scanBarcode() 先把扫码过程包装成个 Promise,不是裸弹窗调用

barcode_dialog.js 里的 scanBarcode(env, facingMode) 很得看

它不是单 dialog.add(...) 后就完事,是:

  • 先创建个 Promise;
  • resolve / reject 存起来;
  • 再打 BarcodeDialog
  • onResult 时 resolve,在 onError 时 reject

这样设计的好处非常直接:

上业务拿到的是等待次扫码结果的步接口,不是打个含摄像头的 UI 组件

这让扫码能力更像个前端服务,不是某个页面私有的弹窗辑

二Dialog 只负责入口和失败兜底,不负责识别细

BarcodeDialog 自己做的事情其实很少:

  • 判断当前浏览器是否支持扫码能力;
  • 若支持,就载 BarcodeVideoScanner
  • 若不支持或运行失败,就显示错误界面

这里特别得注意的是:

onResult() 会先 close() 再把结果回传

这说明 Odoo 明确把拿到首个有效结果并关闭扫码界面当成默认用户路径,不是让扫描器继续在后台活

对于单次扫码业务,这是非常合理的默认

三测器不是写死种实现,是原生 API 优先,ZXing 回

BarcodeVideoScanneronWillStart() 里会先判断:

  • 浏览器是否有 BarcodeDetector
  • 没有的话,再 loadJS() 引入 ZXing 库

然后统构 this.detector

这步非常关键,因为它表达的不是支持或不支持扫码,是:

在浏览器能力不致的前提下,前端要尽量为同种业务接口提供兼容实现

对业务方来说,理想的状不是知道底用了哪个库,是无论原生还是 ZXing,上都继续只关心 onResult()

四媒体流生命周期处理得很干:拿不到权限就报错,卸载时定停流

onMounted() 里会调用:

browser.navigator.mediaDevices.getUserMedia({ video: { facingMode }, audio: false })

果失败,源码会按错误类型映射成更友好的提示:

  • NotFoundError -> 没找到设备
  • NotAllowedError -> 要授权

在组件卸载或常出时,又会执行 cleanStreamAndTimeout()

  • 清理 setTimeout
  • 停止有媒体 track

这比很多临时扫码实现强太多

不少项目里,扫码弹窗关掉后摄像头还亮,根源就是没有把媒体流生命周期认真收口

五真正的关键不只是识别,是识别边界

很多人第次看 BarcodeVideoScanner 时,会把注意力都放在 detector.detect(...)

但这段实现真正厉害的地方,在于它没有默认整张视频画面都算有效区域

BarcodeVideoScanner 会过 CropOverlay 拿到 overlayInfo,然后在 detectCode() 里做两类边界控制:

1)ZXing 路径:直接把裁剪区传给 detector

onResize() 里若测到当前是 ZXing 回实现,就会:

this.detector.setCropArea(this.adaptValuesWithRatio(this.overlayInfo, true))

也就是说,裁剪区会在测器就被当成扫描边界

2)原生 BarcodeDetector 路径:识别后再按 bounding box 过滤

若用的是原生 API,Odoo 会对返回结果个查 boundingBox 是否落在 overlay 范围内,超出就跳过

这个分支设计非常成熟,因为它承认不同测后端的能力不完全样:

  • 有的能在测前裁剪;
  • 有的只能在测后过滤

但上要得到致行为:只有框内的码才算有效

CropOverlay 不是纯视觉蒙,是可以持久化的几何约束

crop_overlay.js 这部分尤其得前端同学细看

它会:

  • setupCropRect() 里计算默认裁剪区;
  • localStorage 恢复用户上次调整的位置;
  • 用 CSS 变量更新可视框位置;
  • 再过 executeOnResizeCallback() 把几何信息回传给扫描器

这说明 CropOverlay 的地位不是画个半明框,是:

维护份用户可调整浏览器可记忆扫描器可消费的裁剪几何状

它甚至还特别处理了 iOS 分支样式,这说明官方是把移动端扫码体验当真来做的,不是桌面端顺手兼容下

七连续扫描并不是无限触发,delayBetweenScan 用来流下次识别

barcodeDetected(barcode) 里有段很重要:

  • 若配置了 delayBetweenScan,先把 scanPaused = true
  • 延迟结束后再恢复测循环

这在连续扫码或批量收货类业务里尤其重要

否则个码只要还停留在镜头中,同帧或连续帧就可能反复触发,前端和业务辑都会被刷爆

以 Odoo 这里不是识别到就直回调,是给出了可配置流边界

八二容易踩的坑

误区 1:把扫码界面和扫码结果死在个页面组件里

更好的方式是像 scanBarcode() 这样先抽象成 Promise 能力

误区 2:整张画面都算有效区域

这会让背景里的条码包装箱上的其他码起干扰结果

误区 3:只做 Chrome happy path,不虑回实现

现实设备和浏览器环境远比本地测试复

误区 4:关闭弹窗后不清理 stream / timeout

摄像头残留占用就是这样来的

九结论

Odoo 的条码扫描前端之以靠谱,不是因为它能打摄像头,是因为它把扫码拆成了几职责清晰的前端协议:

  • scanBarcode() 暴露 Promise 风格能力接口;
  • BarcodeDialog 管入口与失败兜底;
  • BarcodeVideoScanner 管测器装配媒体流和扫描循环;
  • CropOverlay 管用户可视可调的真实裁剪边界

以真正得学的不是何调用相机,是:

何让摄像头测器裁剪区和业务结果在同条链路里协同工作,不是互相打架

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。