8
7

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.

JavaScriptでカメラ使うアプリケーションを実装するぞ!

Posted at

利用するぞ!

下のようなコードで cameraStreamでカメラ映像のmediaStreamが手に入る。
mediaDevices.getUserMediaに渡すオブジェクトでカメラに限らずデバイスに存在するメディアからなにをどう使うかを指定できる。
MediaDevices.getUserMedia() - Web API | MDN


const cameraStream = await mediaDevices.getUserMedia({
  video: {
    facingMode: 'environment',

    aspectRatio: {
      exact: 1.6,
    },
  },
  audio: false,
});

上のコードだとfacingMode: 'environment'で背面カメラ、aspectRatioでカメラのアスペクト比を指定している。

カメラの幅をwidthやheightで指定したときにはクセのある挙動をするので、詳しい方の解説を読んだ方が良い getUserMedia()で指定できるMediaTrackConstraintsのよもやま - console.lealog();

videoタグでカメラの様子をうつす

突然HTMLだが、このままでサクっと動くはず。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <script>
      async function startCamera() {
        const cameraStream = await navigator.mediaDevices.getUserMedia({
          video: {
            facingMode: 'environment',

            aspectRatio: {
              exact: 1.6,
            },
          },
          audio: false,
        });
        const videoElement = document.createElement("video")
        videoElement.autoplay = true;
        videoElement.srcObject = cameraStream;
        document.body.append(videoElement);
        videoElement.addEventListener("resize", () => {
          videoElement.width = videoElement.videoWidth;
          videoElement.height = videoElement.videoHeight;
        });
      }
      startCamera()
    </script>
  </body>
</html>



videoElement.autoplay = true;
videoElement.srcObject = cameraStream;

srcObjectにmediaStreamをセットすればvideoタグにカメラで写している範囲が再生される。

カメラから画像をキャプチャする


function captureImage(videoElement) {
	const canvas = document.createElement('canvas');
	const context = canvas.getContext('2d');
	if (context === null) {
	  throw new Error("canvas.getContext('2d') return null");
	}

	canvas.width = videoElement.videoWidth;
	canvas.height = videoElement.videoHeight;
	context.drawImage(this.videoElement, 0, 0, canvas.width, 		canvas.height);
	return canvas.toDataURL('image/jpeg', 0.95);
}

const capturedImageUrl = captureImage(videoElement)

document.getElementsByTagName("img").src = capturedImageUrl

canvasのdrawImageにvideo要素を渡し、canvasからdata-urlとして画像を抜き出すことができる。

よしこれで最高のアプリをつくるぞ!!!

ちょっとまって!そのまえに!

カメラが起動できない状況を判別する

スクリプトのカメラ起動をユーザは承認/否認することができる。
承認されたときはうまくいきそうだけど、否認された時はどうなるんだろう?
ちゃんとハンドリングしたいよね。



function isPermissionDenied(err: Error): boolean {
  return (
    err.message.includes('Permission denied') || // for chrome
    err.message.includes('denied permission.') // for safari
  );
}

try {
	const stream = mediaDevices.getUserMedia(constraints);
} catch (e) {
	if (isPermissionDenied(e)) {
		alert("カメラ利用を承認してもらえないと利用できないんです!")
	}
	// and so on
}

Errorのもつメッセージから判別する他方法がない(ハズ)なのでerr.message.includes(...)で判定している。

カメラを切断する

接続したら切断まで面倒を見よう。


function cameraStop(videoElement, cameraStream.getTracks) {
	videoElement.pause();
	videoElement.src = '';
	cameraStream.getTracks()[0].stop();
}

cameraStream.getTracks()[0].stop();

でカメラは切断されます。

とくに気にしておきたいのはスクリプトが存在するページからブラウザのタブのフォーカスが離れたときに切断するか否か。


const visibleHandler = () => {
  if (document.visibilityState === 'hidden') {
   	cameraStop(videoElement, cameraStream.getTracks);
  }
};
document.addEventListener('visibilitychange', visibleHandler);

タブが非アクティブになったときにはカメラを切断させるようなら上のようにvisibilitychange イベントをトリガーにできる。

おわりに

タブが非アクティブになったらカメラは閉じるべきか否か、決めよう。
見てないページで思いがけずカメラが起動しているとびっくりしちゃうね。

8
7
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
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?