目的
html+javascriptだけで実装されたシンプルなQRコードリーダーのサンプル(が欲しかった)を作る。
機能
- スマホのカメラやWebカメラでQRコードを値を取得する。
- QRコードを認識したら、赤枠を表示する。
処理概要
-
<video>
タグでカメラ映像を表示 - タイマーでカメラ映像をイメージ化、QR認識ライブラリjsQRに引き渡す
- 認識すると、コードの値とQRコードがある領域の座標を取得します。
- 2.に戻り認識を繰り返します。
利用モジュール
jsQR.js
ファイルを使います。https://github.com/cozmo/jsQR/tree/master/dist/jsQR.js
使い方
下記ファイルを同一フォルダに配置して、ブラウザ(スマホの方が使いやすい)から開きます(要Webカメラ)。
- index.html
- jsQR.js
※スマホの場合, httpsで開く必要があります
-
github pagesから試せます。
ソース解説
全体のソースは最後に載せてあります。
- カメラ動画を表示しながら、
- タイマーで画像に変換
- jsQRで認識
<head>
部分
- QR読み取りライブラリをロードする
<script src="./jsQR.js"></script>
<body>
部分
-
<div id="result" ~>
- QR読み取り結果を表示
-
<video>~</video>
- カメラ映像出力部分。z-indexにマイナスを指定して、オーバーレイの奥に表示します。
-
<div id="overlay" >~</div>
- QRコードを赤枠で囲うためのオーバーレイ。position:relative;で重ねています。
<div id="result" style="min-height: 20px;"></div>
<div>
<div style="position:relative;">
<video style="position: absolute; z-index: -100;"></video>
<div id="overlay" style="position: absolute; border: 1px solid #F00;"></div>
</div>
</div>
<script>
部分
- jsQRで認識したQRコードの箇所を赤枠で囲むための関数
- id="overlay"をQRコードの位置へ移動します。
const drawRect = (topLeft, bottomRight) => {
const { x: x1, y: y1 } = topLeft;
const { x: x2, y: y2 }= bottomRight;
const overlay = document.querySelector('#overlay');
overlay.style.left = `${x1}px`;
overlay.style.top =`${y1}px`;
overlay.style.width = `${x2 - x1}px`;
overlay.style.height =`${y2 - y1}px`;
};
- カメラの準備(videoタグに表示)
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const video = document.querySelector('video');
video.srcObject = stream;
video.play();
- canvas作成
- 描画負荷を軽減するためOffscreenCanvasを作成します。
- QRコード認識で必要なイメージを、videoから作成するために利用します。
const { width, height } = constraints.video;
const canvas = new OffscreenCanvas(width, height);
const context = canvas.getContext('2d');
- QRコード認識
- videoからイメージデータを作成し、QR認識処理に引き渡します。
- 認識した場合は、コードの表示と赤枠の表示を行います。
- タイマーで繰り返します。
const timer = setInterval(() => {
context.drawImage(video, 0, 0, width, height);
const imageData = context.getImageData(0, 0, width, height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
document.querySelector('#result').textContent = code.data;
drawRect(code.location.topLeftCorner, code.location.bottomRightCorner);
} else {
document.querySelector('#result').textContent = '';
}
}, 300);
- 全体
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>JS QR Code Reader</title>
<meta name="description" content="QR Code Reader" />
<script src="./jsQR.js"></script>
</head>
<body>
<div id="result" style="min-height: 20px;"></div>
<div>
<div style="position:relative;">
<video style="position: absolute; z-index: -100;"></video>
<div id="overlay" style="position: absolute; border: 1px solid #F00;"></div>
</div>
</div>
<script>
const constraints = {
audio: false,
video: {
facingMode: 'environment',
width: 500,
height: 500,
}};
const drawRect = (topLeft, bottomRight) => {
const { x: x1, y: y1 } = topLeft;
const { x: x2, y: y2 }= bottomRight;
const overlay = document.querySelector('#overlay');
overlay.style.left = `${x1}px`;
overlay.style.top =`${y1}px`;
overlay.style.width = `${x2 - x1}px`;
overlay.style.height =`${y2 - y1}px`;
};
(async() => {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const video = document.querySelector('video');
video.srcObject = stream;
video.play();
const { width, height } = constraints.video;
const canvas = new OffscreenCanvas(width, height);
const context = canvas.getContext('2d');
const timer = setInterval(() => {
context.drawImage(video, 0, 0, width, height);
const imageData = context.getImageData(0, 0, width, height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
document.querySelector('#result').textContent = code.data;
drawRect(code.location.topLeftCorner, code.location.bottomRightCorner);
} else {
document.querySelector('#result').textContent = '';
}
}, 300);
} catch(error) {
console.log('load error', error);
}
})();
</script>
</body>
</html>
ローカルで試す場合
httpsが必要なため、オレオレ証明書を作成してから、http-serverを起動してください。
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
Generating a RSA private key
.................................+++++
.......................+++++
~~略~~
npx http-server -S -C cert.pem
Starting up http-server, serving ./public through https
Available on:
https://172.23.0.1:8080
https://127.0.0.1:8080