react-webcamでビデオ映像を非表示とした状態で撮影をする

2021.08.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

react-webcamを使用すると、Reactアプリにカメラ撮影機能を簡単に実装することができます。

今回は、react-webcamでビデオ映像を非表示とした状態で撮影をしてみました。

react-webcam使用時に表示されるビデオ映像について

前回の記事でもreact-webcamでカメラアプリを作成しましたが、その時にはビデオ映像を常に表示していました。

任意のタイミングの静止画をカメラで撮影したいだけであればビデオ映像の画面表示は一見不要そうですが、これはアプリがカメラで撮影している映像や静止画のデータを取得するためには画面表示されたVideo要素(<video>)に対してキャプチャを行う必要があるというHTMLの仕様があるためです。

しかしこのビデオ映像の表示はアプリ実装においては不要の場合が多いと思います。そのため今回ビデオ映像を非表示とした状態での撮影(キャプチャ取得)を行ってみました。

デモ

上記デモで[開始]をクリックするとカメラが起動します。カメラ撮影の権限を求められたら許可してください。(撮影したデータの外部への送信は行っていません)

するとボタンが表示されるので[キャプチャ]をクリックします。

するとキャプチャが取得されアプリに表示できました。

コード概要

今回のコードで重要なのはWebcamコンポーネントに適用しているスタイルです。

src/App.tsx

import { useRef, useState, useCallback } from "react";
import Webcam from "react-webcam";
import { makeStyles } from "@material-ui/core/styles";
import "./styles.css";

const useStyles = makeStyles(() => ({
  webcam: {
    position: "absolute",
    top: "0px",
    left: "0px",
    visibility: "hidden",
  },
}));

const videoConstraints = {
  width: 720,
  height: 360,
  facingMode: "user",
};

export const App = () => {
  const classes = useStyles();

  const [isCaptureEnable, setCaptureEnable] = useState<boolean>(false);
  const webcamRef = useRef<Webcam>(null);
  const [url, setUrl] = useState<string | null>(null);
  const capture = useCallback(() => {
    const imageSrc = webcamRef.current?.getScreenshot();
    if (imageSrc) {
      setUrl(imageSrc);
    }
  }, [webcamRef]);

  return (
    <>
      <header>
        <h1>カメラアプリ(ビデオ非表示)</h1>
      </header>
      {isCaptureEnable || (
        <button onClick={() => setCaptureEnable(true)}>開始</button>
      )}
      {isCaptureEnable && (
        <>
          <div>
            <button onClick={() => setCaptureEnable(false)}>終了</button>
          </div>
          <div>
            <Webcam
              audio={false}
              width={540}
              height={360}
              ref={webcamRef}
              screenshotFormat="image/jpeg"
              videoConstraints={videoConstraints}
              className={classes.webcam}
            />
          </div>
          <button onClick={capture}>キャプチャ</button>
        </>
      )}
      {url && (
        <>
          <div>
            <button
              onClick={() => {
                setUrl(null);
              }}
            >
              削除
            </button>
          </div>
          <div>
            <img src={url} alt="Screenshot" />
          </div>
        </>
      )}
    </>
  );
};

Webcamコンポーネントを配置した位置にはブラウザ側ではVideo要素が配置され、カメラで撮影している映像が表示されます。このWebcamコンポーネントに対して下記のスタイルを適用しています。

  webcam: {
    position: "absolute",
    top: "0px",
    left: "0px",
    visibility: "hidden"
  }

position: "absolute"top: "0px"left: "0px"によりVideo要素を(1画面アプリの場合)必ず画面内に配置されるようにし、かつ他のコンポーネントの配置に影響を与えないようにしています。またvisibility: "hidden"によりVideo要素を非表示にして不必要に見えないようにしています。

Video要素が画面外に配置された場合

スタイルでtop: "1000px"のようにしてVideo要素が画面外に配置されるようにします。

  webcam: {
    position: "absolute",
    top: "1000px",
    left: "0px",
    visibility: "hidden"
  }

この状態でキャプチャを取得すると真っ黒な画像となり正常にキャプチャが取得できません。

このことからも、react-webcamで(というよりはHTMLにおいて)キャプチャを取得する場合はキャプチャ元のVideo要素が画面内にある必要があるというのが分かります。

おわりに

react-webcamでビデオ映像を非表示とした状態で撮影をしてみました。

当初はWebcamコンポーネントのプロパティでVideoの表示/非表示が制御できると思っていましたが、そのようなものは無かったため今回こうしてスタイルを活用して非表示するに至りました。

参考

以上