How to Use Web API

In this tutorial, we will illustrate how we could use MediaDevices API to capture picture using device camera in the following sample application.

Creating Electron TODO App

First of all, let’s create our application with Monaca template. From the Monaca Dashboard, you can go to Create New Project → Sample Applications . Then choose Electron TODO App from the template list, fill in the Project Name and Description and click Create Project.

Application Features

This sample application allows users to create and manage a todo list. User can add pictures by uploading an existing picture or taking a picture. The list is saved in local storage.

In this tutorial, we will be focusing on explaining how we use MediaCapture API to take pictures using the built-in camera.

Once you open the application, it will display any items saved in the local storage.

Application Dashboard

To create a new item, you can click on + new button on the right side of the application. The new modal dialog will be displayed. You can input any todo items, upload an existing picture, or taking a new picture by clicking on camera button.

Add New Item

Once the camera button is clicked, another modal dialog with the camera view will appear. If you press Capture, it will capture what rendering in the box and save it as image.

Taking Picture Using Laptop Camera

HTML Explanation

Camera Modal

The following code snippet is the camera modal. This modal will display when we click on the camera button. It has 3 buttons - rotate, capture , and cancel. The rotate button is only visible if the device support flipping between front and rear camera. The video-container video element is used to stream pictures from the built-in camera.

<div class="modal fade" id="camera-modal" tabindex="-1" data-backdrop="static" role="dialog" aria-labelledby="camera-modal-label" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="camera-header-modal"></div>
            <div class="camera-modal-body modal-body text-center">
                <video id="video-container" class="camera-preview" autoplay></video>
            </div>
            <a id="cordova-camera-cancel" class="btn enabled camera-close-btn" data-dismiss="modal"><i class="fa fa-times"></i> cancel</a>
            <a id="rotate-camera" class="btn enabled camera-rotate-btn"><i class="fa fa-sync"></i> Rotate</a>
            <a id="camera-capture" class="btn enabled camera-capture-btn" data-dismiss="modal"><i class="fa fa-camera"></i> Capture</a>
        </div>
    </div>
</div>

JavaScript Explanation

Global variable declaration

Let’s quickly go through some of the important variable regarding camera features.

  • isFrontCamera is used to toggle between front and rear camera.
  • FRONT_CAMERA is used to store front camera value of mediaDevices API’s facingMode Enum.
  • REAR_CAMERA is used to store rear camera value of mediaDevices API’s facingMode Enum.
let isFrontCamera;
const FRONT_CAMERA = 'user';
const REAR_CAMERA = 'environment';
const videoContainer = document.getElementById('video-container');
const btnOpenCameraModal = document.getElementById('open-camera-modal');
const btnRotateCamera = document.getElementById('rotate-camera');
...

Check if mediaDevices API is supported

The following code snippet is to (1) check whether the mediaDevices API is supported and (2) turn on the rotate button if the device is Android and used the rear camera as default.

function initialize() {
  // Check if getUserMedia is supported on the device
  if (!hasMediaDevicesApi()) {
    btnOpenCameraModal.hidden = true;
  }
  if (isAndroidOS()) {
    // use rear camera for android
    btnRotateCamera.hidden = false;
    isFrontCamera = false;
  } else {
    // use front camera for browser/electron
    btnRotateCamera.hidden = true;
    isFrontCamera = true;
  }
  ...
}

function hasMediaDevicesApi() {
  return !!(navigator.mediaDevices &&
    navigator.mediaDevices.getUserMedia);
}

Turn On/Off Camera

We will use getUserMedia() to prompt users for permission to use media input in which produce a MediaStream. The stream can include a video track (such as camera, video recording device, screen sharing service, etc), an audio track, and possibly other track types. For more detail, please refer to the official document here. The syntax is as following:

var promise = navigator.mediaDevices.getUserMedia(constraints);

The function returns a Promise and accepts MediaStreamConstraints object as a parameter.

In the following code snippet, we specify the width and height to be exact 250 pixel and set the camera facing mode based on the provided argument. Once the camera device is connected successfully, the Media Stream object is returned and assigned to the video element.

function turnOnCamera(frontCamera) {
  const facingModeOption = frontCamera ? FRONT_CAMERA : REAR_CAMERA;
  const constraints = {
    video: {
      width: {
        exact: 250
      },
      height: {
        exact: 250
      },
      facingMode: facingModeOption
    }
  };
  // Access to the camera and turn it own
  navigator.mediaDevices.getUserMedia(constraints)
    .then(handleSuccess)
    .catch(handleError);

  function handleSuccess(stream) {
    videoContainer.srcObject = stream;
  }

  function handleError(error) {
    alert('Could not get user media API' + JSON.stringify(error));
  }
}

To turn of camera, we need to stop all the source associated to the track.

function turnOfCamera() {
  if (videoContainer && videoContainer.srcObject) {
    videoContainer.srcObject.getTracks().forEach(function(track) {
      track.stop();
    });
    videoContainer.srcObject = null;
  }
}

The function below is to flip camera between front and rear view.

function rotateCamera(e) {
  isFrontCamera = !isFrontCamera;
  turnOfCamera();
  turnOnCamera(isFrontCamera);
}

Capture a Photo

To capture a photo from a video stream/track, we first create a canvas element where the width and height are the from the video container. Then, we stop all the track and convert the last track to image format.

function takePicture(e) {
  const canvas = document.createElement('canvas');
  // Saving current image
  canvas.width = videoContainer.videoWidth;
  canvas.height = videoContainer.videoHeight;
  canvas.getContext('2d').drawImage(videoContainer, 0, 0);
  // If the video source Object is set, stop all tracks
  if (videoContainer.srcObject) {
    videoContainer.srcObject.getTracks().forEach(function(track) {
      track.stop();
      try {
        // Other browsers will fall back to image/png
        todoItemImage.src = canvas.toDataURL('image/webp');
      } catch (error) {
        alert('Could not get the picture.' + JSON.stringify(error));
      }
    });
  }
}

See Also: