@cclemon/scanner-vue
@cclemon/scanner-vue
useBarcodeScanner() composable(Vue 3)。完整手冊在 @cclemon/scanner-core 的 README(同一份 GUIDE.md)。
@cclemon/scanner 快速上手(Vue 3 + Vite)
條碼 / QR 掃描,iPad Safari 實測:對準後約 25 ms 出結果。
這份文件可以直接複製到其他專案的 docs/。
1. 安裝
pnpm add @cclemon/scanner-ui @cclemon/scanner-vue @cclemon/scanner-core
需要
@cclemon/scanner-core≥ 0.1.5(點陣式 Data Matrix 強化版;wasm 與完整手冊內附)。pnpm view @cclemon/scanner-core version確認。還在 0.1.1 的過渡做法:
pnpm add zxing-wasm@3.1.4,然後import wasmUrl from 'zxing-wasm/reader/zxing_reader.wasm?url'(同一個檔案,之後換成下面那行即可)。
2. 最簡用法(5 行)
<script setup>
import { BarcodeScanner } from '@cclemon/scanner-ui'
import wasmUrl from '@cclemon/scanner-core/zxing_reader.wasm?url'
const onDecoded = (result) => {
console.log(result.text, result.format) // 例如 'Y04900132', 'code_128'
}
const errorText = { 'permission-denied': '請允許相機權限', 'no-camera': '找不到相機', 'camera-in-use': '相機被其他 App 佔用' }
</script>
<template>
<!-- 元件會填滿父容器,父容器要給尺寸 -->
<div style="width: 100%; max-width: 480px; aspect-ratio: 3 / 4">
<BarcodeScanner :formats="['qr_code', 'code_128']" :wasm-url="wasmUrl" @decoded="onDecoded">
<template #hint="{ hint }">
{{ { align: '把條碼對進框內', 'move-closer': '靠近一點', 'hold-steady': '拿穩一點' }[hint] }}
</template>
<template #error="{ error, retry }">
<button @click="retry">{{ errorText[error.code] ?? error.code }},點我重試</button>
</template>
<template #default="{ state, start, stop }">
<button v-if="state !== 'scanning'" @click="start">開始掃描</button>
<button v-else @click="stop">停止</button>
</template>
</BarcodeScanner>
</div>
</template>
- Vite dev 模式要在
vite.config.js加optimizeDeps: { exclude: ['@cclemon/scanner-core', '@cclemon/scanner-vue', '@cclemon/scanner-ui'] }, 否則解碼 Worker 會 404(core ≥ 0.2.2 會自動退回正確路徑並在 console 警告,≤ 0.2.1 則直接報decoder-crashed)。 改完要npx vite --force清快取。production build 不受影響。 :wasm-url在 Vite 專案請一定要傳(用上面的?url匯入)。Vite 打包 production 時不會替依賴套件內的 wasm 產生資源,不傳的話會退回外網 CDN,內網部署失敗;console 會有[scanner] ... fell back to CDN警告。- 一定要有一個按鈕呼叫
start():iOS 要求相機由使用者手勢觸發,不能自動開。 - 元件本身沒有任何文字,所有文案由 slot 提供(方便 i18n)。
- 畫面同時有多個碼時,只回報框中央的那一個;同一個碼留在框內只會觸發
@decoded一次(拿開 1.5 秒再放回才會再觸發),所以直接在onDecoded送 API 不會重複送。
3. 格式(越少越快)
import { QR_FORMATS, LINEAR_FORMATS } from '@cclemon/scanner-vue'
:formats="QR_FORMATS" // 只掃 QR(預設)
:formats="LINEAR_FORMATS" // 只掃一維條碼(EAN/UPC/Code128/Code39/ITF…)
:formats="[...QR_FORMATS, ...LINEAR_FORMATS]" // 混合,成本約 1.5–2 倍
formats 是響應式的:綁一個 ref 改值就即時切換,不重啟相機。
3.5 Data Matrix(含 PCB 點陣式)
:formats="['data_matrix']" // 或 [...QR_FORMATS, 'data_matrix']
PCB 鑽孔 / 雷刻的 點陣式 Data Matrix(每個模組是圓點)zxing 原生解不出,
core ≥ 0.1.3 在 formats 含 data_matrix 時會自動開啟前處理(dotted: 'auto'),
每幀多花約 6–10 ms。實測 17 張 PCB 照片:拍照模式 11/17(遠拍碼太小的 6 張任何解碼器都不可能)。
要對準:把碼放在框中央、鏡頭距離讓碼佔框寬 1/4 以上,等約半秒(會輪替幾種前處理參數)。
4. 自己排版:composable
<script setup>
import { ref } from 'vue'
import { useBarcodeScanner } from '@cclemon/scanner-vue'
import wasmUrl from '@cclemon/scanner-core/zxing_reader.wasm?url'
const video = ref(null)
const { state, lastResult, error, start, stop, toggleTorch, capabilities } = useBarcodeScanner(video, {
formats: ['qr_code'],
wasm: { wasmUrl },
onDecoded: (r) => submit(r.text),
})
</script>
<template>
<video ref="video" style="width: 100%; object-fit: cover" />
<button @click="start">掃描</button>
<button v-if="capabilities?.torch" @click="toggleTorch">手電筒</button>
<p>{{ lastResult?.text }}</p>
<p v-if="error">{{ error.code }}</p>
</template>
回傳值(都是 readonly ref):state error backend camera capabilities settings lastResult candidates support;
方法:start stop pause resume toggleTorch setZoom switchCamera updateOptions。
元件卸載時自動釋放相機與 Worker。
5. 錯誤代碼 → 文案(自己對 i18n)
error.code |
意思 | 建議文案 |
|---|---|---|
permission-denied |
使用者拒絕相機 | 請允許相機權限後重試 |
no-camera |
沒有相機 | 此裝置沒有相機 |
camera-in-use |
被其他 App 佔用 | 請關閉其他使用相機的 App |
insecure-context |
不是 HTTPS | 需以 HTTPS 開啟 |
camera-failed / track-ended |
相機異常 | 相機異常,請重試 |
decoder-init-failed |
wasm 或 Worker 載入失敗(wasm-url 沒設 / 路徑錯 / Vite dev 沒加 optimizeDeps.exclude) |
掃描器載入失敗 |
error.recoverable === true 時顯示「重試」按鈕即可。
6. 部署注意
- HTTPS(相機硬性條件;
http://localhost例外) - nginx 舊版的
mime.types沒有 wasm:改全域檔/etc/nginx/mime.types加一行application/wasm wasm;(見 deploy.md)。 不要在 location 裡單獨寫types { application/wasm wasm; }——那會整份取代 MIME 表,JS/CSS 全變text/plain、整站白頁。 MIME 不對時 wasm 仍能載入(退回非串流編譯),只是慢一點。 - 不能有
Permissions-Policy: camera=()header - 掛在子路徑(
/rms/)時?url匯入會自動處理,不用改
7. 常見問題
| 現象 | 原因 |
|---|---|
按了開始沒反應、state 停在 requesting-permission |
瀏覽器正在問權限,或 iPad 上不是由手勢觸發 |
decoder-init-failed |
沒傳 wasm-url,core 去 CDN 抓 wasm 但內網沒外網;或 dev 模式 Worker 404(見上面的 optimizeDeps.exclude)。細節在 error.cause |
| 掃得到 QR 掃不到條碼 | formats 沒包含一維格式 |
| Data Matrix 有黃框但解不出 | 點陣式碼:需 core ≥ 0.1.3(自動前處理),且 formats 要含 data_matrix |
| 兩個碼只回報一個 | 設計如此(單一模式);要全部回報加 :multi="true" |
| 前鏡頭畫面左右相反 | <BarcodeScanner mirror> |
更多
-
完整手冊(所有選項與調校、食譜、疑難排解):裝好套件後看
node_modules/@cclemon/scanner-core/README.md,或 https://www.npmjs.com/package/@cclemon/scanner-core -
完整說明與 iPad 實測數據:https://github.com/Peter-1119/WebCamCall (private)
-
部署細節:
docs/deploy.md;實機檢查表:docs/device-checklist.md
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.3 | ^3.3.0 | MIT | |
| 0.1.2 | ^3.3.0 | MIT | |
| 0.1.1 | ^3.3.0 | MIT |