Skip to content

Commit

Permalink
feat: build in location tracking
Browse files Browse the repository at this point in the history
By default visually hightlights detected QR codes. Behavior can be
controlled via the `track` prop. Pass Boolean values to enable/disable
this feature. Pass a repaint function to customize the way location
coordinates are painted.

Closes #37
BREAKING CHANGE: removed locate event
  • Loading branch information
gruhn committed Jun 9, 2018
1 parent d4fdc17 commit 6bcb16d
Showing 1 changed file with 93 additions and 30 deletions.
123 changes: 93 additions & 30 deletions src/components/QrcodeReader.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
<template lang="html">
<div class="qrcode-reader">
<div class="qrcode-reader__inner-wrapper">
<div class="qrcode-reader__overlay">
<slot></slot>
</div>

<canvas
ref="trackingLayer"
class="qrcode-reader__tracking-layer"
></canvas>

<video
ref="video"
class="qrcode-reader__camera"
></video>

<div class="qrcode-reader__overlay">
<slot></slot>
</div>
</div>
</div>
</template>
Expand All @@ -18,8 +23,6 @@ import * as Scanner from '../misc/Scanner.js'
import Camera from '../misc/Camera.js'
import isBoolean from 'lodash/isBoolean'
const NO_LOCATION = [] // use specific array instance to guarantee equality ([] !== [] but NO_LOCATION === NO_LOCATION)
export default {
props: {
paused: {
Expand All @@ -31,6 +34,11 @@ export default {
type: [Object, Boolean],
default: () => ({}), // empty object
},
track: {
type: [Function, Boolean],
default: true,
},
},
data () {
Expand Down Expand Up @@ -64,6 +72,7 @@ export default {
facingMode: { ideal: 'environment' },
width: { min: 360, ideal: 680, max: 1920 },
height: { min: 240, ideal: 480, max: 1080 },
...this.videoConstraints,
}
}
Expand All @@ -74,6 +83,37 @@ export default {
}
},
trackRepaintFunction () {
if (this.track === true) {
return function (location, ctx) {
if (location !== null) {
const {
topLeftCorner,
topRightCorner,
bottomLeftCorner,
bottomRightCorner,
} = location
ctx.strokeStyle = 'red'
ctx.beginPath()
ctx.moveTo(topLeftCorner.x, topRightCorner.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()
}
}
} else if (this.track === false) {
return null
} else {
return this.track
}
},
},
watch: {
Expand Down Expand Up @@ -143,33 +183,48 @@ export default {
},
onLocate (location) {
if (location === null) {
this.$emit('locate', NO_LOCATION)
} else {
const locationArray = this.normalizeLocation([
location.topLeftCorner,
location.topRightCorner,
location.bottomRightCorner,
location.bottomLeftCorner,
])
this.$emit('locate', locationArray)
location = this.normalizeLocation(location)
if (this.repaintTrack !== null) {
this.repaintTrack(location)
}
this.$emit('locate', location)
},
/**
* The coordinates are based on the original camera resolution but the
* video element is responsive and scales with space available. Therefore
* the coordinates are re-calculated to be relative to the video element.
*/
normalizeLocation (locationArray) {
const widthRatio = this.camera.displayWidth / this.camera.resolutionWidth
const heightRatio = this.camera.displayHeight / this.camera.resolutionHeight
return locationArray.map(({ x, y }) => ({
x: Math.floor(x * widthRatio),
y: Math.floor(y * heightRatio),
}))
normalizeLocation (location) {
if (location === null) {
return null
} else {
const widthRatio = this.camera.displayWidth / this.camera.resolutionWidth
const heightRatio = this.camera.displayHeight / this.camera.resolutionHeight
Object.keys(location).forEach(key => {
const { x, y } = location[key]
location[key].x = Math.floor(x * widthRatio)
location[key].y = Math.floor(y * heightRatio)
})
return location
}
},
repaintTrack (location) {
const canvas = this.$refs.trackingLayer
const ctx = canvas.getContext('2d')
canvas.width = this.camera.displayWidth
canvas.height = this.camera.displayHeight
window.requestAnimationFrame(
() => this.trackRepaintFunction(location, ctx)
)
},
},
Expand All @@ -194,13 +249,21 @@ export default {
object-fit: contain;
max-width: 100%;
max-height: 100%;
z-index: 10;
}
.qrcode-reader__overlay {
.qrcode-reader__overlay,
.qrcode-reader__tracking-layer {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
}
.qrcode-reader__overlay {
z-index: 30;
}
.qrcode-reader__tracking-layer {
z-index: 20;
}
</style>

0 comments on commit 6bcb16d

Please sign in to comment.