Showing posts with label vue.js. Show all posts
Showing posts with label vue.js. Show all posts

Friday, December 11, 2020

How to Implement Webcam in VueJs Application

In this tutorial, we are going to implement a webcam in the Vuejs application.


Generally, what it does is:

  1. Stream the video from the webcam
  2. Capture photo from the streaming video
  3. Preview the captured photo
  4. Convert it into the file to send to the server
The overall implementation looks as below:




1. Create a sample Vuejs application.

Open the terminal or command prompt and create the application.

vue create vue-camera
  

Select the default option or can select manually, where you can have a choice to set up.
cd vue-camera
yarn serve

2. Create a Vuejs webcam component



Now, let's create a component called Camera.vue.  
<div class="camera-box">
    <div style="display: flex; justify-content: center;">
        <img style="height: 25px;" v-if="isCameraOpen"
             src="https://img.icons8.com/material-outlined/50/000000/camera--v2.png"
             class="button-img camera-shoot" @click="capture"/>
        <div class="camera-button">
            <button type="button" class="button is-rounded cam-button"
                    style="margin-left: 40%; background-color: white; border: 0px;"
                    @click="toggleCamera"
            >
        <span v-if="!isCameraOpen"><img style="height: 25px;" class="button-img"
                                        src="https://img.icons8.com/material-outlined/50/000000/camera--v2.png"></span>
                <span v-else><img style="height: 25px;" class="button-img"
                                  src="https://img.icons8.com/material-outlined/50/000000/cancel.png"></span>
            </button>
        </div>
    </div>
    <div style="height: 200px">
        <div v-if="isCameraOpen" class="camera-canvas">
            <video ref="camera" :width="canvasWidth" :height="canvasHeight" autoplay></video>
            <canvas v-show="false" id="photoTaken" ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
        </div>
    </div>
</div>
Let's set the data used.
export default {
  name: "Camera",
  components: {
  },
  data() {
    return {
      isCameraOpen: false,
      canvasHeight:200,
      canvasWidth:190,
      items: [],
    }
  },
  }
<style scoped>
.camera-box {
  border: 1px dashed #d6d6d6;
  border-radius: 4px;
  padding: 2px;
  width: 80%;
  min-height: 300px;
}

</style>





The HTML file used will show the camera image button to capture the photo. Here, we are using canvas height and width for the photo size. You can always adjust the different size photo by adjusting canvas height and width.

Let's use the different functions used in HTML.
toggleCamera() {
      if (this.isCameraOpen) {
        this.isCameraOpen = false;
        this.stopCameraStream();
      } else {
        this.isCameraOpen = true;
        this.startCameraStream();
      }
    },
This function simply toggles the camera to capture the photo. And start streaming the video. Let's implement the function used inside this toggleCamer().
startCameraStream() {
      const constraints = (window.constraints = {
        audio: false,
        video: true
      });
      navigator.mediaDevices
          .getUserMedia(constraints)
          .then(stream => {
            this.$refs.camera.srcObject = stream;
          }).catch(error => {
        alert("Browser doesn't support or there is some errors." + error);
      });
    },
The above function will start the camera by using the navigator mediaDevices interface which will access to the connected media input device, in our case its camera. And asign the streaming to canvas dom element to show the video. Here we are setting the constraints as audio is false because we don't need the audio to capture the photo.
stopCameraStream() {
      let tracks = this.$refs.camera.srcObject.getTracks();
      tracks.forEach(track => {
        track.stop();
      });
    },
The above function will simply stop the camera by stopping the sequence of track in streaming.
capture() {
      const FLASH_TIMEOUT = 50;
      let self = this;
      setTimeout(() => {
        const context = self.$refs.canvas.getContext('2d');
        context.drawImage(self.$refs.camera, 0, 0, self.canvasWidth, self.canvasHeight);
        const dataUrl = self.$refs.canvas.toDataURL("image/jpeg")
            .replace("image/jpeg", "image/octet-stream");
        self.addToPhotoGallery(dataUrl);
        self.uploadPhoto(dataUrl);
        self.isCameraOpen = false;
        self.stopCameraStream();
      }, FLASH_TIMEOUT);
    },
When a user clicks the camera button, then it will take the video streaming dom context and captured the 2d photo with the given width and height. We are using the setTimeout because video streaming will take some time to stream.




Let's implement the self.addToPhotoGallery() used in the above function. This is to preview the captured photo. For this, we are using the "vue-picture-swipe" library. So first let's add this to our application.

For yarn package manager:

yarn add vue-picture-swipe

For npm package manager:

npm install --save vue-picture-swipe

Import the library in our application:

import VuePictureSwipe from 'vue-picture-swipe';
Register the component:

components: {
    VuePictureSwipe
  },
Use in the template:

<vue-picture-swipe :items="items"></vue-picture-swipe>

addToPhotoGallery(dataURI) {
      this.items.push(
          {
            src: dataURI,
            thumbnail: dataURI,
            w: this.canvasWidth,
            h: this.canvasHeight,
            alt: '' // optional alt attribute for thumbnail image
          }
      )
    },
This will preview the captured pictures.

Finally, using self.uploadPhoto() we can upload the captured picture to the server.

uploadPhoto(dataURL){
      let uniquePictureName = this.generateCapturePhotoName();
      let capturedPhotoFile = this.dataURLtoFile(dataURL, uniquePictureName+'.jpg')
      let formData = new FormData()
      formData.append('file', capturedPhotoFile)
      // Upload api
      // axios.post('http://your-url-upload', formData).then(response => {
      //   console.log(response)
      // })
    },
    generateCapturePhotoName(){
      return  Math.random().toString(36).substring(2, 15)
    },
    dataURLtoFile(dataURL, filename) {
      let arr = dataURL.split(','),
          mime = arr[0].match(/:(.*?);/)[1],
          bstr = atob(arr[1]),
          n = bstr.length,
          u8arr = new Uint8Array(n);

      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, {type: mime});
    },




generateCapturePhotoName() will generate the unique name for each captured picture, and to send to the server, first we are converting the dataUrl value to file using dataURLtoFile()

The overall implementation of the component Camera.vue looks like as below:

<template>
    <div class="camera-box">
        <div style="display: flex; justify-content: center;">
            <img style="height: 25px;" v-if="isCameraOpen"
                 src="https://img.icons8.com/material-outlined/50/000000/camera--v2.png"
                 class="button-img camera-shoot" @click="capture"/>
            <div class="camera-button">
                <button type="button" class="button is-rounded cam-button"
                        style="margin-left: 40%; background-color: white; border: 0px;"
                        @click="toggleCamera"
                >
        <span v-if="!isCameraOpen"><img style="height: 25px;" class="button-img"
                                        src="https://img.icons8.com/material-outlined/50/000000/camera--v2.png"></span>
                    <span v-else><img style="height: 25px;" class="button-img"
                                      src="https://img.icons8.com/material-outlined/50/000000/cancel.png"></span>
                </button>
            </div>
        </div>
        <div style="height: 200px">
            <div v-if="isCameraOpen" class="camera-canvas">
                <video ref="camera" :width="canvasWidth" :height="canvasHeight" autoplay></video>
                <canvas v-show="false" id="photoTaken" ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
            </div>
        </div>
        <vue-picture-swipe :items="items"></vue-picture-swipe>
    </div>
</template>

<script>
    import VuePictureSwipe from 'vue-picture-swipe';

    export default {
        name: "Camera",
        components: {
            VuePictureSwipe
        },
        data() {
            return {
                isCameraOpen: false,
                canvasHeight:200,
                canvasWidth:190,
                items: [],
            }
        },
        methods: {
            toggleCamera() {
                if (this.isCameraOpen) {
                    this.isCameraOpen = false;
                    this.stopCameraStream();
                } else {
                    this.isCameraOpen = true;
                    this.startCameraStream();
                }
            },
            startCameraStream() {
                const constraints = (window.constraints = {
                    audio: false,
                    video: true
                });
                navigator.mediaDevices
                    .getUserMedia(constraints)
                    .then(stream => {
                        this.$refs.camera.srcObject = stream;
                    }).catch(error => {
                    alert("Browser doesn't support or there is some errors." + error);
                });
            },

            stopCameraStream() {
                let tracks = this.$refs.camera.srcObject.getTracks();
                tracks.forEach(track => {
                    track.stop();
                });
            },

            capture() {
                const FLASH_TIMEOUT = 50;
                let self = this;
                setTimeout(() => {
                    const context = self.$refs.canvas.getContext('2d');
                    context.drawImage(self.$refs.camera, 0, 0, self.canvasWidth, self.canvasHeight);
                    const dataUrl = self.$refs.canvas.toDataURL("image/jpeg")
                        .replace("image/jpeg", "image/octet-stream");
                    self.addToPhotoGallery(dataUrl);
                    self.uploadPhoto(dataUrl);
                    self.isCameraOpen = false;
                    self.stopCameraStream();
                }, FLASH_TIMEOUT);
            },

            addToPhotoGallery(dataURI) {
                this.items.push(
                    {
                        src: dataURI,
                        thumbnail: dataURI,
                        w: this.canvasWidth,
                        h: this.canvasHeight,
                        alt: 'some numbers on a grey background' // optional alt attribute for thumbnail image
                    }
                )
            },
            uploadPhoto(dataURL){
                let uniquePictureName = this.generateCapturePhotoName();
                let capturedPhotoFile = this.dataURLtoFile(dataURL, uniquePictureName+'.jpg')
                let formData = new FormData()
                formData.append('file', capturedPhotoFile)
                // Upload image api
                // axios.post('http://your-url-upload', formData).then(response => {
                //   console.log(response)
                // })
            },

            generateCapturePhotoName(){
                return  Math.random().toString(36).substring(2, 15)
            },

            dataURLtoFile(dataURL, filename) {
                let arr = dataURL.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);

                while (n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                return new File([u8arr], filename, {type: mime});
            },
        }
    }
</script>

<style scoped>
    .camera-box {
        border: 1px dashed #d6d6d6;
        border-radius: 4px;
        padding: 2px;
        width: 80%;
        min-height: 300px;
    }

</style>



Share:

Monday, December 7, 2020

How to Build the Vuejs Application as a Library and Web Component

In this tutorial, we are going to learn how to create a library and a web component of the Vuejs application, so that we can reuse them as a library in a different application and a custom HTML element in different webpages.

Using VueCli 3.x it's very easy to build our application. It gives some command to create them.

1. Create a sample Vuejs application:

Open the command prompt or terminal and use the following command:

vue create vue-build-target-app
  

You can either select the Default option so that you don't manually setup. This will default use the yarn package manager so, make sure to have package manager installed. Also, you can manually select features option.

Now, go to the project directory and run the application using the command:
cd vue-build-target-app
yarn serve
  
If you open the project, you can see the default component called HelloWorld.vue. We are using the same component to test.

2. Build as a Library:

Note: In lib mode, Vue is externalized. This means the bundle will not bundle Vue even if your code imports Vue

While building as a library even if we are importing the Vue in the component it will not use that in the bundle.

Building a single component as a library:

vue-cli-service build --target lib --name myLib [entry]
  
This is the command that we use where myLib is the name of the output bundle file and [entry] is the entity that we use.

For e.g:

vue-cli-service build --target lib --name HelloWorld src/components/HelloWorld.vue
  
This will build the single component HelloWorld.vue as the library. So, what you need to do is place the above command inside the package.json file under scripts.

"build-lib": "vue-cli-service build --target lib --name HelloWorld src/components/HelloWorld.vue",
  

Now, run the script from the command prompt:

yarn build-lib
  
This command will build the target files inside dis folder.


dist\HelloWorld.umd.js is the UMD bundle that can be served in the normal HTML file. As you can see the sample use case inside the dist folder contain the following demo.html file:

<meta charset="utf-8"></meta>
<title>HelloWorld demo</title>
<script src="https://unpkg.com/vue"></script>
<script src="./HelloWorld.umd.js"></script>

 <link href="./HelloWorld.css" rel="stylesheet"> </link>


<div id="app">
  <demo>  </demo>
</div>

<script>
new Vue({
  components: {
    demo: HelloWorld
  }
}).$mount('#app')
</script>
  
If you run this HTML file then you can see the desire output for that page.

dist\HelloWorld.umd.min.js   is the minified version of the UMD bundle 

dist\HelloWorld.common.js  is used in a module-based application

dist\HelloWorld.css is the CSS file that is associated with that component.

You can use those desired files on your web pages.

3. Build as a Web Component:

You can build a single entry as:

vue-cli-service build --target wc --name my-element [entry]
Here wc will build as a web component. For e.g:

vue-cli-service build --target wc -name HelloWorld src/components/HelloWorld.vue



Use this inside script and run the command:

"build-wc":vue-cli-service build --target wc -name HelloWorld src/components/HelloWorld.vue
Here you can give any name instead of  "build-wc" but you need to run the build command with this name

yarn build-wc
Which will create the following files under the dist folder:

dist\hello-world.js 
dist\hello-world.min.js

This is the web pack bundle that can be used in web pages. Sample example is created under the same folder i.e demo.html.

This doesn't require component registration, as the component is already registered while creating it. So it looks like a normal plain HTML element.

If you want to build the multiple components file then use the following command instead:

//package.json
    "build-wc": "vue-cli-service build --target wc --name demo src/components/*.vue",





Share:

Saturday, December 5, 2020

How to Implement Multiple Image Upload Mechanism in Vue.js Application

In this tutorial, we are going to implement the multiple-image upload using Vue.js.

The overall multiple image upload mechanism looks like below:

1. Create a sample Vuejs application:

Open the command prompt or terminal and create a project.
vue create vue-multiple-image-upload
  
Select the default option or manually select feature of your own and hit enter.


This will create a Vuejs application, Now go to the project folder. 
cd vue-multiple-image-upload
  
Run the application.
yarn serve //for yarn package manager
    npm run serve //for npm package manager
  

2. Implement Multiple Image Upload Mechanism.


Here, we are using vue-upload-multiple-image component dependency. The advantage of using this dependency is
  • Can upload single and multiple images
  • Have drag and drop feature
  • Can preview the uploaded images
  • Can add, edit and delete the uploaded images
  • Can be marked as the default or primary image  
Now, add the dependency in our sample application created.

For yarn package manager:

  yarn add vue-upload-multiple-image
  
For npm package manager:

  npm install vue-upload-multiple-image
  
Open the project in your favorite editor. And open the App.vue file or any file where you want to import it.

  import VueUploadMultipleImage from 'vue-upload-multiple-image'
  
Register the component:

  components: {
    VueUploadMultipleImage,
  },
  
Use the component:

 <vue-upload-multiple-image 
    @upload-success="uploadImageSuccess"
    @edit-image="editImage"
    @mark-is-primary="markIsPrimary"
    @limit-exceeded="limitExceeded"
    @before-remove="beforeRemove"
    id-upload="myIdUpload"
    id-edit="myIdEdit"
    :max-image=20
    primary-text="Default"
    browse-text="Browse picture(s)"
    drag-text="Drag pictures"
    mark-is-primary-text="Set as default"
    popup-text="This image will be displayed as default"
    :multiple=true
    :show-edit=true
    :show-delete=true
    :show-add=true
 >
 </vue-upload-multiple-image>
  
Here, all the events and props are implemented.

@upload-success event will be trigger for each image upload.

@edit-image will be trigger when editing an image i.e trying to replace the particular image

@mark-is-primary will be trigger when if you want to mark any image as a default or primary image.

@limit-exceeded will be trigger when the image upload number exceeds that is defined in max-image         props

@before-remove will be trigger when we try to delete the image 

Now let's implement each event function to handle them. In order to do so, use the following functions inside Vuejs methods.



methods: {
    uploadImageSuccess(formData, index, fileList) {
      console.log('data', formData, index, fileList)
      // Upload image api
      // axios.post('http://your-url-upload', formData).then(response => {
      //   console.log(response)
      // })
    },
    beforeRemove(index, removeCallBack) {
      console.log('index', index)
      var r = confirm("remove image")
      if (r == true) {
        removeCallBack()
      }
    },
    editImage(formData, index, fileList) {
      console.log('edit data', formData, index, fileList)
    },
    markIsPrimary(index, fileList){
      console.log('markIsPrimary data', index, fileList)
    },
    limitExceeded(amount){
      console.log('limitExceeded data', amount)
    }
  },
  
Here, you can handle each event triggered. For e.g for uploadImageSuccess function, we can call the endpoint to upload the images.

Now, run the application and test it.




Share: