gotoshin

主に学んだ事の自分メモ用です。記事に書くまでも無いような事はhttps://scrapbox.io/study-diary/に書いてます。

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

公式サンプルのcssソースコード

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"] }
}
読み込んでいるコールバック処理について
  • 引数にエラーを受け取った場合、処理を中止し、そうで無い場合読込処理を開始する。
    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

これまでの設定だけで上手くいきそうなものなのですが、これだけだと緑の枠がカメラの外にはみ出てしまいます。。。

f:id:hatehate-nazenaze:20200717220624p:plain

そこで公式に記載の以下のCSSを当てる事で正しく表示されました。

#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
  margin-left: -640px;
}

追記

スマートフォンで開いた時に位置がずれていたため以下のCSSに変更することで解決しました。

  .drawingBuffer {
    position: absolute;
    left: 0;
  }