久しぶりの、「便利ページ:Javascriptでちょっとした便利な機能を作ってみた」 のシリーズものです。
今回は、QRコードスキャンです。
QRコード生成はすでに実装してあったのですが、スキャンする方はありませんでした。
HTML5では、PCに接続されたカメラを扱うことができますので、ブラウザだけでスキャンできます。また、AndroidやiPhoneでのChromeでも動作するので、これでPCだけでなくスマホでも動きます。
いつもの通りGitHubにも上げてあります。
https://github.com/poruruba/utilities
参考までに、以下にデモとしてアクセスできるようにしてあります。
https://poruruba.github.io/utilities/
(2020/2/2 修正)
・画像サイズを300px固定ではなく、カメラ画像のサイズに合わせるようにしました。
QRコードスキャンのためのライブラリ
以下を使わせていただきました。ありがとうございます。
cozmo/jsQR
https://github.com/cozmo/jsQR
HTMLで以下のようにスクリプトをロードしておきます。
<script src="dist/js/jsQR.js"></script>
ソースコード抜粋
肝心のJavascriptのソースコードです。重要部分のみ抜粋しています。
qrcode_scan: function(){
this.qrcode_video = $('#qrcode_camera')[0];
this.qrcode_canvas = $('#qrcode_canvas')[0];
if( this.qrcode_running ){
this.qrcode_forcestop();
return;
}
this.qrcode_running = true;
this.qrcode_btn = 'QRスキャン停止';
this.qrcode_timer = setTimeout(() =>{
this.qrcode_forcestop();
}, QRCODE_CANCEL_TIMER);
return navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" }, audio: false })
.then(stream =>{
this.qrcode_scaned_data = "";
this.qrcode_video.srcObject = stream;
this.qrcode_draw();
})
.catch(error =>{
alert(error);
});
},
qrcode_draw: function(){
// console.log(this.qrcode_video.videoWidth, this.qrcode_video.videoHeight);
if( this.qrcode_context == null ){
if( this.qrcode_video.videoWidth == 0 || this.qrcode_video.videoHeight == 0 ){
if( this.qrcode_running )
requestAnimationFrame(this.qrcode_draw);
return;
}
this.qrcode_canvas.width = this.qrcode_video.videoWidth;
this.qrcode_canvas.height = this.qrcode_video.videoHeight;
this.qrcode_context = this.qrcode_canvas.getContext('2d');
}
this.qrcode_context.drawImage(this.qrcode_video, 0, 0, this.qrcode_canvas.width, this.qrcode_canvas.height);
const imageData = this.qrcode_context.getImageData(0, 0, this.qrcode_canvas.width, this.qrcode_canvas.height);
const code = jsQR(imageData.data, this.qrcode_canvas.width, this.qrcode_canvas.height);
if( code && code.data != "" ){
this.qrcode_scaned_data = code.data;
console.log(code);
this.qrcode_forcestop();
this.qrcode_context.strokeStyle = "blue";
this.qrcode_context.lineWidth = 3;
var pos = code.location;
this.qrcode_context.beginPath();
this.qrcode_context.moveTo(pos.topLeftCorner.x, pos.topLeftCorner.y);
this.qrcode_context.lineTo(pos.topRightCorner.x, pos.topRightCorner.y);
this.qrcode_context.lineTo(pos.bottomRightCorner.x, pos.bottomRightCorner.y);
this.qrcode_context.lineTo(pos.bottomLeftCorner.x, pos.bottomLeftCorner.y);
this.qrcode_context.lineTo(pos.topLeftCorner.x, pos.topLeftCorner.y);
this.qrcode_context.stroke();
}else{
if( this.qrcode_running )
requestAnimationFrame(this.qrcode_draw);
}
},
qrcode_forcestop: function(){
if( !this.qrcode_running )
return;
this.qrcode_running = false;
if( this.qrcode_timer != null ){
clearTimeout(this.qrcode_timer);
this.qrcode_timer = null;
}
this.qrcode_video.pause();
this.qrcode_video.srcObject = null;
this.qrcode_btn = 'QRスキャン開始';
},
ご参考までに、HTMLの方も。
<label>scaned data</label>
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default clip_btn glyphicon glyphicon-paperclip" data-clipboard-target="#qrcode_scaned_data"></button>
</span>
<input id="qrcode_scaned_data" type="text" class="form-control" v-model="qrcode_scaned_data" readonly>
</div><br>
<button class="btn btn-primary" v-on:click="qrcode_scan()">{{qrcode_btn}}</button><br>
<div>
<img v-show="!qrcode_running && qrcode_scaned_data==''" id="qrcode_start" class="img-responsive" src="./img/qrcode_start.png"><br>
<video v-show="qrcode_running" id="qrcode_camera" class="img-responsive" autoplay></video>
<canvas v-show="!qrcode_running && qrcode_scaned_data!=''" id="qrcode_canvas" class="img-responsive"></canvas>
</div>
解説
・qrcode_scan()
ボタン押下をトリガに、QRコードスキャンを開始します。
まずは、「navigator.mediaDevices.getUserMedia」を呼び出して、ユーザに対してカメラ利用の許可を聞いた後にカメラを起動させます。
起動した後、「this.qrcode_video.srcObject = stream;」でカメラ画像をWebページに表示させ、「qrcode_draw()」の呼び出しで、カメラ画像からQRコードを探します。
setTimeout()がありますが、一定時間QRコードスキャンで見つからなかったときに、QRコードスキャンを停止するためのものです。
ちなみに、ブラウザからカメラを利用するため、HTMLはHTTPSでホスティングしている必要があります。
・qrcode_draw()
カメラ画像からQRコードをスキャンします。
いったん、別のcanvasのcontextにコピーしたのち、QRコードスキャンのライブラリを呼び出します。
QRコードがあった場合には、QRコードの部分を青色の四角形で囲ってスキャン終了です。
もしQRコードがなかった場合には、「requestAnimationFrame」を呼び出して次のカメラ画像を待ちます。
補足
CPU負荷を低減せねば。。。
以上