利用するぞ!
下のようなコードで 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
イベントをトリガーにできる。
おわりに
タブが非アクティブになったらカメラは閉じるべきか否か、決めよう。
見てないページで思いがけずカメラが起動しているとびっくりしちゃうね。