5
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

html+javascriptだけで実装したシンプルなQRコードリーダー

Last updated at Posted at 2021-04-30

目的

html+javascriptだけで実装されたシンプルなQRコードリーダーのサンプル(が欲しかった)を作る。

機能

  1. スマホのカメラやWebカメラでQRコードを値を取得する。
  2. QRコードを認識したら、赤枠を表示する。

qr.png

処理概要

  1. <video>タグでカメラ映像を表示
  2. タイマーでカメラ映像をイメージ化、QR認識ライブラリjsQRに引き渡す
  3. 認識すると、コードの値とQRコードがある領域の座標を取得します。
  4. 2.に戻り認識を繰り返します。

利用モジュール

jsQR.jsファイルを使います。https://github.com/cozmo/jsQR/tree/master/dist/jsQR.js

使い方

下記ファイルを同一フォルダに配置して、ブラウザ(スマホの方が使いやすい)から開きます(要Webカメラ)。

  1. index.html
  2. jsQR.js

※スマホの場合, httpsで開く必要があります


ソース解説

全体のソースは最後に載せてあります。

  1. カメラ動画を表示しながら、
  2. タイマーで画像に変換
  3. 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
5
9
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
5
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?