WebRTCを使ってブラウザでQRコードを読み取る

ブラウザで#QRコードを読み取る方法を模索している。
以下の記事が参考になりました。

[HTML5 JavaScript QR WebRTC]JavaScriptでQRコードリーダーを作ってみる | MD-Blog | MONSTER DIVE

早速、#jsQRを素振りしてみる。

cozmo/jsQR: A pure javascript QR code reading library. This library takes in raw images and will locate, extract and parse any QR code found within.

デモサイトがあったので、開いて確認してみる。

QRコードのサンプルとして、以下のサイトを開いて、テストをする。

QRコード例 – エクスサポート情報サイト

無事、認識できた。

スクリーンショット
スクリーンショット

手元で動かしたかったので、HTMLをコピーして、若干修正して、以下のHTMLを保存して開いて確認する。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>jsQR Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jsQR.min.js"></script>
  <link href="https://fonts.googleapis.com/css?family=Ropa+Sans" rel="stylesheet">
  <style>
    body {
      font-family: 'Ropa Sans', sans-serif;
      color: #333;
      max-width: 640px;
      margin: 0 auto;
      position: relative;
    }

    #githubLink {
      position: absolute;
      right: 0;
      top: 12px;
      color: #2D99FF;
    }

    h1 {
      margin: 10px 0;
      font-size: 40px;
    }

    #loadingMessage {
      text-align: center;
      padding: 40px;
      background-color: #eee;
    }

    #canvas {
      width: 100%;
    }

    #output {
      margin-top: 20px;
      background: #eee;
      padding: 10px;
      padding-bottom: 0;
    }

    #output div {
      padding-bottom: 10px;
      word-wrap: break-word;
    }

    #noQRFound {
      text-align: center;
    }
  </style>
</head>
<body>
<h1>jsQR Demo</h1>
<a id="githubLink" href="https://github.com/cozmo/jsQR">View documentation on Github</a>
<p>Pure JavaScript QR code decoding library.</p>
<div id="loadingMessage">🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>
<canvas id="canvas" hidden></canvas>
<div id="output" hidden>
  <div id="outputMessage">No QR code detected.</div>
  <div hidden><b>Data:</b> <span id="outputData"></span></div>
</div>
<script>
  var video = document.createElement('video');
  var canvasElement = document.getElementById('canvas');
  var canvas = canvasElement.getContext('2d');
  var loadingMessage = document.getElementById('loadingMessage');
  var outputContainer = document.getElementById('output');
  var outputMessage = document.getElementById('outputMessage');
  var outputData = document.getElementById('outputData');

  function drawLine(begin, end, color) {
    canvas.beginPath();
    canvas.moveTo(begin.x, begin.y);
    canvas.lineTo(end.x, end.y);
    canvas.lineWidth = 4;
    canvas.strokeStyle = color;
    canvas.stroke();
  }

  // Use facingMode: environment to attemt to get the front camera on phones
  navigator.mediaDevices.getUserMedia({video: {facingMode: 'environment'}}).then(function(stream) {
    video.srcObject = stream;
    video.setAttribute('playsinline', true); // required to tell iOS safari we don't want fullscreen
    video.play();
    requestAnimationFrame(tick);
  });

  function tick() {
    loadingMessage.innerText = '⌛ Loading video...';
    if (video.readyState === video.HAVE_ENOUGH_DATA) {
      loadingMessage.hidden = true;
      canvasElement.hidden = false;
      outputContainer.hidden = false;

      canvasElement.height = video.videoHeight;
      canvasElement.width = video.videoWidth;
      canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height);
      var imageData = canvas.getImageData(0, 0, canvasElement.width, canvasElement.height);
      var code = jsQR(imageData.data, imageData.width, imageData.height, {
        inversionAttempts: 'dontInvert',
      });
      if (code) {
        drawLine(code.location.topLeftCorner, code.location.topRightCorner, '#FF3B58');
        drawLine(code.location.topRightCorner, code.location.bottomRightCorner, '#FF3B58');
        drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, '#FF3B58');
        drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, '#FF3B58');
        outputMessage.hidden = true;
        outputData.parentElement.hidden = false;
        outputData.innerText = code.data;

        // 追加
        if (true) {
          if (code.data) {
            console.log(`QRコードの値は「${code.data}」です。`);
          }
        }

      } else {
        outputMessage.hidden = false;
        outputData.parentElement.hidden = true;
      }
    }
    requestAnimationFrame(tick);
  }
</script>
</body>
</html>

確認のため、追加したコードが無事動きました。これで実装できそうです。

スクリーンショット
スクリーンショット