更新记录

1.6.0(2026-10-28)

功能

  • startScan 新增 autoClose 参数(默认 true):传 false 时识别成功不再自动关闭扫码页,而是停掉相机、把画面定格在最后一帧,等开发者自行关闭
  • 新增 closeScan() API:关闭当前打开着的扫码页,配合 autoClose: false 在 success 回调里调用;页面不存在时静默返回,不会二次触发任何回调

实现说明

  • 手动关闭(autoClose: false):三端都在发成功回调之前「定格 + 停相机」——Android 用 TextureView.getBitmap() 抓一帧盖成全屏 ImageView 再 stopRepeating();iOS 用 AVCaptureVideoPreviewLayer.render(in:) 截图后 stopRunning();鸿蒙复用点选层的 createPixelMapFromSurface 抓帧落盘 + customScan.stop()。关闭入口统一为 closeScan():Android 经 ScanResultBus.setCloseHandler 回调 Activity finish(),iOS 用 strongController.closeManually() dismiss,鸿蒙经 SCAN_DIALOG.closeRequest() → closeDialog();三端都只关页面、不再触发任何回调
  • 定格态仍保留「X / 关闭」按钮:JS 忘记调 closeScan() 时用户还能自己退出去(鸿蒙端同时把返回键也接到同一入口)

1.5.0(2026-10-25)

修复

  • 修正 Android / iOS 返回的 format 不实:Android 的 ZXing 兜底命中后固定上报 QR_CODE,iOS 的系统扫码(AVCaptureMetadataOutput)通道也固定上报 QR_CODE,扫条形码会被当成二维码返回,且同一张码走不同通道结果还会不一致。现按真实码制映射(与鸿蒙、iOS Vision 通道共用同一张映射表)
  • 修正 Android ZXing 兜底只认二维码:兜底解码器的 POSSIBLE_FORMATS 原先硬写 QR_CODE 且用的是 QRCodeReader,formats 配成条形码时 ML Kit 一失手,兜底完全帮不上忙(等于没有兜底)。现改用 MultiFormatReader,尝试的码制由 formats 配置推导
  • Android / iOS 多码点选返回的 format 不再固定 QR_CODE:点选层此前只携带内容与坐标,用户点中的是条形码也会被报成二维码。现点选层带上每个码的真实码制,与鸿蒙端 PickItem 行为一致
  • 鸿蒙多码点选层「定格底图」卡顿:裁边原来把整幅像素又读了一遍(720×1280 RGBA ≈3.7MB)并逐行逐列全量扫描,点选层弹出时会在 UI 线程顿一下。现只读中间两条各 16 像素宽的带子判定四条黑边,读取量降到约 128KB
  • iOS 扫码页发热 / 掉帧:帧回调里每帧都 CIContext() 新建一个上下文(内含 GPU 上下文与着色器缓存),代价比图像转换本身还大。现改为全局复用同一个 CIContext
  • 修复 Android / iOS 多码点选层「箭头不居中」:标记原先用 TextView(iOS 是 UIButton 标题)显示「→」字符,圆圈位置本身是对的,但字符受 includeFontPadding / 字体上下留白不对称、以及字形左右 bearing 影响,视觉上总偏向一边。现两端都改为自绘箭头(Android ArrowMarkerView、iOS PickMarkerView):绿圆 + 白描边 + 横杆与箭头,全部按半径等比、以圆心为几何中心,跨机型一致;顺带把箭头放大(横杆 0.92r → 1.16r,描边 2 → 2.5)

实现说明

  • 实时多码定格底图的裁边取样:黑边一定贯穿整幅,所以不再读整帧——只读中间 16 列(判上下黑边)与中间 16 行(判左右黑边)两条带子,某行/列采样点 ≥92% 偏黑即判为补黑(原为 98%;样本从整行降到 16 个后 98% 等价于「全黑」,一点噪点就会把黑边判成内容);四条边都从边缘往里扫、命中即停;裁出的框小于原图 40% 判定为暗场景误裁,改用整幅;readPixels 异常时降级为整幅。读取量从 720×1280 RGBA ≈3.7MB 降到约 128KB
  • Android 兜底解码:ML Kit 漏检时用 ZXing MultiFormatReader 再试(全图 / 反色 / 中心裁切 / 对比度拉伸),尝试的码制由 formats 配置推导;跑在独立的 mlkit-scan-fallback 线程上——相机请求与 ImageReader 回调都排在相机 Looper 上,一次兜底几十毫秒就会让预览掉帧

1.4.0(2026-10-16)

功能

  • 新增鸿蒙(App-Harmony)支持,package.json platforms 加入 app-harmony
  • 鸿蒙端同样是自研全屏页:黑色背景、无扫码框、绿色扫描线、右上角相册、多码定格点选、手电筒、双指缩放,交互与 Android / iOS 对齐
  • 鸿蒙扫描线与 Android / iOS 逐参对齐:径向渐变光带(rx = 0.72w、ry = 0.22h,只露椭圆顶部 0.25ry 那条弧,颜色 #6400FF6A → #5A00FF6A → #2400FF6E → 透明,2400ms 线性无限循环),不再是固定长度的实心细线
  • 14 种识别格式在鸿蒙端全覆盖(含 AZTEC,ScanKit 的 AZTEC_CODE 是真实存在的枚举值)
  • 鸿蒙端 decodeImage 支持相册选图返回的 content:// URI(自动读入沙箱后解码)

修复

  • 鸿蒙实时多码点选不再透出相机预览:改为「定格底图 + 位置标记」。此前底图是半透明遮罩,多码点选时背景里的相机画面还在动,看着像定格没生效;现在点选层背景一律不透明黑底
  • 修复鸿蒙相册多码标记位置偏移:FrozenPickLayer 算图片显示矩形时宽高比取错了倒数,导致横图在竖屏容器里 ratioH 恒为 1、标记纵向被拉满整屏
  • 修复鸿蒙多码点选层「取消」按钮点了没反应(漏了 onClick)
  • 鸿蒙返回键行为对齐 Android / iOS:多码点选层打开时只关点选层、退回实时扫码,再按一次才关闭整个扫码页
  • 修复鸿蒙扫码页被「返回键直接关闭」时 UTS 侧 Promise 一直挂着:补 onDidDisappear → cancel 兜底(幂等)
  • 修复鸿蒙相机预览不满屏(上下各留约 22% 屏高的黑边):XComponent 的 surface 比例原先只按「9/16、3/4、1 里最接近屏幕比例者」猜,选出的 9/16 恰好是本机相机给不出的档位,ScanKit 只能把预览等比缩小塞进 surface。现在先用 CameraKit 查相机真实支持的预览比例,且当屏幕比所有档位都窄时直接取 1:1;实测黑边完全消失
  • 修复鸿蒙实时多码点选层底图与实时预览对不上(底图没铺满、标记位置也对不准):定格底图原先按「整幅 surface 缓冲裁成容器比例」裁,但缓冲是相机裸帧——里面既有补黑的无效区,也有屏幕外看不到的区域,裁完既带黑边、取景范围也和屏幕不一致。现改为按实时预览的同一套几何反推「屏幕可见范围」:先找出缓冲里的有效内容区,算它在 surface 上等比 contain 的显示矩形,再按屏幕可见范围在该矩形里的占比裁图。裁完底图与实时预览逐像素对齐(实测最佳位移 dx=0、dy=0,相关度 0.98)
  • 丢弃完全落在屏幕外的识别结果:XComponent 为了保住预览比例会比屏幕大(溢出部分由窗口裁掉、用户看不到),而 ScanKit 解码的是整幅 surface,会识别出屏幕外的码,出现「屏幕上没看见码却被扫出来」。现在按「矩形与屏幕可见区是否还有交集」过滤,只露一半的码仍算可见;过滤放在「本轮已见集合」之前,避免该码被记成已见、挪进屏幕后反而被去重跳过

实现说明

  • 预览 + 识别:ScanKit customScan
    • XComponent 的 surface 交给 customScan.start(viewControl, cb),相机预览与多码识别都由它完成,不再是「自建相机管线」
    • customScan.start 一次只回调一轮,单码时用 350ms 确认窗口持续 rescan 收帧,避免「同屏两个码只识别出一个」
    • XComponent 按相机预览宽高比居中布局,与官方 SDK 做法一致;这样 scanCodeRect 的坐标系与预览画面严格 1:1,标记不会整体偏移。比例优先用 CameraKit(getSupportedCameras + getSupportedOutputCapability().previewProfiles,均不需要 CAMERA 权限)查到的真实档位、取最接近屏幕比例者;屏幕比所有档位都窄时(绝大多数手机)直接取最大档位 1:1 —— 此时「surface 比例 ≥ 真实预览比例」恒成立,ScanKit 等比 contain 后必然铺满,且可见画面与「surface 恰好等于真实比例」时完全一致。相机上报的能力未必等于它真正渲染的预览比例(本机模拟器只上报 16:9、实际渲染 1:1),故需要这条兜底
    • 手电筒 openFlashLight / closeFlashLight,缩放 setZoom / getZoom
  • 实时多码的定格底图:image.createPixelMapFromSurface 抓 XComponent 的 surface → 顺时针旋转 90° 转正 → 找出「相机有效内容区」(四周 98% 纯黑的行/列视为补黑;裁出的框小于原图 40% 判定为暗场景误裁,改用整幅)→ 按「内容区在 surface 上等比 contain 的显示矩形」换算出屏幕可见范围的占比、据此裁图 → JPEG 写入沙箱 tempDir。这样裁出的底图正好等于屏幕上真正看到的那块,宽高比也正好等于容器,点选层用 ImageFit.Contain 即可铺满,标记直接沿用容器归一化坐标,与相册多码共用同一套落位逻辑
  • 相册:@kit.CoreFileKit 的 PhotoViewPicker
  • 相册识别失败只 toast 并继续扫码,decodeImage API 不弹 toast,错误只走 fail
  • 全屏 dialog:promptAction.openCustomDialog + ComponentContent
    • onWillDismiss 拦截返回键 / 侧滑,多码点选层打开时只关点选层
    • onDidDisappear 兜底补 cancel,避免交互式关闭时 UTS 侧 Promise 挂死

注意

  • 鸿蒙端 decodeImage 的多码数量受 ScanKit 限制:单帧最多返回 4 个,超过的部分不会返回(Android / iOS 无此限制)
查看更多

平台兼容性

uni-app(5.0)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
√ 1.6.0 √ 1.6.0 - - √ 1.6.0 √ 1.6.0 √ 1.6.0 √ 1.6.0 √ 1.6.0
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.0)

Chrome Safari Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本 微信小程序
- - √ 1.6.0 √ 1.6.0 √ 1.6.0 -

其他

多语言 暗黑模式 宽屏模式 蒸汽模式
× × × √

mlkit-scancode

App 端全屏扫码插件。Android 使用 Camera2 取帧,再用 ML Kit 识别,识别不到时用 ZXing 补一次。iOS 使用 AVFoundation,同时走 Vision 和系统扫码接口。鸿蒙使用 ScanKit 的 customScan,预览与多码识别一并完成。

页面没有扫码框。右上角带「相册」按钮,可从相册选图识别。同屏多个二维码时先点选,再返回选中的内容。

平台支持

平台 实现方式 最低版本
App-Android Camera2 + ML Kit + ZXing Android 5.0(API 21)
App-iOS AVFoundation + Vision + 系统扫码 iOS 12.0
App-Harmony ScanKit customScan(预览 + 多码识别) HarmonyOS 5.0(API 12)

三端都是自研全屏页,交互一致:无扫码框、右上角相册、多码点选、手电筒、双指缩放。

鸿蒙端说明:预览与识别都交给 ScanKit 的 customScan —— 把 XComponent 的 surface 交给 customScan.start(viewControl, cb),相机预览与多码识别由它一并完成,不再是「自建相机管线」。start 一次只回调一轮,拿到结果后必须 rescan 才能继续收帧;单码时用 350ms 的确认窗口持续收帧,避免「同屏两个码只识别出一个」。实时多码点选时用 image.createPixelMapFromSurface 抓一帧定格底图当背景。

本插件只支持 App。Web、小程序请继续使用 uni.scanCode。

不要在插件里再引入 CameraX。基座的 Camera、Barcode 模块已经带有一套 CameraX,版本更高时会在打开扫码页时崩溃。

安装

插件在项目内,从 @/uni_modules/mlkit-scancode 导入。

修改原生代码后,必须重新打自定义调试基座或云打包。标准基座不会加载这里的原生实现。

首次扫码会申请相机权限。Android 需要 CAMERA,iOS 需要在 manifest.json 中配置 NSCameraUsageDescription,鸿蒙的权限已声明在插件的 utssdk/app-harmony/module.json5 里,无需额外配置。

鸿蒙端首次运行前,需要在 manifest.json 里补上 app-harmony 配置段(工程目前只有 app-plus),并在 HBuilderX 的 DCloud 控制台配置好 AppHarmonyOS 的 appid 与签名,然后真机运行到 HarmonyOS。

使用

扫码(打开扫码页)

import { startScan } from '@/uni_modules/mlkit-scancode'

startScan({
  tip: '将二维码对准屏幕,即可自动扫描',
  formats: ['QR_CODE'], // 常见取值见「支持的识别格式」
  vibrate: true,
  success: (res) => {
    console.log('扫码结果:', res.result, res.format)
  },
  fail: (err) => {
    console.error('扫码失败:', err.errCode, err.errMsg)
  },
  complete: (res) => {
    console.log('扫码操作完成')
  },
})

业务入口可以走 src/utils/openScanCode.js。它在 App 端调用本插件,其他端回退 uni.scanCode,返回值仍是扫码文本。

图片识别(不打开扫码页)

import { decodeImage } from '@/uni_modules/mlkit-scancode'

decodeImage({
  path: 'file:///storage/emulated/0/DCIM/xxx.jpg',
  formats: ['QR_CODE'],
  success: (res) => {
    console.log('识别结果:', res.result, res.format)
  },
  fail: (err) => {
    console.error('识别失败:', err.errCode, err.errMsg)
  },
})

path 支持 file:// URI、绝对路径,以及各端的沙箱虚拟路径(_doc/、_user/、internal://、sandbox:// 会自动拼到沙箱目录)。不支持 http:// / https:// 网络图片,请先下载再传入。鸿蒙端相册选图返回的 content:// URI 会自动读入沙箱再解码,可以直接传。

API

startScan(options)

StartScanOptions

参数 类型 必填 默认值 说明
tip string 否 将二维码对准屏幕,即可自动扫描 扫码页提示文案
formats string[] 否 ['QR_CODE'] 识别格式。不传只认二维码,支持的值见下节
vibrate boolean 否 true 成功后是否震动
album boolean 否 true 是否显示右上角「相册」按钮,允许从相册选图识别
autoClose boolean 否 true 扫到码后是否自动关闭扫码页。传 false 时页面停在最后一帧,需在 success 里调 closeScan() 关闭
success (res) => void 否 - 成功回调。多个码时,回调的是用户点选的那一个
fail (err) => void 否 - 失败回调
complete (res) => void 否 - 成功或失败都会触发

支持的识别格式

formats 传字符串数组,三端行为对齐;不区分大小写,逗号或数组元素均可(内部会做 trim/去重)。

值 Android iOS Harmony 备注
QR_CODE 或 QR ✔ ✔ ✔ 二维码,默认格式
AZTEC ✔ ✔ ✔ 二维矩阵码
CODABAR ✔ ✔ ✔ iOS 需 15.0+
CODE_39 ✔ ✔ ✔ 一维码
CODE_93 ✔ ✔ ✔ 一维码
CODE_128 ✔ ✔ ✔ 一维码
DATA_MATRIX ✔ ✔ ✔ 二维矩阵码
EAN_8 ✔ ✔ ✔ 一维码
EAN_13 ✔ ✔ ✔ 一维码
ITF ✔ ✔ ✔ iOS 走 i2of5,鸿蒙对应 ITF14
PDF417 ✔ ✔ ✔ 二维条码
UPC_A ✔ ✔ ✔ iOS 由 EAN_13 覆盖
UPC_E ✔ ✔ ✔ 一维码
ALL / ALL_FORMATS ✔ ✔ ✔ 一次性打开全部格式

回调里 format 字段返回的是上表第一列里的名字,多码同屏时按用户点选的那个返回(该码自身的码制,不再固定 QR_CODE);识别到但拿不到坐标、走「点选回退」列表时同样返回各码自己的码制。

ScanSuccess

字段 类型 说明
result string 扫码内容
format string 格式,例如 QR_CODE
errMsg string startScan:ok

错误码

错误码 说明
9010001 无法打开扫码页
9010002 相机权限被拒绝
9010003 用户取消
9010004 没有识别到内容
9010005 图片读取失败(decodeImage 专用)

取消不要当成无效二维码提示。openScanCode.js 用 isScanCanceled 判断 9010003。

closeScan()

关闭当前打开着的扫码页。配合 startScan({ autoClose: false }) 使用:识别成功后页面不会自动关闭,你可以在 success 里先做校验 / 上传 / 弹自己的 UI,处理完再调 closeScan()。

import { startScan, closeScan } from '@/uni_modules/mlkit-scancode'

startScan({
  autoClose: false,
  success: async (res) => {
    // 页面停在识别成功的那一帧,相机已停止,可以放心做异步处理
    await submitCode(res.result)
    closeScan()
  },
})
  • 页面不存在(已关闭 / 从没打开)时静默返回,不触发 fail / cancel
  • 只关页面,不会二次触发 success / fail / complete
  • 定格态下不再继续识别;要重新扫请先 closeScan() 再调一次 startScan
  • decodeImage 不打开页面,与本方法无关

注意:定格期间扫码页仍在前台(Android 是独立 Activity、iOS 是全屏 present 的 VC、鸿蒙是 CustomDialog),uni.showModal / uni.showLoading 这类页面级 UI 会被它遮住,用户也点不到。需要给用户看进度,请先把状态记在自己页面的 data 里,关页后再展示(demo 页就是这么做的)。

decodeImage(options)

从图片路径解码条码内容,不打开扫码页。图片里有多个码时会全部返回。

DecodeImageOptions

参数 类型 必填 说明
path string 是 图片路径。支持 file:// URI、绝对路径。_doc/ 路径请先转换
formats string[] 否 识别格式,默认 ['QR_CODE']
success (res) => void 否 成功回调。图片中有多个码时,results 返回全部
fail (err) => void 否 失败回调
complete (res) => void 否 成功或失败都会触发

DecodeImageSuccess

字段 类型 说明
result string 第一个码的内容,等价于 results[0].result
format string 第一个码的格式,等价于 results[0].format
results ScanCodeItem[] 图片中识别到的全部码,已按内容去重
errMsg string 固定为 decodeImage:ok

results 中每项为 { result: string, format: string }。

鸿蒙端的 results 受 ScanKit 限制:单帧最多返回 4 个码,超过的部分不会返回。Android / iOS 无此限制。

decodeImage({
  path,
  success: (res) => {
    console.log(res.results.length)      // 码的个数
    console.log(res.results[0].result)   // 第一个码的内容
  },
  fail: (err) => console.log(err),
})

错误码

错误码 说明
9010001 无法获取页面
9010004 未识别到内容
9010005 图片读取失败

目录

uni_modules/mlkit-scancode/
├── package.json
├── readme.md
├── changelog.md
└── utssdk/
    ├── interface.uts
    ├── unierror.uts
    ├── app-android/
    ├── app-ios/
    └── app-harmony/

许可

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

CAMERA

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

无

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。