Nuxt × quaggaJSでバーコードの読み込み
この記事について
quaggaJSというJSのライブラリを使って、バコードリーダを作りました。
色んな記事を参考に何とか動かせましたが、なぜ動いているのか自分でもよく分からんとなってしまったので自分用に整理したものです。
動作イメージは以下の公式のサンプルの様なイメージです。
公式のサンプル QuaggaJS, an advanced barcode-reader written in JavaScript
アジェンダ
参考にした記事
公式のトップページ
QuaggaJS, an advanced barcode-reader written in JavaScript
公式サンプルのhtmlのソースコード
quaggaJS/live_w_locator.html at master · serratus/quaggaJS · GitHub
公式サンプルのjsのソースコード
quaggaJS/live_w_locator.js at master · serratus/quaggaJS · GitHub
quaggaJS/styles.css at master · serratus/quaggaJS · GitHub
その他参考記事
Nuxt.jsでバーコードリーダを作ったら、いろいろハマった上にiOSのPWAでカメラにアクセスできなかった話 - Qiita
WebRTCを使ってブラウザでバーコードのスキャンをしてみる - Qiita
導入
パッケージをinstallする
yarn add quagga
対象vueファイルにimportする
import Quagga from 'quagga';
htmlファイルに画像を移す要素を記載する
<div id="interactive" class="viewport"></div>
処理全体
処理の解説
Quagga.init(config, this.onInitilize);
Quagga.init()メソッドとは?(公式の日本語直訳)
このメソッドは、特定の構成設定(以下を参照)のライブラリを初期化し、Quaggaがブートストラップフェーズを完了したときにコールバック(err)を呼び出します。リアルタイム検出が設定されている場合、初期化プロセスはカメラへのアクセスも要求します。エラーの場合、errパラメータが設定され、原因に関する情報が含まれます。考えられる原因は、inputStream.typeがLiveStreamに設定されている可能性がありますが、ブラウザーがこのAPIをサポートしていないか、単にユーザーがカメラの使用許可を拒否した場合に発生します。ターゲットを指定しない場合、QuaggaJSはCSSセレクター#interactive.viewportと一致する要素を探します(後方互換性のため)。 targetは文字列(DOMノードのいずれかに一致するCSSセレクター)またはDOMノードです。
- 要するに
- 引数で受け取った設定用のパラメータを元にQuaggaを初期化
- カメラへのアクセスを要求し、エラー(ユーザーがカメラへのアクセスを拒否する等)が発生した場合コールバックを呼び出す
読み込んでいる設定について
const config = { // カメラの映像の設定 inputStream: { type: "LiveStream", // カメラ映像を表示するHTML要素の設定 target: document.querySelector("#interactive"), // バックカメラの利用を設定. (フロントカメラは"user") constraints: { facingMode: "environment" }, // 検出範囲の指定: 上下30%は対象外 area: { top: "30%", right: "0%", left: "0%", bottom: "30%" } }, // 解析するワーカ数の設定 numOfWorkers: navigator.hardwareConcurrency || 4, // バーコードの種類を設定: ISBNは"ean_reader" decoder: { readers: ["ean_reader"] } }
areaで左右に対象範囲外のエリアを指定すると、極端にバーコードの認識に時間が掛かってしまったため0で設定。
Nuxt.jsでバーコードリーダを作ったら、いろいろハマった上にiOSのPWAでカメラにアクセスできなかった話 - Qiita
読み込んでいるコールバック処理について
- 引数にエラーを受け取った場合、処理を中止し、そうで無い場合読込処理を開始する。
onInitilize (error) { if (!!error) { console.error(`Error: ${error}`, error); return; } // エラーがない場合は、読み取りを開始 Quagga.start(); },
Quagga.start()メソッドとは?(公式の日本語直訳)
ライブラリが初期化されると、start()メソッドがビデオストリームを開始し、画像の検索とデコードを開始します。
- 要するにライブラリ初期化後に
start()
メソッドを呼び出す事で、画像の読込を開始する
Quagga.onDetected(callback)
Quagga.onDetected()メソッドとは?(公式の日本語直訳)
バーコードパターンが検出されて正常にデコードされたときにトリガーされるコールバック(データ)関数を登録します。渡されたデータオブジェクトには、data.codeResult.codeを呼び出して取得できる検出されたコードを含む、デコードプロセスに関する情報が含まれています。
要するに、バーコードをデコードが完了したタイミングでの処理を記載する
このタイミングでISBN番号が取得出来るので、それを使った処理を記載する
// methods Quagga.onDetected(this.onDetected);
// methods onDetected (success) { const isbn = success.codeResult.code; // ISBNコードを検出した場合 if(isbn.includes('978')) { Quagga.stop() this.$emit('search-rakuten-api', isbn) } },
Quagga.onProcessed(callback);
Quagga.onProcessed()メソッドとは?(公式の日本語直訳)
このメソッドは、処理が完了した後にフレームごとに呼び出されるcallback(data)関数を登録します。データオブジェクトには、操作の成功/失敗に関する詳細情報が含まれます。出力は、検出やデコードが成功したかどうかによって異なります。
要するに、バーコードを無事に読み込んだ時の処理を記載する。
onDetected
と違ってまだデコードは終わっていない。このタイミングでバーコードを認識した際の処理(枠線で囲む等)を記載する
Quagga.onProcessed(this.onProcessed);
onProcessed
onProcessed(result) { const drawingCtx = Quagga.canvas.ctx.overlay; const drawingCanvas = Quagga.canvas.dom.overlay; if (result) { // 検出中の緑の線の枠 if (result.boxes) { drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); result.boxes.filter(function (box) { return box !== result.box; }).forEach(function (box) { Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "green", lineWidth: 2}); }); } // 読込中の青枠 if (result.box) { Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2}); } // 検出完了時の赤線 if (result.codeResult && result.codeResult.code) { Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3}); } } }
CSS
これまでの設定だけで上手くいきそうなものなのですが、これだけだと緑の枠がカメラの外にはみ出てしまいます。。。
そこで公式に記載の以下のCSSを当てる事で正しく表示されました。
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer { margin-left: -640px; }
追記
スマートフォンで開いた時に位置がずれていたため以下のCSSに変更することで解決しました。
.drawingBuffer { position: absolute; left: 0; }