init commit
This commit is contained in:
327
src/services/web/index.js
Normal file
327
src/services/web/index.js
Normal file
@@ -0,0 +1,327 @@
|
||||
import { createUUID } from "../../utils/uuid";
|
||||
import { getConfig } from "../config";
|
||||
|
||||
const scanWeb = {
|
||||
uuid: null,
|
||||
finish: true
|
||||
}
|
||||
|
||||
function removeEl(id) {
|
||||
try {
|
||||
let el = document.getElementById(id);
|
||||
document.body.removeChild(el);
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
function createEl(tagName, id, style, appendChild) {
|
||||
let el = document.getElementById(id);
|
||||
if (!el) {
|
||||
el = document.createElement(tagName);
|
||||
el.id = id;
|
||||
el.style = style;
|
||||
appendChild && document.body.appendChild(el);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
function canvasDrawLine(context, width, begin, end, color) {
|
||||
context.beginPath();
|
||||
context.moveTo(width - begin.x, begin.y);
|
||||
context.lineTo(width - end.x, end.y);
|
||||
context.lineWidth = 4;
|
||||
context.strokeStyle = color;
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
function getBarcodeFormats(scanType) {
|
||||
let formats = [];
|
||||
if (!scanType) {
|
||||
scanType = ["qrCode", "barCode"];
|
||||
}
|
||||
if (scanType.includes('qrCode')) {
|
||||
formats.push('qr_code');
|
||||
}
|
||||
if (scanType.includes('barCode')) {
|
||||
formats.push(
|
||||
'ean_13',
|
||||
'ean_8',
|
||||
'code_128',
|
||||
'code_39',
|
||||
'codabar',
|
||||
'upc_a',
|
||||
'upc_e',
|
||||
'itf',
|
||||
'aztec',
|
||||
'data_matrix',
|
||||
'pdf417'
|
||||
);
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
|
||||
function createBarcodeDetector(scanType) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (typeof BarcodeDetector === 'undefined') {
|
||||
throw new Error("BarcodeDetector is not supported");
|
||||
}
|
||||
const formats = getBarcodeFormats(scanType);
|
||||
if (BarcodeDetector.getSupportedFormats) {
|
||||
return BarcodeDetector.getSupportedFormats().then(supportedFormats => {
|
||||
const supported = formats.filter(format => supportedFormats.indexOf(format) !== -1);
|
||||
if (!supported.length) {
|
||||
throw new Error("No supported barcode formats");
|
||||
}
|
||||
return new BarcodeDetector({ formats: supported });
|
||||
});
|
||||
}
|
||||
return new BarcodeDetector({ formats });
|
||||
});
|
||||
}
|
||||
|
||||
function drawBarcode(context, width, barcode) {
|
||||
const cornerPoints = barcode.cornerPoints;
|
||||
if (cornerPoints && cornerPoints.length) {
|
||||
for (let i = 0; i < cornerPoints.length; i++) {
|
||||
canvasDrawLine(context, width, cornerPoints[i], cornerPoints[(i + 1) % cornerPoints.length], "#FF3B58");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (barcode.boundingBox) {
|
||||
const rect = barcode.boundingBox;
|
||||
const points = [
|
||||
{ x: rect.x, y: rect.y },
|
||||
{ x: rect.x + rect.width, y: rect.y },
|
||||
{ x: rect.x + rect.width, y: rect.y + rect.height },
|
||||
{ x: rect.x, y: rect.y + rect.height }
|
||||
];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
canvasDrawLine(context, width, points[i], points[(i + 1) % points.length], "#FF3B58");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isSupportWebScan() {
|
||||
return typeof navigator !== 'undefined'
|
||||
&& navigator.mediaDevices
|
||||
&& navigator.mediaDevices.getUserMedia
|
||||
&& typeof BarcodeDetector !== 'undefined';
|
||||
}
|
||||
|
||||
export function isSupportImageScan() {
|
||||
return typeof document !== 'undefined'
|
||||
&& typeof BarcodeDetector !== 'undefined'
|
||||
&& typeof URL !== 'undefined'
|
||||
&& URL.createObjectURL;
|
||||
}
|
||||
|
||||
export function stopScanForWeb() {
|
||||
return Promise.resolve().then(() => {
|
||||
scanWeb.uuid = null;
|
||||
})
|
||||
}
|
||||
|
||||
function chooseImageFile() {
|
||||
return new Promise(resolve => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "image/*";
|
||||
input.style.display = "none";
|
||||
let finished = false;
|
||||
let finish = file => {
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
finished = true;
|
||||
removeEl("__webscan_image_input__");
|
||||
resolve(file);
|
||||
};
|
||||
input.id = "__webscan_image_input__";
|
||||
input.onchange = () => {
|
||||
finish(input.files && input.files[0]);
|
||||
};
|
||||
input.oncancel = () => {
|
||||
finish(null);
|
||||
};
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
});
|
||||
}
|
||||
|
||||
function detectImageFile(detector, file) {
|
||||
if (!file) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
if (typeof createImageBitmap !== 'undefined') {
|
||||
return createImageBitmap(file).then(image => {
|
||||
return detector.detect(image).then(barcodes => {
|
||||
image.close && image.close();
|
||||
return barcodes && barcodes[0];
|
||||
}).catch(err => {
|
||||
image.close && image.close();
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
const url = URL.createObjectURL(file);
|
||||
image.onload = () => {
|
||||
detector.detect(image).then(barcodes => {
|
||||
URL.revokeObjectURL(url);
|
||||
resolve(barcodes && barcodes[0]);
|
||||
}).catch(err => {
|
||||
URL.revokeObjectURL(url);
|
||||
reject(err);
|
||||
});
|
||||
};
|
||||
image.onerror = err => {
|
||||
URL.revokeObjectURL(url);
|
||||
reject(err);
|
||||
};
|
||||
image.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
export function startScanForImage() {
|
||||
return createBarcodeDetector(getConfig("scanType")).then(detector => {
|
||||
return chooseImageFile().then(file => detectImageFile(detector, file));
|
||||
}).then(code => {
|
||||
if (code && code.rawValue) {
|
||||
return {
|
||||
result: code.rawValue
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: "未识别到二维码或条形码"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function startScanForWeb(canvasStyle, onResult) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
scanWeb.uuid = createUUID();
|
||||
scanWeb.finish = false;
|
||||
let videoEl = createEl("video",
|
||||
"__webscan_video__",
|
||||
"display: none", false);
|
||||
let canvasDisplay = "";
|
||||
let canvasBaseStyle = canvasStyle || "position: fixed; width: 300px; height: 240px; top: 0; left: 0; z-index: 9999;";
|
||||
let canvasEl = createEl("canvas",
|
||||
"__webscan_canvas__",
|
||||
canvasBaseStyle + " display: none;", true);
|
||||
canvasDisplay = canvasEl.style.display;
|
||||
canvasEl.style.cssText = canvasBaseStyle;
|
||||
canvasDisplay = canvasEl.style.display;
|
||||
canvasEl.style.display = "none";
|
||||
let context = canvasEl.getContext("2d");
|
||||
let currentUuid = scanWeb.uuid;
|
||||
videoEl.width = 300;
|
||||
videoEl.height = 300;
|
||||
videoEl.uuid = scanWeb.uuid;
|
||||
createBarcodeDetector(getConfig("scanType")).then(detector => {
|
||||
return navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
facingMode: "environment"
|
||||
}
|
||||
}).then(stream => {
|
||||
return {
|
||||
detector,
|
||||
stream
|
||||
};
|
||||
});
|
||||
}).then(function (options) {
|
||||
const detector = options.detector;
|
||||
const stream = options.stream;
|
||||
videoEl.srcObject = stream;
|
||||
videoEl.setAttribute("playsinline", true); // iOS使用
|
||||
videoEl.play();
|
||||
let closeWebScan = getConfig("closeWebScan", () => {
|
||||
// no thing
|
||||
});
|
||||
let displayWebScan = getConfig("displayWebScan", (canvasEl, cancal) => {
|
||||
// no thing
|
||||
});
|
||||
displayWebScan && displayWebScan(canvasEl, () => {
|
||||
stopScanForWeb();
|
||||
});
|
||||
canvasEl.style.display = "none";
|
||||
let detecting = false;
|
||||
let displayed = false;
|
||||
let closed = false;
|
||||
let close = () => {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
try {
|
||||
stream.getTracks()[0].stop();
|
||||
} catch (_e) { }
|
||||
closeWebScan && closeWebScan();
|
||||
};
|
||||
let tick = () => {
|
||||
try {
|
||||
if (videoEl.readyState === videoEl.HAVE_ENOUGH_DATA && !detecting) {
|
||||
canvasEl.height = videoEl.videoHeight;
|
||||
canvasEl.width = videoEl.videoWidth;
|
||||
context.setTransform(-1, 0, 0, 1, canvasEl.width, 0);
|
||||
context.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
if (!displayed) {
|
||||
displayed = true;
|
||||
canvasEl.style.display = canvasDisplay || "";
|
||||
}
|
||||
detecting = true;
|
||||
detector.detect(videoEl).then(barcodes => {
|
||||
const code = barcodes && barcodes[0];
|
||||
if (code && code.rawValue && scanWeb.uuid == currentUuid) {
|
||||
if (onResult && !onResult(code.rawValue)) {
|
||||
return;
|
||||
}
|
||||
drawBarcode(context, canvasEl.width, code);
|
||||
scanWeb.uuid = null;
|
||||
scanWeb.finish = true;
|
||||
close();
|
||||
resolve({
|
||||
result: code.rawValue
|
||||
})
|
||||
}
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
detecting = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
detecting = false;
|
||||
}
|
||||
if (scanWeb.uuid == currentUuid) {
|
||||
requestAnimationFrame(() => {
|
||||
tick()
|
||||
});
|
||||
} else {
|
||||
if (!scanWeb.finish) {
|
||||
reject({ cancel: 1 });
|
||||
scanWeb.finish = true;
|
||||
}
|
||||
close();
|
||||
}
|
||||
if (scanWeb.finish) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(() => {
|
||||
tick();
|
||||
});
|
||||
}).catch(err => {
|
||||
reject({ error: err });
|
||||
});
|
||||
} catch (e) {
|
||||
reject({ error: e });
|
||||
}
|
||||
}).finally(() => {
|
||||
removeEl("__webscan_video__");
|
||||
removeEl("__webscan_canvas__");
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user