10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

【Angular】ブラウザでカメラを利用する

Angular アプリケーションでデバイスのカメラを利用する方法を記載します。

Angular でのカメラ利用は Navigator.mediaDevices を利用すれば実現できます。

各ブラウザの対応状況については以下を参照下さい。 https://developer.mozilla.org/ja/docs/Web/API/Navigator/mediaDevices

1. <video>を用意する。

まずはHTML側に<video> を配置します。

そして、それぞれのElementをViewChildとして定義しておきます。

HTML

<video id="camera" #camera autoplay></video>

TS

@ViewChild('camera') camera?: ElementRef;
2. カメラを起動する処理を追加

次にカメラ起動に関する処理を記載します。

カメラを起動するには MediaDevices.getUserMedia() を利用します。

getUserMedia() の引数として MediaStreamConstraintsを定義しておきます。(引数に直接指定しても問題ないです。)

constraints: MediaStreamConstraints = {
    audio: false,
    video: {
        //  environment : Rear camera / user : Self-view camera
        facingMode: 'environment' as VideoFacingModeEnum,
    },
};

<video>のsrcObjectに対し取得したMediaStreamをセットします。

カメラを起動する処理は AfterViewInit 等のタイミングで呼び出せば良いと思います。

if (!navigator.mediaDevices) {
    // 【navigator.mediaDevicesが取得できない場合の処理】
    return;
}
navigator.mediaDevices
    .getUserMedia(this.constraints)
    .then((stream: MediaStream) => {
        if (this.camera) {
            this.camera.nativeElement.srcObject = stream;
        }
    })
    .catch((error) => {
        // 【エラーが発生した場合の処理】
    });
3. カメラを停止する処理を追加

OnDestroy 等カメラを停止したいタイミングで MediaStreamTrack.stop()をコールします。

if (this.camera?.nativeElement.srcObject) {
    const track = this.camera.nativeElement.srcObject.getTracks()[0] as MediaStreamTrack;
    track.stop();
}
4. 撮影処理を追加

これで基本的にカメラの起動/停止ができると思うので

後は撮影処理を実装すればカメラ機能として利用できます。

以下は撮影した画像をData URLとして取得する例です。(<canvas>を利用しています。)

HTML

<canvas id="canvas" #canvas></canvas>

TS

let width = this.camera?.nativeElement.clientWidth;
let height = this.camera?.nativeElement.clientHeight;

if (this.canvas) {
    const context = this.canvas.nativeElement.getContext('2d') as CanvasRenderingContext2D;
    this.canvas.nativeElement.width = width;
    this.canvas.nativeElement.height = height;
    const dataUrl = this.canvas.nativeElement.toDataURL(
        context.drawImage(this.camera?.nativeElement, 0, 0, width, height)
    );

    this.dialogRef.close(dataUrl);
}
注意点

ブラウザのカメラはセキュリティ上、基本的にHTTPSの場合かローカル(localhost)の場合のみ利用できるようになっており、 HTTPでは利用できません。(navigator.mediaDevices が取得できません。)

サンプル

以下サンプルとなります。

※ここではカメラをダイアログ表示(全画面)するものしMatDialogRefを入れています。

HTML

<video id="camera" #camera autoplay></video>
<canvas id="canvas" #canvas></canvas>

※<button>や<img>等 撮影ボタンや必要に応じて閉じるボタン等を配置

TS

export class CameraComponent implements AfterViewInit, OnDestroy {
    @ViewChild('camera') camera?: ElementRef;
    @ViewChild('canvas') canvas?: ElementRef;
    
    constraints: MediaStreamConstraints = {
        audio: false,
        video: {
            //  environment : Rear camera / user : Self-view camera
            facingMode: 'environment' as VideoFacingModeEnum,
        },
    };
    
    constructor(private dialog: MatDialog, private dialogRef: MatDialogRef<CameraComponent>) {}
    
    ngAfterViewInit(): void {
        this.startCamera();
    }

    ngOnDestroy(): void {
        this.stopCamera();
    }

    closeDialog() {
        this.dialogRef.close();
    }
    
    startCamera() {
        if (!navigator.mediaDevices) {
            alert('camera is not supported.');
            return;
        }
        navigator.mediaDevices
            .getUserMedia(this.constraints)
            .then((stream: MediaStream) => {
                if (this.camera) {
                    this.camera.nativeElement.srcObject = stream;
                }
            })
            .catch((error) => {
                // 【エラーが発生した場合の処理】
            });
    }
    
    stopCamera() {
        if (this.camera?.nativeElement.srcObject) {
            const track = this.camera.nativeElement.srcObject.getTracks()[0] as MediaStreamTrack;
            track.stop();
        }
    }
    
    shoot() {
        let width = this.camera?.nativeElement.clientWidth;
        let height = this.camera?.nativeElement.clientHeight;

        if (this.canvas) {
            const context = this.canvas.nativeElement.getContext('2d') as CanvasRenderingContext2D;
            this.canvas.nativeElement.width = width;
            this.canvas.nativeElement.height = height;
            const dataUrl = this.canvas.nativeElement.toDataURL(
                context.drawImage(this.camera?.nativeElement, 0, 0, width, height)
            );

            this.dialogRef.close(dataUrl);
        }
    }
}