ブラウザでバーコードスキャン機能を実装

技術

最近、いろいろと調べながら対応したので、メモしておきます。

今回やったこと

  • ブラウザ上でバーコードスキャン機能を実装
  • モーダル画面を表示するとカメラをオンにしてスキャン開始
  • モーダル画面を閉じるとスキャン終了してカメラをオフ
  • モーダル画面を開いたボタンに応じて、スキャン結果の返却先を変更
  • VB.NET (.NET Framework)環境で実装
f:id:love_zawa:20210326181526p:plainf:id:love_zawa:20210326181333p:plain
バーコードスキャン用のモーダル画面を表示する様子。※読み込んでいる画像は念のため隠しています。

使用したライブラリやフレームワーク

  • QuaggaJS
    • バーコードスキャン機能を提供してくれるライブラリ
    • QRコードには非対応
  • BootStrap 5
    • 画面全体のスタイル調整
    • モーダル画面の表示にも活用

ソース

HTML部分(一部抜粋):sample.aspx

<!doctype html>
<html lang="ja">
<head runat="server">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>バーコードスキャンテスト</title>
    <%--BootstrapのCSS読み込み--%>
    <link rel="stylesheet" type="text/css" href="../../Css/Bootstrap/bootstrap.min.css" />
    <%--画面のCSS読み込み--%>
    <link rel="stylesheet" type="text/css" href="../../Css/Sample/sample.css" />
    <%--jQuery読み込み--%>
    <script src="../../Script/jQuery/jquery-3.6.0.min.js"></script>
</head>
<body>
<%--検索条件--%>
<div id="KensakuArea" class="container">
<div class="row">
<div class="col-auto">
<table>
<tr>
<%--バーコード①--%>
<td>
<%--モーダルウィンドウの結果を設定するためにIDモードをStaticにしている--%>
<asp:TextBox ID="txtBarcode1" runat="server" ClientIDMode="Static" placeholder="バーコード①" CssClass="form-control form-control-sm"/>
</td>
<td>
<%--バーコードスキャン用モーダル画面の表示--%>
<%--ASPタグを利用するとPostBackが生じてモーダル画面が表示後即座に消えてるため、あえて通常のタグを利用している--%>
<button type="button" style="width:30px; margin:0px; padding:0px; border:none;" data-bs-toggle="modal" data-bs-target="#staticBackdrop" data-textboxid="txtBarcode1">
<img src="../../Img/camera.png" style="width: 30px;"/>
</button>
</td>
<%--バーコード②--%>
<td>
<%--モーダルウィンドウの結果を設定するためにIDモードをStaticにしている--%>
<asp:TextBox ID="txtBarcode2" runat="server" CssClass="form-control form-control-sm" ClientIDMode="Static"/>
</td>
<td>
<%--バーコードスキャン用モーダル画面の表示--%>
<%--ASPタグを利用するとPostBackが生じてモーダル画面が表示後即座に消えてるため、あえて通常のタグを利用している--%>
<button type="button" style="width:30px; margin:0px; padding:0px; border:none;" data-bs-toggle="modal" data-bs-target="#staticBackdrop" data-textboxid="txtBarcode2">
<img src="../../Img/camera.png" style="width: 30px;"/>
</button>
</td>
</tr>
</table>
</div>
<%--モーダルウィンドウ(開始)--%>
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-fullscreen-lg-down">
<div class="modal-content">
<%--タイトル--%>
<div class="modal-header">
<H4 class="modal-title" id="staticBackdropLabel">バーコードスキャン</H4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<%--読み取り部--%>
<div class="modal-body text-center" style="margin: 0px auto;">
<%--呼び出し元から渡された引数を保持するHiiden項目--%>
<div style="visibility:visible;">
<input type="hidden" id="targetTextBoxID">
</div>
<div>
<input type="text" id="scanResult" placeholder="読み取り結果">
</div>
<%--photo-area部分にカメラの映像を表示--%>
<div id="photo-area" class="viewport"></div>
</div>
<%--フッター--%>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">キャンセル</button>
<button type="button" id="btnDone" class="btn btn-primary">決定</button>
</div>
</div>
</div>
</div>
<%--モーダルウィンドウ(終了)--%>
</div>
</div>
<%--QuaggaJS読み込み--%>
<script type="text/javascript" src="../../Script/QuaggaJS/quagga.js"></script>
<%--画面用のScript読み込み--%>
<script type="text/javascript" src="../../Script/Sample/sample.js"></script>
<%--BootstrapのScript読み込み--%>
<script src="../../Script/Bootstrap/bootstrap.bundle.min.js"></script>
</body>
</html>

CSSファイル(sample.css)

/*********************************
 * バーコード用モーダル
 * canvasやvideo等のHTML上にないタグはQuaggaJSから生成される
 *********************************/
#photo-area.viewport canvas, video {
margin: 10px auto;
}
#photo-area.viewport canvas.drawingBuffer, video.drawingBuffer {
margin-left: -640px;
}

JSファイル(sample.js)

//===================================================================================
// モーダル画面の表示・親画面への値返却
//===================================================================================
// モーダル画面を開いた時の処理
$('#staticBackdrop').on('show.bs.modal', function (event) {
// モーダルを開いたボタンを取得
var button = $(event.relatedTarget);
// data-textboxidの値取得
var targetTextBoxID = button.data('textboxid');
// モーダルを取得
var modal = $(this);
// 受け取った値をspanタグのとこに保持
modal.find('.modal-body #targetTextBoxID').text(targetTextBoxID);
// スキャン開始
console.log("start");
startScanner();
});
// モーダルの「×」や「キャンセル」ボタンを押した時の処理
$('#staticBackdrop').on('hidden.bs.modal', function (event) {
// モーダル閉じる
$('#staticBackdrop').modal('hide');
// スキャン終了
console.log("stop");
Quagga.stop();
});
// モーダルの「決定」ボタンを押した時の処理
$("#btnDoneScan").on('click', function () {
// モーダル閉じる
$('#staticBackdrop').modal('hide');
// モーダル画面で設定した値を取得し変数に保存
var scanResultVal = $('#scanResult').val();
var targetTextBoxID = $('#targetTextBoxID').text();
// 変数保存済みの読み込み結果をクリア
$('#scanResult').val("");
// 呼び出し元のボタンに応じたテキストボックスに設定
switch (targetTextBoxID) {
case 'txtBarcode1':
$("#txtBarcode2").val(scanResultVal);
break;
case 'txtBarcode2':
$("#txtBarcode2").val(scanResultVal);
}
// スキャン終了
console.log("stop");
Quagga.stop();
});
//===================================================================================
// バーコード用
//===================================================================================
const startScanner = () => {
Quagga.init({
// 設定
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#photo-area'),
constraints: {
decodeBarCodeRate: 3,
successTimeout: 500,
codeRepetition: true,
tryVertical: true,
frameRate: 15,
width: 640,
height: 480,
facingMode: "environment"
}
},
// デコードタイプ(複数指定可能)
decoder: {
readers: [
"code_39_reader"
]
}
}, function (err) {
if (err) {
console.log(err);
return;
}
console.log("Initialization finished. Ready to start");
Quagga.start();
// Set flag to is running
_scannerIsRunning = true;
});
/**
     * Quagga.onProcessed(callback)
     * 処理が完了した後にフレームごとに呼び出される関数callback(data)を登録。
     **/
Quagga.onProcessed(function (result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
// console.log("processing");
// 検出中の緑の線の枠
if (result.boxes) {
//drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
drawingCtx.clearRect(0, 0, drawingCanvas.width, drawingCanvas.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) {
console.log("success");
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
}
);
}
}
});
//barcode read call back
Quagga.onDetected(function (result) {
// 取得時の画像を表示
var resultImg = document.querySelector(".resultImg");
//resultImg.setAttribute("src", this.Quagga.canvas.dom.image.toDataURL());
var resultCode = result.codeResult.code;
// モーダルテキストボックスに表示
$('#scanResult').val(resultCode);
console.log("stop");
Quagga.stop();
});
}

参考サイト

QuaggaJSの使い方について

メッチャ参考になりました。
ameblo.jp

BootStrapでのモーダル画面について

getbootstrap.jp
qiita.com

ASP.NET が自動生成するDOMのクライアント側での操作について

aspnet.keicode.com

コメント

タイトルとURLをコピーしました