説明
As most of you have already noticed, the API of Quagga is neither intuitive nor easy to use. Within this issue I would like you to discuss with me ideas for a new API. The new design should be readable, easy to use and extensible for features to come.
I'm very interested in your suggestions and ideas, so please help me make this API better for everyone. Tell me your current pain-points and let's discuss them in this thread. Based on your feedback and my time available, I'll push a draft of the API in the coming days. Feel free to collaborate.
Requirements
- Work with instances rather than singletons
- Event based, in preparation for reactive streams
- Easy to use with RxJS
- Prefer promises over callbacks
- creating scanners using images or video in a consistent way
Proposal
As it is currently under implementation
Creating a default video-based scanner
Creates a scanner with default settings and the user's camera:
Quagga
.fromVideo({constraints: {facingMode: "environment"}})
.addEventListener('detected', (result) => (console.log(result)));
Creating a default image-based scanner
Creates a scanner that is suitable for static images
Quagga
.fromImage('../test/fixtures/code_39/image-001.jpg', {size: 800})
.addEventListener('processed', (result) => (console.log(result)));
When scanning images, the consumer either expects a result, or an error (no result). Hence, returning a Promise would be handy:
Quagga
.fromImage('../test/fixtures/code_39/image-001.jpg', {size: 800})
.toPromise()
.then((result) => {
console.log(result);
}).catch((result) => {
console.log("not found!");
});
Passing configuration
In most cases, the default configuration is not sufficient and can be adjusted prior to creation:
const customScanner = Quagga
.decoder({readers: ['ean_reader']})
.locator({patchSize: 'medium'});
or pass in the entire configuration at once:
const customScanner = Quagga
.config({decoder: {readers: ['ean_reader']}, locator: {patchSize: 'medium'}});
Starting/Stopping
The general idea is, that everything should be as lazy as possible.
Consumer based
Meaning that the scanner should not start automatically without an event-listener attached. Therefore the processing is started/paused based on the state of the event-listeners. As long as there is a consumer attached, the scanner is active. It should automatically stop/pause when the last consumer is detached via removeEventListener(eventName, callback).
Manually
Another approach would be to start/stop the scanner manually by calling the appropriate methods provided.
RxJS
One reason for redesigning the whole API is the desired move towards a more reactive implementation. With RxJS one could simply write the following code to scan Code 128 and EAN-13 barcodes and react only to the results of interest:
// creating a stream listening on the "processed" events
const processStream = Quagga
.decoder({readers: ['ean_reader', 'code_128_reader']})
.fromVideo({constraints: {facingMode: "environment"}})
.observe('processed');
// create a stream only containing the label and format of a detected barcode
const detectedCodes = processStream
.filter(result => result && result.codeResult && result.codeResult.code)
.map(result => ({label: result.codeResult.code, format: result.codeResult.format});
// create a stream containing all boxes which are detected during scanning
const detecedBoxes = processStream
.filter(result => result.boxes && result.boxes.length > 0)
.map(result => result.boxes);
After creating these streams, one can listen to them:
detectedCodes.subscribe(code => console.log(code));
detecedBoxes.subscribe(boxes => draw(boxes));