LoginSignup
31
41

More than 1 year has passed since last update.

jsQRを使ってブラウザQRコードリーダーを作る【技術メモ】【小ネタ】

Last updated at Posted at 2022-10-26

image.png

はじめに

株式会社マイスター・ギルド新規事業部のウサギーです。
弊社新規事業部では、新規サービスの立ち上げを目指して
日々、アイディアの検証やプロトタイプの作成を行っています。

今回はブラウザで使えるQRコードリーダーをさっくり作りたいな~と思いjsQRを使ってみました。

jsQRとは?

javascriptで作られたQRコード読み取りライブラリです。
公式ページ https://github.com/cozmo/jsQR
デモページ https://cozmo.github.io/jsQR/

↓ デモページを試した図
image.png

使用方法

公式のDemoページと同じようなものを作ってみます。

1 jsQR.jsを使う準備

jsQR.jsをDLしてプロジェクトに含めます。
image.png

HTMLにjsQR.jsを読み込みます。

index.html
<script src="./jsQR.js"></script>

2 表示の準備

・QR読み込みのためのカメラ映像を表示する<video>要素
・<video>要素をもとにカメラ映像を表示する<canvas>要素
・QRコードの周りに赤枠を表示するための<canvas>要素
を準備します。

index.html
<div id="wrapper">
    <video id="video" autoplay muted playsinline></video>
    <canvas id="camera-canvas"></canvas>
    <canvas id="rect-canvas"></canvas>
</div>

次で説明しますが、jsQRを使ってQRコードを検出するときのinputソースにvideo要素が使えないため
カメラ映像を表示用の<canvas>要素も用意しています。

これらを重ねて表示したい&video要素は見えなくてもよいので、cssは次のように用意しました。

style.css
#wrapper{
    position: relative;
}

#video{
    position: absolute;
    top: 0px;
    left: 0px;
    visibility: hidden;
}

#camera-canvas{
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 50;
}

#rect-canvas{
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 100;
}

3 カメラ映像の表示

script.js
// Webカメラの起動
const video = document.getElementById('video');
let contentWidth;
let contentHeight;

const media = navigator.mediaDevices.getUserMedia({ audio: false, video: {width:640, height:480} })
   .then((stream) => {
      video.srcObject = stream;
      video.onloadeddata = () => {
         video.play();
         contentWidth = video.clientWidth;
         contentHeight = video.clientHeight;
         canvasUpdate(); // 次で記述
         checkImage(); // 次で記述
      }
   }).catch((e) => {
      console.log(e);
   });
script.js
// カメラ映像のキャンバス表示
const cvs = document.getElementById('camera-canvas');
const ctx = cvs.getContext('2d');
const canvasUpdate = () => {
   cvs.width = contentWidth;
   cvs.height = contentHeight;
   ctx.drawImage(video, 0, 0, contentWidth, contentHeight);
   requestAnimationFrame(canvasUpdate);
}

4 QRコードの検出

公式Usage
const code = jsQR(imageData, width, height, options?);

if (code) {
  console.log("Found QR code", code);
}

QRコードの検出は上記のコードを使います。
jsQRメソッドの第一引数が「 imageData 」なので、<canvas>要素を用意した訳です。

script.js
// QRコードの検出
const rectCvs = document.getElementById('rect-canvas');
const rectCtx =  rectCvs.getContext('2d');
const checkImage = () => {
   // imageDataを作る
   const imageData = ctx.getImageData(0, 0, contentWidth, contentHeight);
   // jsQRに渡す
   const code = jsQR(imageData.data, contentWidth, contentHeight);

   // 検出結果に合わせて処理を実施
   if (code) {
      console.log("QRcodeが見つかりました", code);
      drawRect(code.location);
   } else {
      console.log("QRcodeが見つかりません…", code);
      rectCtx.clearRect(0, 0, contentWidth, contentHeight);
   }
   setTimeout(()=>{ checkImage() }, 500);
}
script.js
// 四辺形の描画
const drawRect = (location) => {
   rectCvs.width = contentWidth;
   rectCvs.height = contentHeight;
   drawLine(location.topLeftCorner, location.topRightCorner);
   drawLine(location.topRightCorner, location.bottomRightCorner);
   drawLine(location.bottomRightCorner, location.bottomLeftCorner);
   drawLine(location.bottomLeftCorner, location.topLeftCorner)
}

// 線の描画
const drawLine = (begin, end) => {
   rectCtx.lineWidth = 4;
   rectCtx.strokeStyle = "#F00";
   rectCtx.beginPath();
   rectCtx.moveTo(begin.x, begin.y);
   rectCtx.lineTo(end.x, end.y);
   rectCtx.stroke();
}

image.png

これで、検出部はできました!

5 QRコードの情報取得

QRコードの情報も取り出してみます。

表示部を用意

index.html
<span id="qr-msg">QRコード: 見つかりません</span>
style.css
#qr-msg{
    position: absolute;
    top: 500px;
    left: 0px;
}
script.js
// QRコードの検出
const rectCvs = document.getElementById('rect-canvas');
const rectCtx =  rectCvs.getContext('2d');
const checkImage = () => {
   // imageDataを作る
   const imageData = ctx.getImageData(0, 0, contentWidth, contentHeight);
   // jsQRに渡す
   const code = jsQR(imageData.data, contentWidth, contentHeight);

   // 検出結果に合わせて処理を実施
   if (code) {
      console.log("QRcodeが見つかりました", code);
      drawRect(code.location);
+     document.getElementById('qr-msg').textContent = `QRコード:${code.data}`;
   } else {
      console.log("QRcodeが見つかりません…", code);
      rectCtx.clearRect(0, 0, contentWidth, contentHeight);
+     document.getElementById('qr-msg').textContent = `QRコード: 見つかりません`;
   }
   setTimeout(()=>{ checkImage() }, 500);
}

image.png
情報も出るようになりました。

コード全体

index.html
index.html
<!DOCTYPE html>
<head>
    <title>QRコードのテスト</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
        <div id="wrapper">
            <video id="video" autoplay muted playsinline></video>
            <canvas id="camera-canvas"></canvas>
            <canvas id="rect-canvas"></canvas>
            <span id="qr-msg">QRコード: 見つかりません</span>
        </div>
    <script src="./jsQR.js"></script>
    <script src="./script.js"></script>
</body>
</html>
style.css
style.css
#wrapper{
    position: relative;
}

#video{
    position: absolute;
    top: 0px;
    left: 0px;
    visibility: hidden;
}

#camera-canvas{
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 50;
}

#rect-canvas{
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 100;
}

#qr-msg{
    position: absolute;
    top: 500px;
    left: 0px;
}
script.js
script.js
// Webカメラの起動
const video = document.getElementById('video');
let contentWidth;
let contentHeight;

const media = navigator.mediaDevices.getUserMedia({ audio: false, video: {width:640, height:480} })
   .then((stream) => {
      video.srcObject = stream;
      video.onloadeddata = () => {
         video.play();
         contentWidth = video.clientWidth;
         contentHeight = video.clientHeight;
         canvasUpdate();
         checkImage();
      }
   }).catch((e) => {
      console.log(e);
   });

// カメラ映像のキャンバス表示
const cvs = document.getElementById('camera-canvas');
const ctx = cvs.getContext('2d');
const canvasUpdate = () => {
   cvs.width = contentWidth;
   cvs.height = contentHeight;
   ctx.drawImage(video, 0, 0, contentWidth, contentHeight);
   requestAnimationFrame(canvasUpdate);
}

// QRコードの検出
const rectCvs = document.getElementById('rect-canvas');
const rectCtx =  rectCvs.getContext('2d');
const checkImage = () => {
   // imageDataを作る
   const imageData = ctx.getImageData(0, 0, contentWidth, contentHeight);
   // jsQRに渡す
   const code = jsQR(imageData.data, contentWidth, contentHeight);

   // 検出結果に合わせて処理を実施
   if (code) {
      console.log("QRcodeが見つかりました", code);
      drawRect(code.location);
      document.getElementById('qr-msg').textContent = `QRコード:${code.data}`;
   } else {
      console.log("QRcodeが見つかりません…", code);
      rectCtx.clearRect(0, 0, contentWidth, contentHeight);
      document.getElementById('qr-msg').textContent = `QRコード: 見つかりません`;
   }
   setTimeout(()=>{ checkImage() }, 500);
}

// 四辺形の描画
const drawRect = (location) => {
   rectCvs.width = contentWidth;
   rectCvs.height = contentHeight;
   drawLine(location.topLeftCorner, location.topRightCorner);
   drawLine(location.topRightCorner, location.bottomRightCorner);
   drawLine(location.bottomRightCorner, location.bottomLeftCorner);
   drawLine(location.bottomLeftCorner, location.topLeftCorner)
}

// 線の描画
const drawLine = (begin, end) => {
   rectCtx.lineWidth = 4;
   rectCtx.strokeStyle = "#F00";
   rectCtx.beginPath();
   rectCtx.moveTo(begin.x, begin.y);
   rectCtx.lineTo(end.x, end.y);
   rectCtx.stroke();
}

おまけ

QRコードでなんか遊べないかなぁ、と思って
ドレミファソラシドの音が出るプログラムを作ってみました。
「C4」~「C5」のQRコードを作ってかざすと音が出ます。

script.js
   // 検出結果に合わせて処理を実施
   if (code) {
      console.log("QRcodeが見つかりました", code);
      drawRect(code.location);
      document.getElementById('my-qr-msg').textContent = `QRコード:${code.data}`;
+     playPitche(code.data);
   } 
script.js
+// 指定周波数音を出す
+let audioCtx = new AudioContext();
+const playMelody = (hz) => {
+    let osc = audioCtx.createOscillator();
+    osc.frequency.value = hz;
+    let audDes = audioCtx.destination;
+    osc.connect(audDes);
+    osc.start = osc.start || osc.noteOn;
+    osc.start();
+    setTimeout(function() { osc.stop(0);}, 200);
+}

+// ドレミファソラシドを鳴らす
+const playPitche = (name) => {
+    switch(name){
+        case 'C4':
+            playMelody(261);
+            break;
+        case 'D4':
+            playMelody(294);
+            break;
+        case 'E4':
+            playMelody(330);
+            break;
+        case 'F4':
+            playMelody(349);
+            break;
+        case 'G4':
+            playMelody(392);
+            break;
+        case 'A4':
+            playMelody(440);
+            break;
+        case 'B4':
+            playMelody(494);
+            break;
+        case 'C5':
+            playMelody(522);
+            break;
+    }
+}

31
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
41