vue-qrcode-reader
A Vue.js 2 component, accessing the device camera and allowing users to read QR codes, within the browser.
- read camera stream and/or drag-and-dropped images
- customizable visual tracking of QR codes
- responsive and layout agnostic
Browser support
- Chrome requires HTTPS or localhost (see #38 for help)
- Safari also requires HTTPS even on localhost (see #48)
- on iOS it only works with Safari. Chrome or Firefox for iOS are not supported (see #29)
- more details on Caniuse
Usage
decode
event
Once a stream from the users camera is loaded, it is displayed and continuously scanned for QR codes. Results are indicated by the decode
event. This also accounts for decoded images drag-and-dropped in the area the component occupies.
<qrcode-reader @decode="onDecode"></qrcode-reader>
methods: {
onDecode (decodedString) {
}
}
You might notice that when you scan the same QR code multiple times in a row, decode
is still only emitted once. When you hold a QR code in the camera, frames are actually decoded multiple times a second but you don't want to be flooded with decode
events that often. That's why the last decoded QR code is always cached and only new results are propagated. However, you can clear this internal cache by setting the paused
prop to true.
detect
event
The detect
event is quite similar to decode
but it provides more details. decode
only gives you the string encoded by QR codes. detect
additionally
- tells you where results came from (i.e. a camera stream, a drag-and-dropped file or url)
- gives you the unprocessed raw image data
- the coordinates of the QR code in the image/camera frame
In case of errors decode
also silently fails. For example when a non-image file is drag-and-dropped.
<qrcode-reader @detect="onDetect"></qrcode-reader>
methods: {
async onDetect (promise) {
try {
const {
source,
imageData,
content,
location
} = await promise
} catch (error) {
if (error.name === 'DropImageFetchError') {
} else if (error.name === 'DropImageDecodeError') {
} else {
}
}
}
}
init
event
It might take a while before the component is ready and the scanning process starts. The user has to be asked for camera access permission first and the camera stream has to be loaded.
If you want to show a loading indicator, you can listen for the init
event. It's emitted as soon as the component is mounted and carries a promise which resolves when everything is ready. The promise is rejected if initialization fails. This can have a couple of reasons.
In Chrome you can't prompt users for permissions a second time. Once denied, users can only manually grant them. Make sure your users understand why you need access to their camera before you mount this component. Otherwise they might panic and deny and then get frustrated because they don't know how to change their decision.
<qrcode-reader @init="onInit"></qrcode-reader>
methods: {
async onInit (promise) {
try {
await promise
} catch (error) {
if (error.name === 'NotAllowedError') {
} else if (error.name === 'NotFoundError') {
} else if (error.name === 'NotSupportedError') {
} else if (error.name === 'NotReadableError') {
} else if (error.name === 'OverconstrainedError') {
} else {
}
} finally {
}
}
}
track
prop
By default detected QR codes are visually highlighted. A transparent canvas overlays the camera stream. When a QR code is detected, its location is painted to the canvas. You can enable/disable this feature by passing true
/false
via the track
prop. If tracking is disabled the camera stream is scanned much less frequently. So if you encounter performance problems on your target device, this might help.
You can also pass a function with track
to customize the way the location is painted. This function is called to produce each frame. It receives the location object as the first argument and a CanvasRenderingContext2D
instance as the second argument.
Avoid access to reactive properties in this function (like stuff in data
, computed
or your Vuex store). The function is called several times a second and might cause memory leaks. If you want to be save don't access this
at all.
Say you want to paint in a different color that better fits your overall page theme.
<qrcode-reader :track="repaintLocation"></qrcode-reader>
methods: {
repaintLocation (location, ctx) {
if (location !== null) {
const {
topLeftCorner,
topRightCorner,
bottomLeftCorner,
bottomRightCorner,
} = location
ctx.strokeStyle = 'blue'
ctx.beginPath()
ctx.moveTo(topLeftCorner.x, topLeftCorner.y)
ctx.lineTo(bottomLeftCorner.x, bottomLeftCorner.y)
ctx.lineTo(bottomRightCorner.x, bottomRightCorner.y)
ctx.lineTo(topRightCorner.x, topRightCorner.y)
ctx.lineTo(topLeftCorner.x, topLeftCorner.y)
ctx.closePath()
ctx.stroke()
}
}
}
default slot
Distributed content will overlay the camera stream, wrapped in a position: absolute
container.
<qrcode-reader>
<b>stuff here overlays the camera stream</b>
</qrcode-reader>
paused
prop
With the paused
prop you can prevent further decode
propagation and functions passed via track
are stopped being called. Useful for example if you want to validate results one at a time. This will also freeze the camera stream.
<qrcode-reader @decode="onDecode" :paused="paused"></qrcode-reader>
data () {
return {
paused: false
}
},
methods: {
onDecode (content) {
this.paused = true
}
}
video-constraints
prop
This component uses getUserMedia to request camera streams. This method accepts a constraints object to configure for example if front or rear camera should be accessed. This is passed by default:
{
audio: false,
video: {
facingMode: { ideal: 'environment' },
width: { min: 360, ideal: 680, max: 1920 },
height: { min: 240, ideal: 480, max: 1080 },
}
}
You can change the video
part using the video-constraints
prop. Note that you only have to pass properties you want to override. If you want to use the front camera for example and change nothing else, pass this:
<qrcode-reader :video-constraints="{ facingMode: 'user' }"></qrcode-reader>
If you change this property after initialization, a new camera stream will be requested and the init
event will be emitted again.
Installation
yarn add vue-qrcode-reader
or using NPM:
npm install --save vue-qrcode-reader
Default import
Register component globally:
import Vue from 'vue'
import VueQrcodeReader from 'vue-qrcode-reader'
Vue.use(VueQrcodeReader)
Register locally in other components scope:
import Vue from 'vue'
import { QrcodeReader } from 'vue-qrcode-reader'
Vue.component('my-component', {
components: { QrcodeReader },
)
⚠️ A css file is included when importing the package. You may have to setup your bundler to embed the css in your page.
Browser
You need to include a script and CSS file. You can pull both from unpkg.com. Make sure to replace [VERSION]
with the version you need (for example 1.0.1
):
<link rel="stylesheet" href="https://unpkg.com/vue-qrcode-reader@[VERSION]/dist/vue-qrcode-reader.css"/>
<script src="vue.js"></script>
<script src="https://unpkg.com/vue-qrcode-reader@[VERSION]/dist/vue-qrcode-reader.browser.js"></script>
The plugin should be auto-installed. If not, you can install it manually.
Register component globally:
Vue.use(VueQrcodeReader)
Register locally in other components scope:
Vue.component('my-component', {
components: {
'qrcode-reader': VueQrcodeReader.QrcodeReader
},
)
License
MIT