loading...

Lets Access a webcam with Vue.js

jbull328 profile image John Bull Updated on ・3 min read

Recently I was tasked with creating a web application that could take pictures and store them along with some data about each record. I had no idea at first how to go about this, but with my naive optimism in hand, I began work on the web application. I was able to choose my own stack and decided on Vue.js for the interface. It turns out that it's pretty straight forward to access the device camera and work with it.

If you want to follow along you can clone this repo and check out the start branch.

I have created a basic template to work with the video stream and manipulate the image.

camera app interface

Interface continued:

camera app interface2

What we have is a big blank spot, not very exciting but we are going to fix that. Our blank spot is going to be a space for our camera feed, a few buttons below that. Finally a default image at the bottom, we will rig this up to hold a copy of the image once we take a picture.

Great, let's get started. The first thing we should do is get the camera working so we can see that image right away when we load up our app.

We know we want to use a life cycle hook so let's get that sorted by adding the following code to our script section. We also need to add these data elements.

data() {
        return {
            mediaStream: null,
            imageData: {
                image: '',
                image_orientation: 0,
            },
        }
    },

mounted() {
        navigator.mediaDevices.getUserMedia({video: true})
            .then(mediaStream => {
                    this.$refs.video.srcObject = mediaStream;
                    this.$refs.video.play()
                    this.mediaStream = mediaStream                   
            })   
    },

So now if we add a reference to the video in our template it should work, right?

<div class='ui container'>
        <video v-if="!imageData.image" ref="video" class="camera-stream" />
        <div class='ui divider'></div>
        <div class="icon-group">   
            <div  class="camera-icon">
                <i class="big camera icon" ></i>
                <p>Take Picture</p>
            </div>
            <div class='camera-icon'>
                <i class="big redo alternate icon"></i>
                <p>Rotate</p> 
            </div>
            <div  class='camera-icon'>
                <i class="big thumbs up outline icon"></i>
                <p>Done</p> 
            </div>
            <div class="camera-icon">
                <i class="big cancel icon"></i>
                <p>Cancel</p>
            </div>
        </div>
</dev>

Well yes and no.
Our code should work but the browser can't access the device without https. So we have a bit of work to do to make that happen.

I'm not an expert on each and every individual set up so I will leave helpful links to creating a self-signed cert for this project. But basically our web application needs to have the cert or it will not be able to access the device hardware this is for security reasons.

Part of the process involves setting create Vue app to run on https in the development server so we can see our progress first hand. In a vue.config.js file we need to add, with the routes to our key and cert files.

const fs = require('fs');

module.exports = {
    devServer: {
        https: {
            key: fs.readFileSync('/etc/ssl/private/my.key'),
            cert: fs.readFileSync('/etc/ssl/certs/my.crt'),
        },
    }
}

Now that we have a cert we should see our camera displayed when we open the app.

Next let's take our video stream and capture an image from it. Essentially we are storing an image as a blob for later manipulation.

captureImage() {
            const mediaStreamTrack = this.mediaStream.getVideoTracks()[0]
            const imageCapture = new window.ImageCapture(mediaStreamTrack)
            let reader = new FileReader();
            return imageCapture.takePhoto().then(blob => {
                reader.readAsDataURL(blob)
                reader.onload = () => {
                    this.imageData.image = reader.result;
                }
            })  
        }

Let's upload out blob image to our server, our backend code will need to handle converting the blog into an image file like a jpeg.
But that would be a different tutorial.

uploadImage() {
            axios({ method: "POST", "url": API_IMAGE_ENDPOINT, "data": this.imageData})
                    .then(response => {
                        this.response = response.data;    
                     })
        }

Now the other methods included in our app are used to manipulate the image since we don't know if our camera might be mounted sideways or not, this happens on many mobile devices.

rotateImage() {
            this.imageData.image_orientation = this.imageData.image_orientation + 90; 
        },
cancelImage() {
            this.imageData.image = null;
            this.showCameraModal = true;
            navigator.mediaDevices.getUserMedia({video: true})
            .then(mediaStream => {
                    this.$refs.video.srcObject = mediaStream;
                    this.$refs.video.play()
                    this.mediaStream = mediaStream                   
            }) 
        },

That should do it, we now have the ability to access the device webcam and save an image to the server. I found that not all devices follow the standard for device access api, and may need slight modifications depending on your device. I hope you enjoyed this tutorial.

Posted on by:

jbull328 profile

John Bull

@jbull328

Creative, Curious Maker, I have 7 years in Tech Software Developer at a mid sized enterprize. Hard working and collaborative, likes to help others. Hit me up!

Discussion

markdown guide