今回はHTML5の機能を利用した「QRコードリーダー」を作成します。 以前作ったJavaScriptによるカメラを操作するコードに、QRコードを読み取ってくれる便利ライブラリをくっつけただけのお手軽版。
PCやスマホに付いているカメラの映像を0.3秒毎にチェックし、QRコードが見つかったらQRに埋め込まれている情報を表示してくれます。見つかったQRコードの場所も簡単にマーキングする機能も付けてみました。
HTML5でQRコードリーダー
サンプル
以下のページから実際にサンプルを実行できます。 miku3.net
初回のアクセス時にWebブラウザから、このサイトにカメラの操作を許可して良いか聞かれますので「許可」ボタンをクリックしてください。「ブロック」ボタンを押すと設定を変更するのにメニューの少し深いところに潜る必要がありますのでご注意を。
準備
今回はこちらのjsQR
ライブラリを使います。
github.com
適当なディレクトリを作成し、webpackから出力されたファイルがdistディレクトリの下にいるので直接取ってきます。
$ mkdir qrreader && cd qrreader $ wget 'https://raw.githubusercontent.com/cozmo/jsQR/master/dist/jsQR.js'
このままだと若干ファイルサイズが大きいのでminifyします。大体半分くらいになりましたね。
$ uglifyjs --compress --mangle -- jsQR.js > jsQR.min.js $ ls -lah -rw-r--r-- 1 katsube staff 248K 6 22 15:40 jsQR.js -rw-r--r-- 1 katsube staff 126K 6 22 15:40 jsQR.min.js
minifyについて詳しくは過去の記事を参照ください。 blog.katsubemakito.net
ソースコード
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>[HTML5] QRCode Reader</title> <style> /* 全体のレイアウト調整 */ #contents { display:flex; width:650px;} #camera, #picture, #result { justify-content:center; margin:5px;} /* リーダー部分 */ #picture { display:none; } #result { border: 1px solid gray; width:300px; height:200px; padding:10px;} small { color:gray; } </style> </head> <body> <h1>QRCode Reader</h1> <section id="contents"> <!-- カメラ映像 --> <video id="camera" width="300" height="200" muted></video> <!-- 処理用 --> <canvas id="picture" width="300" height="200"></canvas> <!-- 読み取り結果 --> <div id="result"> <small>※ここに読み取り結果が表示されます※</small> </div> </section> <script src="jsQR.min.js"></script> <script> const video = $("#camera"); // === document.querySelector("#camera"); const canvas = $("#picture"); // === document.querySelector("#picture"); const ctx = canvas.getContext("2d"); window.onload = () => { /** カメラ設定 */ const constraints = { audio: false, video: { width: 300, height: 200, facingMode: "user" // フロントカメラを利用する } }; /** * カメラを<video>と同期 */ navigator.mediaDevices.getUserMedia(constraints) .then( (stream) => { video.srcObject = stream; video.onloadedmetadata = (e) => { video.play(); // QRコードのチェック開始 checkPicture(); }; }) .catch( (err) => { console.log(err.name + ": " + err.message); }); }; /** * QRコードの読み取り */ function checkPicture(){ // カメラの映像をCanvasに複写 ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // QRコードの読み取り const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const code = jsQR(imageData.data, canvas.width, canvas.height); //---------------------- // 存在する場合 //---------------------- if( code ){ // 結果を表示 setQRResult("#result", code.data); // 文字列 drawLine(ctx, code.location); // 見つかった箇所に線を引く // video と canvas を入れ替え canvas.style.display = 'block'; video.style.display = 'none'; video.pause(); } //---------------------- // 存在しない場合 //---------------------- else{ // 0.3秒後にもう一度チェックする setTimeout( () => { checkPicture(); }, 300); } } /** * 発見されたQRコードに線を引く * * @param {Object} ctx * @param {Object} pos * @param {Object} options * @return {void} */ function drawLine(ctx, pos, options={color:"blue", size:5}){ // 線のスタイル設定 ctx.strokeStyle = options.color; ctx.lineWidth = options.size; // 線を描く ctx.beginPath(); ctx.moveTo(pos.topLeftCorner.x, pos.topLeftCorner.y); // 左上からスタート ctx.lineTo(pos.topRightCorner.x, pos.topRightCorner.y); // 右上 ctx.lineTo(pos.bottomRightCorner.x, pos.bottomRightCorner.y); // 右下 ctx.lineTo(pos.bottomLeftCorner.x, pos.bottomLeftCorner.y); // 左下 ctx.lineTo(pos.topLeftCorner.x, pos.topLeftCorner.y); // 左上に戻る ctx.stroke(); } /** * QRコードの読み取り結果を表示する * * @param {String} id * @param {String} data * @return {void} */ function setQRResult(id, data){ $(id).innerHTML = escapeHTML(data); } /** * jQuery style wrapper * * @param {string} selector * @return {Object} */ function $(selector){ return( document.querySelector(selector) ); } /** * HTML表示用に文字列をエスケープする * * @param {string} str * @return {string} */ function escapeHTML(str){ let result = ""; result = str.replace("&", "&"); result = str.replace("'", "'"); result = str.replace("`", "`"); result = str.replace('"', """); result = str.replace("<", "<"); result = str.replace(">", ">"); result = str.replace(/\n/, "<br>"); return(result); } </script> </body> </html>
解説
大雑把なロジック
video
タグにカメラをつなぎ、0.3秒置きに静止画をcanvas
タグにコピーしたあとに画像データとして取り出します。この画像データをjsQRライブラリに渡しチェック。もし画像内にQRコードが存在している場合はその内容を表示するサンプルになります。QRコード周りの面倒な処理はすべてライブラリが面倒を見てくれますので、それ以外の部分を書いた感じですね。
jsQRの使い方
jsQRは非常にシンプルで、canvasの内容を画像として取り出したあとに、jsQR()に渡し戻り値をチェックするだけです。存在しなければfalseになります。
<script src="jsQR.js"></script> <script> // Canvasの内容を画像として取り出す const canvas = document.querySelector("canvas"); const ctx = canvas.getContetext("2d"); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // jsQRでチェック const code = jsQR(imageData.data, canvas.width, canvas.height); if (code) { console.log("Found QR code", code); } </script>
戻り値はハッシュ(連想配列)として返ってきます。内容は以下の通り。
key | 説明 |
---|---|
binaryData | 認識したQRコードの画像データ(バイナリ) |
data | 認識したQRコード内にあるテキスト情報 |
location | 認識したQRコードが画像内のどこで見つかったかの座標情報 |
今回はcode.data
の内容を表示し、code.location
を元に画像に線を引いたというわけです。