ブラウザでQRコードが弄れるらしいので、試してみました。
QR解析
カメラやマイクなどの入力デバイスを扱えるMediaDevicesインターフェイスを使用し、カメラから静止画を取得します。
そして静止画をjsQRというQR解析ライブラリへ通すことで、QRコードを読み取ることができます。
function qrParse(video){
const canvas = new OffscreenCanvas(240, 320);
const render = canvas.getContext("2d");
return new Promise((res)=>{
const loop = setInterval(()=>{
render.drawImage(video, 0, 0, canvas.width, canvas.height);
const img = render.getImageData(0, 0, canvas.width, canvas.height);
const result = jsQR(img.data, img.width, img.height);
if(result){
clearInterval(loop);
return res(result.data);
}
}, 100);
});
}
まず、このメソッドの引数としてHTMLVideoElement
を受け取ります。
そして、映像から定期的に静止画を抽出する必要があるため、1秒間に10回の間隔でHTMLVideoElement
をOffscreanCanvas
へ流し込みスナップショットを作成します。
スナップショットからImageData
を出力し、解析メソッドjsQR()
へ渡します。
解析結果が存在したら、繰り返し処理の解除を行い、QRデータを解決します。
QR生成
qrcodeという、いかにもという名前のQRコード生成ライブラリを使用します。
function qrGenerate(data){
const canvas = new OffscreenCanvas(1, 1);
return new Promise((res, rej) => QRCode.toCanvas(canvas, data, {}, err => !err ? res(canvas) : rej(err)));
}
QRのバージョン、誤り訂正レベル、モードなどは、入力されるデータによってライブラリが自動で決定してくれます。
ライブラリがコールバック仕様なので、利便性のためにPromise
でラップしています。
サンプルHTML
qr.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<script src="https://cdn.jsdelivr.net/npm/qrcode@latest/build/qrcode.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsqr@latest/dist/jsQR.min.js"></script>
<title>QR</title>
</head>
<body>
<video id="video" width="320" height="480" autoplay></video>
<textarea id="result" readonly></textarea>
<canvas id="canvas" width="240" height="240"></canvas>
<textarea id="data"></textarea>
</body>
<script>
function qrParse(video){
const canvas = new OffscreenCanvas(240, 320);
const render = canvas.getContext("2d");
return new Promise((res)=>{
const loop = setInterval(()=>{
render.drawImage(video, 0, 0, canvas.width, canvas.height);
const img = render.getImageData(0, 0, canvas.width, canvas.height);
const result = jsQR(img.data, img.width, img.height);
if(result){
clearInterval(loop);
return res(result.data);
}
}, 100);
});
}
function qrGenerate(data){
const canvas = new OffscreenCanvas(1, 1);
return new Promise((res, rej) => QRCode.toCanvas(canvas, data, {}, err => !err ? res(canvas) : rej(err)));
}
document.getElementById("data").addEventListener("change", async({target})=>{
document.getElementById("canvas").getContext("bitmaprenderer").transferFromImageBitmap((await qrGenerate(target.value)).transferToImageBitmap());
});
(async()=>{
const video = document.getElementById("video");
video.srcObject = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
facingMode: "environment"
}
});
document.getElementById("result").value = await qrParse(video);
})();
</script>
</html>
私のGitHub Pagesで試せます。