[HTML5] カメラのフロントとリアを切り替える

スマホなどでカメラを操作する際に、リアとフロントのカメラをJavaScriptで切り替える簡単なサンプルです。

カメラのフロントとリアを切り替える

実行例

以下から実際のサンプルをお試しいただけます。 miku3.net

  • フロント、リアカメラが両方の付いた端末で実行してください。
    • 要はスマホですね。
    • 例えばフロントしか無い場合は、切り替えたタイミングでエラー表示が出ます

初回のアクセス時にWebブラウザから、このサイトにカメラの操作を許可して良いか聞かれますので「許可」ボタンをクリックしてください。「ブロック」ボタンを押すと設定を変更するのにメニューの少し深いところに潜る必要がありますのでご注意を。

サンプルコード

CSSは別ファイルにしています。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf8">
  <title>Camera Test</title>
  <link rel="stylesheet" href="style.css" type="text/css" media="all">
</head>
<body>

<div id="contents">
  <!-- カメラ映像 -->
  <video id="camera" width="640" height="480"></video>

  <!-- 切り替えボタン -->
  <button type="button" id="btn-toggle">
    <img src="icon/retweet-solid.svg" width="64" height="64">
  </button>
</div>

<script>
//---------------------------------------------
// グローバル変数
//---------------------------------------------
// カメラのデフォルト設定
var CONSTRAINTS = {
  audio: false,
  video: {
    width: 640,
    height: 480,
    facingMode: null  // どのカメラを利用するか

    // facingModeには最終的に以下のいずれかの値を入れる
    //   facingMode: "user"                    // フロントカメラを利用する
    //   facingMode: { exact: "environment" }  // リアカメラを利用する
  }
};

// 現在のStream
var curSTREAM = null;

//---------------------------------------------
// [event] ページ読み込み完了
//---------------------------------------------
window.onload = () => {
  const video = document.querySelector("#camera");
  let useFront = true;     // フロントカメラ:true, バックカメラ:false

  // 縦横の解像度を調整
  adjustCameraSize(video, 640, 480);

  // カメラと同期開始
  syncCamera(video, useFront);
  useFront = !useFront;         // boolean値を反転

  //-------------------------------
  // [event] 切り替えボタン押下
  //-------------------------------
  document.querySelector("#btn-toggle").addEventListener("click", ()=>{
    syncCamera(video, useFront);
    useFront = !useFront;      // boolean値を反転
  });
};

/**
 * カメラを<video>と同期
 *
 * @param {object} video
 * @param {boolean} [is_front=true]
 */
function syncCamera(video, is_front=true){
  // 前後カメラの設定
  CONSTRAINTS.video.facingMode = (is_front)?  "user":{ exact: "environment" };

  // すでにカメラと接続していたら停止
  if( curSTREAM !== null ){
    curSTREAM.getVideoTracks().forEach( (camera) => {
      camera.stop();
    });
  }

  // カメラと接続する
  navigator.mediaDevices.getUserMedia(CONSTRAINTS)
    .then( (stream) => {
      curSTREAM = stream;   // 前後の切り替え用に保持

      // <video>とStremaを接続
      video.srcObject = stream;
      video.onloadedmetadata = (e) => {
        video.play();
      };
    })
    .catch( (err) => {
      console.log(`${err.name}: ${err.message}`);
      alert("カメラとの接続時にエラーが発生しました");
    });
}

/**
 * 解像度に合わせて<video>サイズを調整する
 *
 * @param {object}  video
 * @param {integer} longside   長辺のピクセル数
 * @param {integer} shortside  短辺のピクセル数
 **/
function adjustCameraSize(video, longside, shortside){
  if( window.innerWidth < window.innerHeight ){
    // getUserMediaに食わせる値
    CONSTRAINTS.video.width  = shortside;
    CONSTRAINTS.video.height = longside;
    // videoタグのサイズ
    video.style.width  = shortside;
    video.style.height = longside;
  }
}
</script>
</body>
</html>

解説

フロントとリアの切り替え

カメラと接続する際にnavigator.mediaDevices.getUserMedia()を利用するわけですが、このとき facingModeの値にuserを指定すればフロントカメラ、{ exact: "environment" }ならリアカメラとなります。

フロントカメラ

navigator.mediaDevices.getUserMedia({
  audio: false,
  video: {
    width: 640, height: 480,
    facingMode: "user"
  }
})

リアカメラ

navigator.mediaDevices.getUserMedia({
  audio: false,
  video: {
    width: 640, height: 480,
    facingMode: { exact: "environment" }
  }
})

切り替えその前に

切り替え自体は簡単なんですが、注意すべきは直前のStreamを停止してから接続し直す点です。どんどんStreamが開いちゃいますからねw

ここではグローバル変数curSTREAMにstreamを記録しておき、

  navigator.mediaDevices.getUserMedia(CONSTRAINTS)
    .then( (stream) => {
      curSTREAM = stream;   // 前後の切り替え用に保持

2回目以降の接続時には、Streamをstop()で事前に停止させます。

  if( curSTREAM !== null ){
    curSTREAM.getVideoTracks().forEach( (camera) => {
      camera.stop();
    });
  }

素材

参考ページ