Building a barcode scanner with Vue and QuggaJS

I need a small project to start learning typescript and class based vue components. I’ve also been meaning to build a barcode scanner for a while. Bar and QR codes are one of the easiest ways to tie a physical object to a digital ledger. While the intended use currently is to log my books, the potential uses are endless.

This project uses Vue CLI 3 and quaggaJS

The setup is fairly simple except for major three caveats I encountered.

Camera Resolution Needs to be High.

My old logitech webcam(640p) was not high enough resolution for barcodes, which led to some early frustrations trying to get a working example. However, it did lead me to remote debugging on an old pixel I have. The results are pretty great and tests the use case I had in mind.

Livestream has to be served over HTTPS

Giving the browser access to the user’s camera requires it to be served over https.

Luckily we can just add this to vue.config.js

  configureWebpack: {
    devServer: {
      https: true
    }
  }

Know what code you are scanning

Check what kind of barcode you are using. Selecting the right format for the decoder to get correct reads.

Everything is in one component. Excuse my terrible early typescript.

<template>
  <div>
    <div v-if="foundCodes" class="level">
      <div class="level-item has-text-centered">
        <div>
          <p class="heading">{{foundCodes.type}}</p>
          <p class="title">{{foundCodes.code}}</p>
        </div>
      </div>
    </div>
    <nav class="level" role="navigation" aria-label="main navigation">
      <div v-show="!scannerActive" class="level-item">
        <a @click="start" class="button">Start Scanner</a>
      </div>

      <div v-show="scannerActive" class="level-item">
        <a @click="stop" class="button">Stop Scanner</a>
      </div>
    </nav>
    <div id="videoWindow" class="video"></div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

import Quagga from "quagga";


interface barcode {
  code: string;
  type: string;
}

interface codeResult {
  code: string;
  format: string;
}

@Component
export default class Scanner extends Vue {
  scannerActive: boolean = false;
  foundCodes: barcode | null = null;

  start() {
    this.scannerActive = true;
    const config: object = {
      locate: true,
      inputStream: {
        name: "live",
        type: "LiveStream",
        target: document.querySelector("#videoWindow")
      },
      decoder: {
        readers: ["ean_reader"],
        multiple: true
      },
      locator: {
        halfSample: true,
        patchSize: "medium"
      }
    };
    Quagga.init(config, err => {
      if (err) {
        console.log(err);
        return;
      }
      console.log("initialization complete");
      Quagga.start();
    });
  }

  stop() {
    this.scannerActive = false;
    Quagga.stop();
  }

  mounted() {
    Quagga.onDetected((data: Array<object>) => {
      // console.log(data);
      const foundResult = data[0];
      const foundCode: barcode = {
        code: foundResult.codeResult.code,
        type: foundResult.codeResult.format
      };
      console.log(foundCode);
      this.foundCodes = foundCode;
      this.$emit("found", foundCode);
    });
  }
}
</script>

<style>
.video {
  width: 100%;
}
</style>
GIF.gif

iffy with quick motions but numbers stabilize after it finds the right box.

Repo can be found here: https://github.com/HelloHowAreYouHaveANiceDay/barcode-scanner