Skip to content

Commit

Permalink
refactor: extract BarcodeDetector polyfill
Browse files Browse the repository at this point in the history
Implement dedicated npm package for BarcodeDetector polyfill:

  https://www.npmjs.com/package/barcode-detector

Should fix #197 along the way because dependencies are inlined in the
polyfill package instead of requested at runtime.

BREAKING CHANGE: remove `worker` prop

The idea of the `worker` prop was mainly to allow users to plug in
other detection algorithms to other barcode types. We now fully endorse
the Barcode Detection API and shift efforts to extend polyfill support
for more barcode types.
  • Loading branch information
gruhn committed Apr 7, 2021
1 parent 845c0fc commit 96cd30a
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 312 deletions.
2 changes: 1 addition & 1 deletion docs/api/QrcodeStream.md
Expand Up @@ -234,7 +234,7 @@ methods: {
}
```

### `worker` <Badge text="experimental" type="error" />
### `worker` <Badge text="deprecated" type="warning" />

::: tip
So far *vue-qrcode-reader* could only process QR codes.
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"barcode-detector": "^0.4.0",
"callforth": "^0.3.1",
"core-js": "^3.6.5",
"vue": "^2.6.11",
Expand Down
20 changes: 2 additions & 18 deletions src/components/QrcodeCapture.vue
Expand Up @@ -10,36 +10,20 @@
</template>

<script>
import { scan } from "../misc/scanner.js";
import { imageDataFromFile } from "../misc/image-data.js";
import { processFile } from "../misc/scanner.js";
import CommonAPI from "../mixins/CommonAPI.vue";
import Worker from "../worker/jsqr.js";
export default {
name: "qrcode-capture",
mixins: [CommonAPI],
props: {
worker: {
type: Function,
default: Worker
}
},
methods: {
onChangeInput(event) {
const files = [...event.target.files];
const resultPromises = files.map(this.processFile);
const resultPromises = files.map(processFile);
resultPromises.forEach(this.onDetect);
},
async processFile(file) {
const imageData = await imageDataFromFile(file);
const scanResult = await scan(this.worker, imageData);
return scanResult;
}
}
};
Expand Down
29 changes: 3 additions & 26 deletions src/components/QrcodeDropZone.vue
Expand Up @@ -10,23 +10,14 @@
</template>

<script>
import { scan } from "../misc/scanner.js";
import { imageDataFromFile, imageDataFromUrl } from "../misc/image-data.js";
import { processFile, processUrl } from "../misc/scanner.js";
import CommonAPI from "../mixins/CommonAPI.vue";
import Worker from "../worker/jsqr.js";
export default {
name: "qrcode-drop-zone",
mixins: [CommonAPI],
props: {
worker: {
type: Function,
default: Worker
}
},
methods: {
onDragOver(isDraggingOver) {
this.$emit("dragover", isDraggingOver);
Expand All @@ -39,26 +30,12 @@ export default {
const droppedUrl = dataTransfer.getData("text/uri-list");
droppedFiles.forEach(file => {
this.onDetect(this.processFile(file));
this.onDetect(processFile(file));
});
if (droppedUrl !== "") {
this.onDetect(this.processUrl(droppedUrl));
this.onDetect(processUrl(droppedUrl));
}
},
async processFile(file) {
const imageData = await imageDataFromFile(file);
const scanResult = await scan(this.worker, imageData);
return scanResult;
},
async processUrl(url) {
const imageData = await imageDataFromUrl(url);
const scanResult = await scan(this.worker, imageData);
return scanResult;
}
}
};
Expand Down
72 changes: 23 additions & 49 deletions src/components/QrcodeStream.vue
Expand Up @@ -33,7 +33,6 @@ import { keepScanning } from "../misc/scanner.js";
import { thinSquare } from "../misc/track-func.js";
import Camera from "../misc/camera.js";
import CommonAPI from "../mixins/CommonAPI.vue";
import spawnWorker from "../worker/jsqr.js";
export default {
name: "qrcode-stream",
Expand All @@ -58,19 +57,13 @@ export default {
track: {
type: [Function, Boolean],
default: true
},
worker: {
type: Function,
default: spawnWorker
}
},
data() {
return {
cameraInstance: null,
destroyed: false,
stopScanning: () => {}
destroyed: false
};
},
Expand Down Expand Up @@ -109,18 +102,22 @@ export default {
watch: {
shouldStream(shouldStream) {
if (!shouldStream) {
const frame = this.cameraInstance.captureFrame();
this.paintPauseFrame(frame);
const canvas = this.$refs.pauseFrame;
const ctx = canvas.getContext("2d");
const video = this.$refs.video;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
}
},
shouldScan(shouldScan) {
if (shouldScan) {
this.clearPauseFrame();
this.clearTrackingLayer();
this.clearCanvas(this.$refs.pauseFrame);
this.clearCanvas(this.$refs.trackingLayer);
this.startScanning();
} else {
this.stopScanning();
}
},
Expand All @@ -139,7 +136,6 @@ export default {
beforeDestroy() {
this.beforeResetCamera();
this.stopScanning();
this.destroyed = true;
},
Expand Down Expand Up @@ -183,8 +179,7 @@ export default {
this.onDetect(Promise.resolve(result));
};
// this.stopScanning()
this.stopScanning = keepScanning(this.worker, this.cameraInstance, {
keepScanning(this.$refs.video, {
detectHandler,
locateHandler: this.onLocate,
minDelay: this.scanInterval
Expand All @@ -200,15 +195,18 @@ export default {
onLocate(location) {
if (this.trackRepaintFunction === undefined || location === null) {
this.clearTrackingLayer();
this.clearCanvas(this.$refs.trackingLayer);
} else {
this.repaintTrackingLayer(location);
const video = this.$refs.video;
const canvas = this.$refs.trackingLayer;
if (video !== undefined && canvas !== undefined) {
this.repaintTrackingLayer(video, canvas, location);
}
}
},
repaintTrackingLayer(location) {
const video = this.$refs.video;
const canvas = this.$refs.trackingLayer;
repaintTrackingLayer(video, canvas, location) {
const ctx = canvas.getContext("2d");
// The visually occupied area of the video element.
Expand Down Expand Up @@ -254,35 +252,11 @@ export default {
});
},
clearTrackingLayer() {
const canvas = this.$refs.trackingLayer;
const ctx = canvas.getContext("2d");
window.requestAnimationFrame(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
},
paintPauseFrame(imageData) {
const canvas = this.$refs.pauseFrame;
const ctx = canvas.getContext("2d");
window.requestAnimationFrame(() => {
canvas.width = imageData.width;
canvas.height = imageData.height;
ctx.putImageData(imageData, 0, 0);
});
},
clearPauseFrame() {
const canvas = this.$refs.pauseFrame;
clearCanvas(canvas) {
const ctx = canvas.getContext("2d");
window.requestAnimationFrame(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
};
</script>
Expand Down
5 changes: 0 additions & 5 deletions src/misc/camera.js
@@ -1,5 +1,4 @@
import { StreamApiNotSupportedError, InsecureContextError } from "./errors.js";
import { imageDataFromVideo } from "./image-data.js";
import { eventOn, timeout } from "callforth";
import shimGetUserMedia from "./shimGetUserMedia";

Expand All @@ -18,10 +17,6 @@ class Camera {
});
}

captureFrame() {
return imageDataFromVideo(this.videoEl);
}

getCapabilities() {
const [track] = this.stream.getVideoTracks();
// Firefox does not yet support getCapabilities as of August 2020
Expand Down
64 changes: 0 additions & 64 deletions src/misc/image-data.js

This file was deleted.

0 comments on commit 96cd30a

Please sign in to comment.