[React] React Webcam のカメラ映像を canvas に表示してみる

前回は img タグに表示してみました。今回は canvas タグに表示してみます。
img タグと比べると、少し工夫が必要になります。

準備

React Webcam のインストールなどはこちら。

カメラ映像から画像を取得して表示する

写真を撮るようなイメージで、ボタンを押したときの映像を canvas に表示してみます。
drawImage を使いたいので、new Image() で img要素を作り、onload を待つのがポイントです。
いつものように App.js を編集します。

import Webcam from "react-webcam";
import React, { useEffect, useRef, useState } from 'react';
import './App.css';
import Dropdown from "./components/Dropdown";
import Button from "./components/Button";

// const curried = (func, ...other) => (x => func(x, ...other));

function App() {

  const [cameras, setCameras] = useState([]);
  const [camera, setCamera] = useState(null);

  const webcamRef = useRef(null);
  const canvasRef = useRef(null);

  useEffect(() => {
    navigator.mediaDevices.enumerateDevices().then(mediaDevices => {
      const devices = mediaDevices.filter(({ kind }) => kind === "videoinput");
      setCameras(devices);
      if (devices.length) {
        setCamera(devices[0]);
      }
    })
  }, [])

  const handleClick = (cb) => {
    const imageSrc = webcamRef?.current?.getScreenshot({ width: webcamRef.current.video.videoWidth, height: webcamRef.current.video.videoHeight });
    if (!imageSrc) {
      cb();
      return;
    }

    const img = new Image();
    img.src = imageSrc;
    img.onload = () => {
      const context = canvasRef?.current?.getContext('2d');
      context?.drawImage(img, 0, 0);
      cb();
    }
  };

  return (
    <>
      <div className="bg-dark" style={{ minHeight: '100vh' }}>
        <div className="p-5 text-center">
          <Dropdown
            className="mb-4"
            variant="success"
            title={camera ? camera?.label : 'カメラが見つかりませんでした。'}
            items={cameras?.map(_camera => {
              return {
                children: _camera?.label,
                className: _camera?.deviceId === camera?.deviceId ? 'disabled' : '',
                onClick: () => setCamera(_camera),
              };
            })}
          />
          <div className="d-flex justify-content-center align-items-center">
            <Webcam
              ref={webcamRef}
              videoConstraints={{
                width: 320,
                height: 240,
                deviceId: camera?.deviceId,
              }}
              className="border border-light"
              style={{ width: 320, height: 240, boxSizing: 'content-box' }}
            />
            <Button className="mx-4" onClick={handleClick}>→</Button>
            <canvas
              className="border border-light"
              ref={canvasRef}
              style={{ boxSizing: 'content-box' }}
              width={320}
              height={240}
            />
          </div>
        </div>
      </div>
    </>
  );
}

export default App;

動かしてみます。
左がカメラ、右がcanvasタグ。(といっても見た目は img タグで作ったものと変わりません💦)

はい、できました。


ここにコードがないコンポーネントは、過去の記事にあります ('◇’)ゞ