Add modal for barcode scanner
This commit is contained in:
parent
52dfb0522f
commit
8191799760
322
src/containers/BarcodeScanner/Barcode.js
Normal file
322
src/containers/BarcodeScanner/Barcode.js
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
import {Paper, IconButton} from "@material-ui/core";
|
||||||
|
import { Close as CloseIcon } from '@material-ui/icons'
|
||||||
|
import useStyles from "./styles";
|
||||||
|
|
||||||
|
const Barcode = ({ isScannerOpen, handleClose }) => {
|
||||||
|
const [decodecBarcode, setDecodedBarcode] = useState('');
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
stop()
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
let localMediaStream = null;
|
||||||
|
const dimensions = {
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
start: 0,
|
||||||
|
end: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const elements = {
|
||||||
|
video: null,
|
||||||
|
canvas: null,
|
||||||
|
ctx: null,
|
||||||
|
canvasg: null,
|
||||||
|
ctxg: null
|
||||||
|
}
|
||||||
|
|
||||||
|
const upc = {
|
||||||
|
'0': [3, 2, 1, 1],
|
||||||
|
'1': [2, 2, 2, 1],
|
||||||
|
'2': [2, 1, 2, 2],
|
||||||
|
'3': [1, 4, 1, 1],
|
||||||
|
'4': [1, 1, 3, 2],
|
||||||
|
'5': [1, 2, 3, 1],
|
||||||
|
'6': [1, 1, 1, 4],
|
||||||
|
'7': [1, 3, 1, 2],
|
||||||
|
'8': [1, 2, 1, 3],
|
||||||
|
'9': [3, 1, 1, 2]
|
||||||
|
};
|
||||||
|
|
||||||
|
const check = {
|
||||||
|
'oooooo': '0',
|
||||||
|
'ooeoee': '1',
|
||||||
|
'ooeeoe': '2',
|
||||||
|
'ooeeeo': '3',
|
||||||
|
'oeooee': '4',
|
||||||
|
'oeeooe': '5',
|
||||||
|
'oeeeoo': '6',
|
||||||
|
'oeoeoe': '7',
|
||||||
|
'oeoeeo': '8',
|
||||||
|
'oeeoeo': '9'
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
strokeColor: '#f00',
|
||||||
|
start: 0.1,
|
||||||
|
end: 0.9,
|
||||||
|
threshold: 160,
|
||||||
|
quality: 0.9,
|
||||||
|
delay: 100,
|
||||||
|
video: '#barcodevideo',
|
||||||
|
canvas: '#barcodecanvas',
|
||||||
|
canvasg: '#barcodecanvasg'
|
||||||
|
}
|
||||||
|
|
||||||
|
const play = () => {
|
||||||
|
dimensions.height = elements.video.videoHeight;
|
||||||
|
dimensions.width = elements.video.videoWidth;
|
||||||
|
|
||||||
|
dimensions.start = dimensions.width * config.start;
|
||||||
|
dimensions.end = dimensions.width * config.end;
|
||||||
|
|
||||||
|
elements.canvas.width = dimensions.width;
|
||||||
|
elements.canvas.height = dimensions.height;
|
||||||
|
elements.canvasg.width = dimensions.width;
|
||||||
|
elements.canvasg.height = dimensions.height;
|
||||||
|
|
||||||
|
drawLine(elements.ctxg);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
const snapshot = takeSnapshot(elements.ctx, elements.video)
|
||||||
|
processSnapshot(snapshot)
|
||||||
|
}, config.delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
elements.video.pause();
|
||||||
|
localMediaStream.getTracks()[0].stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = () => {
|
||||||
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||||
|
|
||||||
|
elements.video = document.querySelector(config.video);
|
||||||
|
elements.canvas = document.querySelector(config.canvas);
|
||||||
|
elements.ctx = elements.canvas.getContext('2d');
|
||||||
|
elements.canvasg = document.querySelector(config.canvasg);
|
||||||
|
elements.ctxg = elements.canvasg.getContext('2d');
|
||||||
|
|
||||||
|
if (navigator.getUserMedia) {
|
||||||
|
navigator.getUserMedia({audio: false, video: true}, (stream) => {
|
||||||
|
elements.video.srcObject = stream;
|
||||||
|
localMediaStream = stream;
|
||||||
|
}, (error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.video.addEventListener('canplay', play, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const takeSnapshot = (canvasElement, videoElement) => {
|
||||||
|
canvasElement.drawImage(videoElement, 0, 0, dimensions.width, dimensions.height);
|
||||||
|
return canvasElement.getImageData(dimensions.start, dimensions.height * 0.5, dimensions.end - dimensions.start, 1).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processSnapshot = (snapshot) => {
|
||||||
|
|
||||||
|
const bars = [];
|
||||||
|
const pixels = [];
|
||||||
|
let pixelBars = [];
|
||||||
|
|
||||||
|
// convert to grayscale
|
||||||
|
for (let i = 0; i < snapshot.length; i += 4) {
|
||||||
|
pixels.push(Math.round(snapshot[i] * 0.2126 + snapshot[i + 1] * 0.7152 + snapshot[ i + 2] * 0.0722));
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize and convert to binary
|
||||||
|
const minPixelValue = Math.min(...pixels);
|
||||||
|
const maxPixelValue = Math.max(...pixels);
|
||||||
|
|
||||||
|
const binary = pixels.reduce((arr, val) => {
|
||||||
|
const binaryValue = Math.round((val - minPixelValue) / (maxPixelValue - minPixelValue) * 255) > config.threshold ? 1 : 0
|
||||||
|
return [...arr, binaryValue]
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// determine bar widths
|
||||||
|
let current = binary[0];
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
if (binary[i] === current) {
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
pixelBars.push(count);
|
||||||
|
count = 1;
|
||||||
|
current = binary[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixelBars.push(count);
|
||||||
|
|
||||||
|
// quality check
|
||||||
|
if (pixelBars.length < (3 + 24 + 5 + 24 + 3 + 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find starting sequence
|
||||||
|
const minFactor = 0.5;
|
||||||
|
const maxFactor = 1.5;
|
||||||
|
let startIndex = 0;
|
||||||
|
|
||||||
|
for (let i = 3; i < pixelBars.length; i++) {
|
||||||
|
const refLength = (pixelBars[i] + pixelBars[i - 1] + pixelBars[i - 2]) / 3;
|
||||||
|
if (
|
||||||
|
(pixelBars[i] > (minFactor * refLength) || pixelBars[i] < (maxFactor * refLength))
|
||||||
|
&& (pixelBars[i-1] > (minFactor * refLength) || pixelBars[i-1] < (maxFactor * refLength))
|
||||||
|
&& (pixelBars[i-2] > (minFactor * refLength) || pixelBars[i-2] < (maxFactor * refLength))
|
||||||
|
&& (pixelBars[i-3] > 3 * refLength)
|
||||||
|
) {
|
||||||
|
startIndex = i - 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log("startIndex: " + startIndex );
|
||||||
|
// return if no starting sequence found
|
||||||
|
if (startIndex === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// discard leading and trailing patterns
|
||||||
|
pixelBars = pixelBars.slice(startIndex, startIndex + 3 + 24 + 5 + 24 + 3);
|
||||||
|
|
||||||
|
console.log("pixelBars: " + pixelBars );
|
||||||
|
|
||||||
|
// calculate relative widths
|
||||||
|
const ref = (pixelBars[0] + pixelBars[1] + pixelBars[2]) / 3;
|
||||||
|
|
||||||
|
for (let i = 0; i < pixelBars.length; i++) {
|
||||||
|
bars.push(Math.round(pixelBars[i] / ref * 100) / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyze pattern
|
||||||
|
analyzePattern(bars);
|
||||||
|
}
|
||||||
|
|
||||||
|
const analyzePattern = (bars) => {
|
||||||
|
|
||||||
|
console.clear();
|
||||||
|
console.log("analyzing");
|
||||||
|
// determine parity first digit and reverse sequence if necessary
|
||||||
|
const first = normalize(bars.slice(3, 3 + 4), 7);
|
||||||
|
if (!isOdd(Math.round(first[1] + first[3]))) {
|
||||||
|
bars = bars.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
// split into digits
|
||||||
|
const digits = [
|
||||||
|
normalize(bars.slice(3, 3 + 4), 7),
|
||||||
|
normalize(bars.slice(7, 7 + 4), 7),
|
||||||
|
normalize(bars.slice(11, 11 + 4), 7),
|
||||||
|
normalize(bars.slice(15, 15 + 4), 7),
|
||||||
|
normalize(bars.slice(19, 19 + 4), 7),
|
||||||
|
normalize(bars.slice(23, 23 + 4), 7),
|
||||||
|
normalize(bars.slice(32, 32 + 4), 7),
|
||||||
|
normalize(bars.slice(36, 36 + 4), 7),
|
||||||
|
normalize(bars.slice(40, 40 + 4), 7),
|
||||||
|
normalize(bars.slice(44, 44 + 4), 7),
|
||||||
|
normalize(bars.slice(48, 48 + 4), 7),
|
||||||
|
normalize(bars.slice(52, 52 + 4), 7)
|
||||||
|
]
|
||||||
|
|
||||||
|
console.log("digits: " + digits);
|
||||||
|
|
||||||
|
// determine parity and reverse if necessary
|
||||||
|
const parities = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
if (parity(digits[i])) {
|
||||||
|
parities.push('o');
|
||||||
|
} else {
|
||||||
|
parities.push('e');
|
||||||
|
digits[i] = digits[i].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify digits
|
||||||
|
const result = [];
|
||||||
|
let quality = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < digits.length; i++) {
|
||||||
|
|
||||||
|
let distance = 9;
|
||||||
|
let bestKey = '';
|
||||||
|
|
||||||
|
for (let key in upc) {
|
||||||
|
if (maxDistance(digits[i], upc[key]) < distance) {
|
||||||
|
distance = maxDistance(digits[i], upc[key]);
|
||||||
|
bestKey = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(bestKey);
|
||||||
|
if (distance > quality) {
|
||||||
|
quality = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
console.log("result: " + result);
|
||||||
|
// check digit
|
||||||
|
const checkDigit = check[parities.join('')];
|
||||||
|
|
||||||
|
console.log("quality: " + quality);
|
||||||
|
// output
|
||||||
|
if(quality < config.quality) {
|
||||||
|
const barcode = checkDigit + result.join('')
|
||||||
|
setDecodedBarcode(barcode)
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalize = (input, total) => {
|
||||||
|
const sum = input.reduce((acc, val) => acc + val, 0);
|
||||||
|
return input.reduce((acc, val) => [...acc, val / sum * total], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isOdd = (num) => num % 2;
|
||||||
|
|
||||||
|
const maxDistance = (a, b) =>
|
||||||
|
a.reduce((max, value, index) => {
|
||||||
|
const current = Math.abs(value - b[index])
|
||||||
|
return current > max ? current : max
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
const parity = (digit) => isOdd(Math.round(digit[1] + digit[3]))
|
||||||
|
|
||||||
|
const drawLine = (canvasContext) => {
|
||||||
|
canvasContext.strokeStyle = config.strokeColor;
|
||||||
|
canvasContext.lineWidth = 3;
|
||||||
|
canvasContext.beginPath();
|
||||||
|
canvasContext.moveTo(dimensions.start, dimensions.height * 0.5);
|
||||||
|
canvasContext.lineTo(dimensions.end, dimensions.height * 0.5);
|
||||||
|
canvasContext.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
init()
|
||||||
|
return () => elements.video.removeEventListener('canplay', play, false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper className={classes.container}>
|
||||||
|
<div className={classes.close}>
|
||||||
|
<IconButton onClick={onClose}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
<div className={classes.inner}>
|
||||||
|
<video className={classes.video} id="barcodevideo" autoPlay />
|
||||||
|
<canvas className={classes.canvasg} id="barcodecanvasg" />
|
||||||
|
</div>
|
||||||
|
<canvas className={classes.canvas} id="barcodecanvas" />
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Barcode;
|
@ -1,293 +1,28 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, {useState} from 'react';
|
||||||
import {IconButton, Grid} from '@material-ui/core'
|
import {IconButton} from "@material-ui/core";
|
||||||
import {CropFree as CropFreeIcon} from '@material-ui/icons'
|
import {CropFree as CropFreeIcon} from '@material-ui/icons'
|
||||||
import './styles.css'
|
import Barcode from './Barcode'
|
||||||
|
|
||||||
const BarcodeScanner = () => {
|
const BarcodeScanner = () => {
|
||||||
const [decodecBarcode, setDecodedBarcode] = useState('');
|
const [isScannerOpen, setIsScannerOpen] = useState(false)
|
||||||
|
|
||||||
const dimensions = {
|
const handleOpen = () => {
|
||||||
height: 0,
|
setIsScannerOpen(true)
|
||||||
width: 0,
|
|
||||||
start: 0,
|
|
||||||
end: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const elements = {
|
const handleClose = () => {
|
||||||
video: null,
|
setIsScannerOpen(false)
|
||||||
canvas: null,
|
|
||||||
ctx: null,
|
|
||||||
canvasg: null,
|
|
||||||
ctxg: null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const upc = {
|
|
||||||
'0': [3, 2, 1, 1],
|
|
||||||
'1': [2, 2, 2, 1],
|
|
||||||
'2': [2, 1, 2, 2],
|
|
||||||
'3': [1, 4, 1, 1],
|
|
||||||
'4': [1, 1, 3, 2],
|
|
||||||
'5': [1, 2, 3, 1],
|
|
||||||
'6': [1, 1, 1, 4],
|
|
||||||
'7': [1, 3, 1, 2],
|
|
||||||
'8': [1, 2, 1, 3],
|
|
||||||
'9': [3, 1, 1, 2]
|
|
||||||
};
|
|
||||||
|
|
||||||
const check = {
|
|
||||||
'oooooo': '0',
|
|
||||||
'ooeoee': '1',
|
|
||||||
'ooeeoe': '2',
|
|
||||||
'ooeeeo': '3',
|
|
||||||
'oeooee': '4',
|
|
||||||
'oeeooe': '5',
|
|
||||||
'oeeeoo': '6',
|
|
||||||
'oeoeoe': '7',
|
|
||||||
'oeoeeo': '8',
|
|
||||||
'oeeoeo': '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
strokeColor: '#f00',
|
|
||||||
start: 0.1,
|
|
||||||
end: 0.9,
|
|
||||||
threshold: 160,
|
|
||||||
quality: 0.45,
|
|
||||||
delay: 100,
|
|
||||||
video: '#barcodevideo',
|
|
||||||
canvas: '#barcodecanvas',
|
|
||||||
canvasg: '#barcodecanvasg'
|
|
||||||
}
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
|
||||||
|
|
||||||
elements.video = document.querySelector(config.video);
|
|
||||||
elements.canvas = document.querySelector(config.canvas);
|
|
||||||
elements.ctx = elements.canvas.getContext('2d');
|
|
||||||
elements.canvasg = document.querySelector(config.canvasg);
|
|
||||||
elements.ctxg = elements.canvasg.getContext('2d');
|
|
||||||
|
|
||||||
if (navigator.getUserMedia) {
|
|
||||||
navigator.getUserMedia({audio: false, video: true}, function(stream) {
|
|
||||||
elements.video.srcObject = stream;
|
|
||||||
}, function(error) {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
elements.video.addEventListener('canplay', () => {
|
|
||||||
|
|
||||||
dimensions.height = elements.video.videoHeight;
|
|
||||||
dimensions.width = elements.video.videoWidth;
|
|
||||||
|
|
||||||
dimensions.start = dimensions.width * config.start;
|
|
||||||
dimensions.end = dimensions.width * config.end;
|
|
||||||
|
|
||||||
elements.canvas.width = dimensions.width;
|
|
||||||
elements.canvas.height = dimensions.height;
|
|
||||||
elements.canvasg.width = dimensions.width;
|
|
||||||
elements.canvasg.height = dimensions.height;
|
|
||||||
|
|
||||||
drawLine(elements.ctxg);
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
const snapshot = takeSnapshot(elements.ctx, elements.video)
|
|
||||||
processSnapshot(snapshot)
|
|
||||||
}, config.delay);
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const takeSnapshot = (canvasElement, videoElement) => {
|
|
||||||
canvasElement.drawImage(videoElement, 0, 0, dimensions.width, dimensions.height);
|
|
||||||
return canvasElement.getImageData(dimensions.start, dimensions.height * 0.5, dimensions.end - dimensions.start, 1).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const processSnapshot = (snapshot) => {
|
|
||||||
|
|
||||||
const bars = [];
|
|
||||||
const pixels = [];
|
|
||||||
let pixelBars = [];
|
|
||||||
|
|
||||||
// convert to grayscale
|
|
||||||
for (let i = 0; i < snapshot.length; i += 4) {
|
|
||||||
pixels.push(Math.round(snapshot[i] * 0.2126 + snapshot[i + 1] * 0.7152 + snapshot[ i + 2] * 0.0722));
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize and convert to binary
|
|
||||||
const minPixelValue = Math.min(...pixels);
|
|
||||||
const maxPixelValue = Math.max(...pixels);
|
|
||||||
|
|
||||||
const binary = pixels.reduce((arr, val) => {
|
|
||||||
const binaryValue = Math.round((val - minPixelValue) / (maxPixelValue - minPixelValue) * 255) > config.threshold ? 1 : 0
|
|
||||||
return [...arr, binaryValue]
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// determine bar widths
|
|
||||||
let current = binary[0];
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < binary.length; i++) {
|
|
||||||
if (binary[i] === current) {
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
pixelBars.push(count);
|
|
||||||
count = 1;
|
|
||||||
current = binary[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pixelBars.push(count);
|
|
||||||
|
|
||||||
// quality check
|
|
||||||
if (pixelBars.length < (3 + 24 + 5 + 24 + 3 + 1)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find starting sequence
|
|
||||||
const minFactor = 0.5;
|
|
||||||
const maxFactor = 1.5;
|
|
||||||
let startIndex = 0;
|
|
||||||
|
|
||||||
for (let i = 3; i < pixelBars.length; i++) {
|
|
||||||
const refLength = (pixelBars[i] + pixelBars[i - 1] + pixelBars[i - 2]) / 3;
|
|
||||||
if (
|
|
||||||
(pixelBars[i] > (minFactor * refLength) || pixelBars[i] < (maxFactor * refLength))
|
|
||||||
&& (pixelBars[i-1] > (minFactor * refLength) || pixelBars[i-1] < (maxFactor * refLength))
|
|
||||||
&& (pixelBars[i-2] > (minFactor * refLength) || pixelBars[i-2] < (maxFactor * refLength))
|
|
||||||
&& (pixelBars[i-3] > 3 * refLength)
|
|
||||||
) {
|
|
||||||
startIndex = i - 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return if no starting sequence found
|
|
||||||
if (startIndex === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// discard leading and trailing patterns
|
|
||||||
pixelBars = pixelBars.slice(startIndex, startIndex + 3 + 24 + 5 + 24 + 3);
|
|
||||||
|
|
||||||
// calculate relative widths
|
|
||||||
const ref = (pixelBars[0] + pixelBars[1] + pixelBars[2]) / 3;
|
|
||||||
|
|
||||||
for (let i = 0; i < pixelBars.length; i++) {
|
|
||||||
bars.push(Math.round(pixelBars[i] / ref * 100) / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// analyze pattern
|
|
||||||
analyzePattern(bars);
|
|
||||||
}
|
|
||||||
|
|
||||||
const analyzePattern = (bars) => {
|
|
||||||
// determine parity first digit and reverse sequence if necessary
|
|
||||||
const first = normalize(bars.slice(3, 3 + 4), 7);
|
|
||||||
if (!isOdd(Math.round(first[1] + first[3]))) {
|
|
||||||
bars = bars.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
// split into digits
|
|
||||||
const digits = [
|
|
||||||
normalize(bars.slice(3, 3 + 4), 7),
|
|
||||||
normalize(bars.slice(7, 7 + 4), 7),
|
|
||||||
normalize(bars.slice(11, 11 + 4), 7),
|
|
||||||
normalize(bars.slice(15, 15 + 4), 7),
|
|
||||||
normalize(bars.slice(19, 19 + 4), 7),
|
|
||||||
normalize(bars.slice(23, 23 + 4), 7),
|
|
||||||
normalize(bars.slice(32, 32 + 4), 7),
|
|
||||||
normalize(bars.slice(36, 36 + 4), 7),
|
|
||||||
normalize(bars.slice(40, 40 + 4), 7),
|
|
||||||
normalize(bars.slice(44, 44 + 4), 7),
|
|
||||||
normalize(bars.slice(48, 48 + 4), 7),
|
|
||||||
normalize(bars.slice(52, 52 + 4), 7)
|
|
||||||
]
|
|
||||||
|
|
||||||
// determine parity and reverse if necessary
|
|
||||||
const parities = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
if (parity(digits[i])) {
|
|
||||||
parities.push('o');
|
|
||||||
} else {
|
|
||||||
parities.push('e');
|
|
||||||
digits[i] = digits[i].reverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// identify digits
|
|
||||||
const result = [];
|
|
||||||
let quality = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < digits.length; i++) {
|
|
||||||
|
|
||||||
let distance = 9;
|
|
||||||
let bestKey = '';
|
|
||||||
|
|
||||||
for (let key in upc) {
|
|
||||||
if (maxDistance(digits[i], upc[key]) < distance) {
|
|
||||||
distance = maxDistance(digits[i], upc[key]);
|
|
||||||
bestKey = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push(bestKey);
|
|
||||||
if (distance > quality) {
|
|
||||||
quality = distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// check digit
|
|
||||||
const checkDigit = check[parities.join('')];
|
|
||||||
|
|
||||||
// output
|
|
||||||
if(quality < config.quality) {
|
|
||||||
const barcode = checkDigit + result.join('')
|
|
||||||
setDecodedBarcode(barcode)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalize = (input, total) => {
|
|
||||||
const sum = input.reduce((acc, val) => acc + val, 0);
|
|
||||||
return input.reduce((acc, val) => [...acc, val / sum * total], []);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOdd = (num) => num % 2;
|
|
||||||
|
|
||||||
const maxDistance = (a, b) =>
|
|
||||||
a.reduce((max, value, index) => {
|
|
||||||
const current = Math.abs(value - b[index])
|
|
||||||
return current > max ? current : max
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
const parity = (digit) => isOdd(Math.round(digit[1] + digit[3]))
|
|
||||||
|
|
||||||
const drawLine = (canvasContext) => {
|
|
||||||
canvasContext.strokeStyle = config.strokeColor;
|
|
||||||
canvasContext.lineWidth = 3;
|
|
||||||
canvasContext.beginPath();
|
|
||||||
canvasContext.moveTo(dimensions.start, dimensions.height * 0.5);
|
|
||||||
canvasContext.lineTo(dimensions.end, dimensions.height * 0.5);
|
|
||||||
canvasContext.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
init()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div id="barcode">
|
<IconButton onClick={handleOpen}>
|
||||||
<video id="barcodevideo" autoPlay />
|
<CropFreeIcon />
|
||||||
<canvas id="barcodecanvasg" />
|
</IconButton>
|
||||||
</div>
|
{isScannerOpen ? (
|
||||||
<canvas id="barcodecanvas" />
|
<Barcode isScannerOpen={isScannerOpen} handleClose={handleClose} />
|
||||||
{decodecBarcode === "5449000136350"}
|
) : null}
|
||||||
{decodecBarcode}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default BarcodeScanner;
|
export default BarcodeScanner;
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
#barcodevideo, #barcodecanvas, #barcodecanvasg {
|
|
||||||
height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#barcodecanvasg {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result {
|
|
||||||
font-family: verdana;
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#barcode {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#barcodecanvas {
|
|
||||||
display: none;
|
|
||||||
}
|
|
37
src/containers/BarcodeScanner/styles.js
Normal file
37
src/containers/BarcodeScanner/styles.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {makeStyles} from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
container: {
|
||||||
|
zIndex: theme.zIndex.modal,
|
||||||
|
width: `100%`,
|
||||||
|
height: `100%`,
|
||||||
|
position: `absolute`,
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
inner: {
|
||||||
|
position: `relative`,
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
position: `absolute`,
|
||||||
|
top: 20,
|
||||||
|
right: 20,
|
||||||
|
},
|
||||||
|
video: {
|
||||||
|
height: 400,
|
||||||
|
},
|
||||||
|
canvas: {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
canvasg: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
height: 400,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default useStyles
|
@ -1,108 +1,100 @@
|
|||||||
// import React from 'react';
|
import React from 'react';
|
||||||
// import { useInjectReducer, useInjectSaga } from 'redux-injectors';
|
import { useInjectReducer, useInjectSaga } from 'redux-injectors';
|
||||||
// import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
// import {
|
import {
|
||||||
// Container,
|
Container,
|
||||||
// Grid,
|
Grid,
|
||||||
// Button,
|
Button,
|
||||||
// Typography,
|
Typography,
|
||||||
// FormControlLabel,
|
FormControlLabel,
|
||||||
// Checkbox,
|
Checkbox,
|
||||||
// Link,
|
Link,
|
||||||
// } from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
// import {routes} from "utils";
|
import {routes} from "utils";
|
||||||
// import InputField from 'components/InputField'
|
import InputField from 'components/InputField'
|
||||||
// import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
// import { loginAction } from './actions'
|
import { loginAction } from './actions'
|
||||||
// import useStyles from './styles'
|
import useStyles from './styles'
|
||||||
// import validationSchema from './FormModel/validationSchema'
|
import validationSchema from './FormModel/validationSchema'
|
||||||
// import formInitialValues from './FormModel/formInitialValues'
|
import formInitialValues from './FormModel/formInitialValues'
|
||||||
// import loginFormModel from './FormModel/loginFormModel'
|
import loginFormModel from './FormModel/loginFormModel'
|
||||||
// import saga from "./saga";
|
import saga from "./saga";
|
||||||
//
|
|
||||||
// const {
|
|
||||||
// formId,
|
|
||||||
// formField: {
|
|
||||||
// email,
|
|
||||||
// password
|
|
||||||
// }
|
|
||||||
// } = loginFormModel;
|
|
||||||
//
|
|
||||||
// const key = 'loginPage'
|
|
||||||
//
|
|
||||||
// const Login = () => {
|
|
||||||
// useInjectSaga({ key, saga });
|
|
||||||
// const classes = useStyles()
|
|
||||||
// const dispatch = useDispatch();
|
|
||||||
//
|
|
||||||
// const handleSubmit = (values, actions) => {
|
|
||||||
// dispatch(loginAction(values))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <Container component="main" maxWidth="xs">
|
|
||||||
// <div className={classes.paper}>
|
|
||||||
// <Typography component="h1" variant="h5">
|
|
||||||
// Login to Account
|
|
||||||
// </Typography>
|
|
||||||
// <Formik
|
|
||||||
// initialValues={formInitialValues}
|
|
||||||
// validationSchema={validationSchema}
|
|
||||||
// onSubmit={handleSubmit}
|
|
||||||
// >
|
|
||||||
// {({ isSubmitting }) => (
|
|
||||||
// <Form id={formId}>
|
|
||||||
// <InputField
|
|
||||||
// type="text"
|
|
||||||
// label={email.label}
|
|
||||||
// name={email.name}
|
|
||||||
// variant="outlined"
|
|
||||||
// fullWidth
|
|
||||||
// className={classes.input}
|
|
||||||
// />
|
|
||||||
// <InputField
|
|
||||||
// type="password"
|
|
||||||
// label={password.label}
|
|
||||||
// name={password.name}
|
|
||||||
// variant="outlined"
|
|
||||||
// fullWidth
|
|
||||||
// className={classes.input}
|
|
||||||
// />
|
|
||||||
// <FormControlLabel
|
|
||||||
// control={<Checkbox value="remember" color="primary" />}
|
|
||||||
// label="Remember me"
|
|
||||||
// />
|
|
||||||
// <Button
|
|
||||||
// type="submit"
|
|
||||||
// fullWidth
|
|
||||||
// variant="contained"
|
|
||||||
// color="primary"
|
|
||||||
// className={classes.submit}
|
|
||||||
// disabled={isSubmitting}
|
|
||||||
// >
|
|
||||||
// Sign Up
|
|
||||||
// </Button>
|
|
||||||
// </Form>
|
|
||||||
// )}
|
|
||||||
// </Formik>
|
|
||||||
// <Grid container alignItems="flex-end">
|
|
||||||
// <Grid item>
|
|
||||||
// <Link href={routes.register.path} color="secondary" variant="body2">
|
|
||||||
// Don't have an account? Sign Up
|
|
||||||
// </Link>
|
|
||||||
// </Grid>
|
|
||||||
// </Grid>
|
|
||||||
// </div>
|
|
||||||
// </Container>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// export default Login;
|
|
||||||
|
|
||||||
import BarcodeScanner from "../../containers/BarcodeScanner";
|
const {
|
||||||
import {Container} from "@material-ui/core";
|
formId,
|
||||||
import React from "react";
|
formField: {
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
}
|
||||||
|
} = loginFormModel;
|
||||||
|
|
||||||
export default () => (
|
const key = 'loginPage'
|
||||||
<BarcodeScanner />
|
|
||||||
)
|
const Login = () => {
|
||||||
|
useInjectSaga({ key, saga });
|
||||||
|
const classes = useStyles()
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleSubmit = (values, actions) => {
|
||||||
|
dispatch(loginAction(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container component="main" maxWidth="xs">
|
||||||
|
<div className={classes.paper}>
|
||||||
|
<Typography component="h1" variant="h5">
|
||||||
|
Login to Account
|
||||||
|
</Typography>
|
||||||
|
<Formik
|
||||||
|
initialValues={formInitialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{({ isSubmitting }) => (
|
||||||
|
<Form id={formId}>
|
||||||
|
<InputField
|
||||||
|
type="text"
|
||||||
|
label={email.label}
|
||||||
|
name={email.name}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
className={classes.input}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
type="password"
|
||||||
|
label={password.label}
|
||||||
|
name={password.name}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
className={classes.input}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
control={<Checkbox value="remember" color="primary" />}
|
||||||
|
label="Remember me"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
className={classes.submit}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
<Grid container alignItems="flex-end">
|
||||||
|
<Grid item>
|
||||||
|
<Link href={routes.register.path} color="secondary" variant="body2">
|
||||||
|
Don't have an account? Sign Up
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
||||||
|
Loading…
Reference in New Issue
Block a user