ブラウザでQRコード読込と生成

ブラウザでQRコードを読み込んで解析したり、QRコード画像を生成したりする方法のメモです。 今回のコードは、2019/06/25時点でおそらく最新のChromeブラウザ (v75.0.3770.100) とFifeFox (v67.0.3) で正常に動くことを確認しております。IEでは試してません笑。

目次

QRコードの読込

まずはQRコードを読み込みにはjsQRモジュールを利用します:

<script src="https://cdn.jsdelivr.net/npm/jsqr@1.1.1/dist/jsQR.min.js" integrity="sha384-i4Tuh5Z0ns/3M0289mSougur8irvedWPBlwOcJ7ob5AK/rvN5tjkwzu7P1k1dThG" crossorigin="anonymous"></script>

<!-- jqueryもロード。 NOTE: QRコード処理自体には無関係です。 -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

画像ファイルから読込

QRコード画像選択のファイルinput要素を設置:

<input type="file" accept="image/png,image/jpg,image/jpeg,image/gif" onchange="scan(event)">

ファイルinput要素で入力されたQRコード画像ファイルを読み込み、解析文字列をコンソール出力する処理:

function scan(e) {
  const files = e.target.files || e.dataTransfer.files;
  if (!files.length) return;
  const file = files[0];
  const fileReader = new FileReader();
  fileReader.onload = function(theFile) {
    const image = new Image();
    image.onload = function() {
      // create a canvas in memory:
      const canvas = document.createElement('canvas');
      // canvas needs enough width and height to draw the qrcode image:
      canvas.width = this.width;
      canvas.height = this.height;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(image, 0, 0);
      const imageData = ctx.getImageData(0, 0, this.width, this.height);
      const data = jsQR(imageData.data, imageData.width, imageData.height);
      if (data) {
        const message = data.data;
        console.log("message:", message); // message: "あいうえお"
      }
    };
    const dataURL = theFile.target.result;
    if (!dataURL || !dataURL.startsWith("data:image/")) {
      alert("[ERROR] 読み取りできませんでした。");
    }
    image.src = dataURL;
  };
  fileReader.readAsDataURL(file);
}

Webカメラから読込

Webカメラ起動用のbutton要素、Webカメラ用のvideo要素、そしてスナップショット画像一時保存用のcanvas要素を設置:

<button type="button" onclick="openWebcam(event)">Webカメラ起動</button>
<div id="pane-webcam">
  <video name="video" style="max-height:300px; display: none;" autoplay></video>
  <canvas name="canvas" style="max-height: 300px; display: none;"></canvas>
</div>

Webカメラ起動処理、カメラからのストリーム画像処理、Webカメラの停止処理:

// Webカメラの起動&ストリーム読込開始処理
function openWebcam(e) {
  // related elements:
  const $root = $("#pane-webcam");
  const canvas = $root.find("[name=canvas]")[0];
  const video = $root.find("[name=video]").show()[0];
  const ctx = canvas.getContext('2d');
  // open webcam device
  navigator.mediaDevices.getUserMedia({
    audio: false,
    video: true,
  }).then(function(stream) {
    video.srcObject = stream;
    video.onloadedmetadata = function(e) {
      video.play();
      self.snapshot({ video, canvas, ctx, });
    };
  }).catch(function(e) {
    alert("ERROR: Webカメラの起動に失敗しました: " + e.message);
  });
}

// 読み込んだストリームからスナップショットを取得&解析
function snapshot({ video, canvas, ctx, }) {
  const self = this;
  if (!video.srcObject.active) return;
  // Draws current image from the video element into the canvas
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  const data = jsQR(imageData.data, imageData.width, imageData.height);
  if (!data) {
      // QRコードのスナップショット画像を解析できるまでリトライ・・・
    setTimeout(() => {
      return self.snapshot({ video, canvas, ctx, }); // retry ...
    }, 800); // NOTE: ここを小さくしすぎるとCPUに負荷が掛かります
  } else {
      // 解析成功!
    if (data) {
      const message = data.data; // QRコードからメッセージを取得
      console.log("message:", message);
    }
    // Webカメラの停止
    self.stopWebcam({ video, canvas, ctx, });
  }
}

// Webカメラの停止処理
function stopWebcam({ video, canvas, ctx }) {
  const self = this;
  if (!video) {
    video = $("[name=video]")[0];
  }
  video.pause();
  stream = video.srcObject;
  // self.stream.getVideoTracks()[0].stop();
  stream.getTracks().forEach(track => track.stop());
  video.src = "";
  $(video).hide();
}

QRコードの生成

QRコードの生成にはnode-qrcodeモジュールを利用します:

<script src="https://cdn.jsdelivr.net/npm/qrcode@1.3.2/build/qrcode.min.js" integrity="sha256-I8UYV49P3IKHrVzMUvI9RLtciRSLE0E6oQv1rFM1OOM=" crossorigin="anonymous"></script>

QRコード画像を表示するimg要素を設置:

<img id="qrcode" src="" />

"https://memo.appri.me/"という文字列をQRコード化したDataURLを取得し、img要素のsrc属性にセットすることでQRコードが画面に表示されます:

QRCode.toDataURL("https://memo.appri.me/", function(err, dataurl) {
  if (err) {
    alert(err.message);
    return;
  }
  console.log("result:", dataurl); // "data:image/png;base64,iVBORw..."
  // img要素にDataURLをセット:
  document.querySelector("#qrcode").setAttribute("src", dataurl);
});

ブラウザで使うメリット

jsQRやqrcodeモジュールはもちろんNode.jsでも使えます。ですが、せっかくブラウザでもここまで出来るのであれば、そこそこ負荷の掛かるQRコード読込&生成処理は全てブラウザ側に任せた方がサーバリソース的にもメリットがあると思います。

ひとこと

芽萌丸ツールズのQRコードツールというサービスは、今回のQRコード処理の参考になると思います。

以上です。

PR:時事ネタチェックの時間節約!
芽萌丸プログラミング部 @programming
プログラミング関連アカウント。Web標準技術を中心に書いていきます。フロントエンドからサーバサイドまで JavaScript だけで済ませたい人たちの集いです。