DEV Community

Dita Rahma P.
Dita Rahma P.

Posted on

Make a Camera App in Web (Part 2): Capturing the image

From the past article in Part 1, we already succeeded in accessing the webcam with mediaDevices.getUserMedia() and stop it with a single button. You may want to stop there if you just want to stream something without any action required after. But I doubt you just leave it like that because, what the hell that kind of thing going to be useful, right?

So now we're going to move to the next step. What did the camera do? Capture the image, yes. So after we successfully accessing the user permission to use the camera and stream it to our webpage, then we're going to add some spaces to store and display our captured image and of course, a button to capture it.

First, let's put <canvas></canvas> tag to display our image. Don't forget to make it the same size as the <video></video> so our image won't be cropped.

 <div v-if="isCameraOpen" class="camera-box">
    <video v-show="!isPhotoTaken" ref="camera" :width="450" :height="337.5" autoplay></video>
    <canvas v-show="isPhotoTaken" id="photoTaken" ref="canvas" :width="450" :height="337.5"></canvas>
 </div>
Enter fullscreen mode Exit fullscreen mode

See that there is a condition put there which is isPhotoTaken. Why put the condition there? Think about the camera. It streams when we haven't captured any image yet. Then the moving picture gone replaced by the static image that we captured after clicking a button. So we're going to make just like a camera, the <video></video> will be hidden and <canvas></canvas> would appear after we click the shutter.

There you might think again why using v-show instead of v-if-else. It simply because v-show just hid the element, not delete it, so we're not going to call createCameraElement() multiple times if we want to take another photo.

Let's declare isPhotoTaken inside data() and handle it together with isCameraOpen in the toggleCamera() method so when we close the camera and open it again, it doesn't show the last captured image.

data() {
  return {
    isCameraOpen: false,
    isPhotoTaken: false
  }
},

methods: {
  toggleCamera() {
    if(this.isCameraOpen) {
      this.isCameraOpen = false;
      this.isPhotoTaken = false;
      this.stopCameraStream();
    } else {
      this.isCameraOpen = true;
      this.createCameraElement();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Nah now what we need after that is the shutter button. Let's add it to our HTML and give it an action to called a method that will capture our photo. Let's call our method takePhoto().

<div v-if="isCameraOpen" class="camera-shoot">
  <button type="button" class="button" @click="takePhoto">
    <img src="https://img.icons8.com/material-outlined/50/000000/camera--v2.png">
  </button>
</div>
Enter fullscreen mode Exit fullscreen mode

I use the free image icons to fill the button and make it round with setting border-radius to 100% so it will look like a shutter button on a phone camera. Here's the styling if you wanna take a look.

button {
  height: 60px;
  width: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 100%;

  img {
    height: 35px;
    object-fit: cover;
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we have the shutter button. Great! Next is let's define the method takePhoto(). This method will basically grab the image on the <video></video> and put it inside <canvas></canvas>.

takePhoto() {
  this.isPhotoTaken = !this.isPhotoTaken;

  const context = this.$refs.canvas.getContext('2d');
  context.drawImage(this.$refs.camera, 0, 0, 450, 337.5);
}
Enter fullscreen mode Exit fullscreen mode

In the method above, we have this toggle action on isPhotoTaken. This action will make the button not only as a shutter but also as a switch to replace the image and the camera stream. So when the stream is active, you click the shutter, it will capture your photo. To the opposite, when you already captured a photo and click the shutter again, the camera will stream and the existing photo will be gone.

For saving the image to the canvas, we first have to catch the canvas element by pointer it with $refs and get a drawing context on the canvas with getContext('2d'). And then we can assign the image from camera (<video></video>') to the canvas with drawImage(). For this part make sure that you draw with the correct size. Here's I draw starting from point x = 0, y = 0, and with 450 width and 337.5 height, the same size as the <video></video>.

That's it! Now you may want to run it and try to take a photo to see if it's work or not.

Is it all? No. We want to do something with the photo. You may want to upload it to the server or download it. Now let's try to put a download button below the shutter button like this. We're going to use anchor instead a button because we will assign href attribute later. Don't forget to put download attribute to define your file name that will be downloaded.

<div v-if="isPhotoTaken && isCameraOpen" class="camera-download">
  <a id="downloadPhoto" download="my-photo.jpg" class="button" role="button" @click="downloadImage">
    Download
  </a>
</div>
Enter fullscreen mode Exit fullscreen mode

Giving the condition above, the button will be hidden when we click the close button. Actually, the close camera button will completely hide all the elements except the button itself. Now we have to define the downloadImage on javascript.

downloadImage() {
  const download = document.getElementById("downloadPhoto");
  const canvas = document.getElementById("photoTaken").toDataURL("image/jpeg")
    .replace("image/jpeg", "image/octet-stream");

  download.setAttribute("href", canvas);
}
Enter fullscreen mode Exit fullscreen mode

downloadImage() will get data URI containing a representation of the image in the image/jpeg format then assign it to href attribute in download anchor.

Discussion (0)