2
1

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 1 year has passed since last update.

TypeScriptで"手っ取り早く"ブラウザ搭載のBarcodeDetectorを使いたい

Posted at

BarcodeDetectorとは?

一部のブラウザ(AndroidのChrome等)に標準搭載されている1次元/2次元(QRコード)のバーコード読み取り機能です。
現時点では"Experimental"(実験的な機能)との事です。
今の所で使えそうなのはAndroidな感じですね。

※対応ブラウザ等、詳しくは下記のMDNの解説ページをご確認下さい。

・MDNのBarcodeDetectorページ

前提条件

要HTTPSです。

使い方?

const detector = new window.BarcodeDetector();
const list = await detector.detect(imagedata, canvas.width, canvas.height);

まずwindowからBarcodeDetectorを生成して...
ですが、TypeScriptではBarcodeDetector()部分がエラーとなります。

#正式な機能になったらサポートされるんですよね? きっと...

"手っ取り早く"使えるようにする!

windowに存在しないBarcodeDetector()を、とりあえず存在する事にしましょう。
という訳で同じソースファイル上に、

//windowにBarcodeDetectorを生やす
declare global {
  interface Window {
      BarcodeDetector: any;
  }
}

...を追加します。
ここは"手っ取り早く"なのでanyでお茶を濁します!

するとBarcodeDetector()のエラーが消えました!

※DevelopersIOの解説記事が大変参考になりました!
 今回は記事中の"パターン4"の方法になります。
 もしご自身の環境で上手く行かないヨという場合は、他パターンに代えてお試し下さい m(__)m

TypeScript で window 直下にいろいろ生やしたりグローバル変数を定義する

改めて使い方!

//BarcodeDetector生成
const detector = new window.BarcodeDetector();

//BarcodeDetectorでimagedataを認識させる
//(imagedata,戻り値については上記MDNページのメソッド"detect()"を参照して下さい)
const list = await detector.detect(imagedata, canvas.width, canvas.height);

//認識できた場合、listに要素が入ります
//(ここではバーコードの内容をアラートで表示)
for (const detected of list) {
    alert(`${detected.rawValue}`);
}

ReactのApp.tsx全文

videoを回して200ms毎に判定させて居ます。
"終了はブラウザを閉じる"、"エラー処理なし"という動く環境でしか動かない適当コードですがご参考まで...(それでもチョット長い)

←クリックで展開(長いので畳みました)
App.tsx
import React, { useRef, useEffect, useCallback } from 'react';
import './App.css';

//windowにBarcodeDetectorを生やす
declare global {
    interface Window {
        BarcodeDetector: any;
    }
}

//横・縦幅(てきとう)
const width = 300;
const height = 300;

function App() {
    const videoRef = useRef<HTMLVideoElement>(null);

    async function checkImage() {
        let canvas = document.createElement("canvas");
        if (videoRef === null || videoRef.current === null) {
            return null;
        }

        const { videoWidth, videoHeight } = videoRef.current;

        canvas.width = videoWidth;
        canvas.height = videoHeight;

        const context = canvas.getContext("2d");

        if (context === null || videoRef.current === null) {
            return null;
        }

        context.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);

        if (canvas.width === 0) {
            setTimeout(() => { checkImage() }, 200);
            return null;
        }

        const imagedata = context.getImageData(0, 0, canvas.width, canvas.height);

        const detector = new window.BarcodeDetector();

        const list = await detector.detect(imagedata, canvas.width, canvas.height);

        let cnt = 0;

        //認識できた場合、listに要素が入ります
        //(ここではバーコードの内容をアラートで表示)
        for (const detected of list) {
            cnt++;
            alert(`[${cnt}/${list.length}]${detected.rawValue}`);
        }

        setTimeout(() => { checkImage() }, 200);
    }

    useEffect(() => {
        checkImage();
    }, []);

    //カメラのstreamを取得して返すメソッド
    const getStream = useCallback(async () => {
        const aspectRatio = width / height;

        return await navigator.mediaDevices.getUserMedia({
            video: {
                facingMode: "environment",
                width: {
                    ideal: 1024
                },
                aspectRatio
            },
            audio: false
        });
    }, [width, height]);

    useEffect(() => {
        let stream: MediaStream | null = null;
        let video = videoRef.current;

        //取得したstreamをvideo要素に流す
        const setVideo = async () => {
            stream = await getStream();

            if (video === null || !stream) {
                return;
            }

            video.srcObject = stream;
            video.play();
        };

        setVideo();

        //streamを停止させる
        const cleanupVideo = () => {
            if (!stream) {
                return;
            }

            stream.getTracks().forEach((track) => track.stop());

            if (video === null) {
                return;
            }

            video.srcObject = null;
        };
        return cleanupVideo
    }, [getStream]);

    return (
        <div className="App">
            <body>
                <video ref={videoRef} playsInline width={width} height={height} />
            </body>
        </div>
    );
}

export default App;

使ってみた感想

手元にあったLenovoやTECLASTのタブレットやMotorolaのスマホで試しましたが、反応は中々良いと思いました。

またタブレットの箱に貼ってあった1次元、QRコード合計5個を画面に収まるように入れて読ませた所、上手く解像するタイミングでは5個を一括で認識しました。

環境は限られますが、現時点でも遊べる印象です。
"実験的"では仕事では採用できないとは思いますが、引き続き注目して行きたいと思います。

早く"Experimental"が取れて、環境を気にせず使えるようになって欲しいです!

※個人の感想です。

参考(本文に登場しない分)

videoタグの使い方について、
dashi296さんの
Reactでカメラを使って画像を撮影するダイアログを実装してみた

ブラウザでのバーコード読み取りについて、
@kan_dai(TAM)さんの
Webの技術だけで作るQRコードリーダー
続・Webの技術だけで作るQRコードリーダー

...その他、色々な情報を参考にさせて頂きました m(__)m

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?