前言
最近项目要求实现纯网页中的扫码功能,网上搜素了一些资料, 大都不尽如意。
主要使用MediaDevices.getUserMedia()获取摄像头然后通过扫码解析接口实
现二维码及条形码的解析,且识别率不高。经过测试后,我最终选择了html5-qrcode
效果图如下
主要思路和步骤
- 使用html5-qrcode github:https://github.com/mebjas/html5-qrcode
- 若是vue项目则直接npm i html5-qrcode
- 使用Html5Qrcode自定义面版
- 获取相机权限并获取相机ID扫描解析二维码
- 没有获取到相机ID则传递约束来代替相机ID
- 使用本地图库或者拍照解析二维码
说明:
- 若要使用相机必须在https协议之下
- 目前内联扫描和基于文件的扫描是互斥的
如果您想同时使用两者,请使用html5QrCode.clear()来清除画布 - PC和真机展示效果不一致,真机请在https下测试
代码如下
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>scan</title>
<script src="https://unpkg.com/html5-qrcode"></script>
<style>
button {
display: block;
width: 100%;
margin: 6px;
outline: none;
height: 40px;
line-height: 40px;
color: #fff;
background-color: #26a2ff;
text-align: center;
border-radius: 4px;
border: none;
cursor: pointer;
}
#upload-input {
opacity: 0;
filter: alpha(opacity=0);
display: inline-block;
width: 100%;
height: 100%;
}
#upload-text {
position: relative;
bottom: 40px;
user-select: none;
}
</style>
</head>
<body>
<!-- 相机、文件方式同时只能使用一个,可根据自己需求修改,如:1.改成下拉框;2.改成tab;3.改成radio等等控制显示隐藏和相应逻辑 -->
<button onclick="useCamera()">使用相机扫一扫方式</button>
<button onclick="useLocal()">
<input type="file" id="upload-input" accept="image/*" value="使用文件方式">
<span id="upload-text">使用文件方式</span>
</button>
<div id="reader"></div>
<h3 id="qr-reader-results"></h3>
<script>
//方式一使用库的ui
// var resultContainer = document.getElementById('qr-reader-results');
// var lastResult, countResults = 0;
// function onScanSuccess(decodedText, decodedResult) {
// if (decodedText !== lastResult) {
// ++countResults;
// lastResult = decodedText;
// document.getElementById('qr-reader-results').innerText = lastResult;
// // Handle on success condition with the decoded message.
// console.log(`Scan result ${decodedText}`, decodedResult);
// }
// }
// var html5QrcodeScanner = new Html5QrcodeScanner("reader", { fps: 10, qrbox: 300 });
// html5QrcodeScanner.render(onScanSuccess);
// var resultContainer = document.getElementById('qr-reader-results');
// var lastResult, countResults = 0;
//1.Html5QrcodeScanner是js提供的ui; 2.Html5Qrcode是自定义面板
let html5QrCode = new Html5Qrcode("reader");
let reader = document.getElementById("reader");
let res = document.getElementById('qr-reader-results');
let uploadInput = document.getElementById('upload-input');
let config = { fps: 10, qrbox: { width: 300, height: 280 } }; //扫一扫相关设置
//使用本地文件
function useLocal() {
reader.style.display = "none";
res.innerText = "";
uploadInput.addEventListener("change", (e) => {
if (e.target.files.length == 0) {
return;
}
const imageFile = e.target.files[0];
html5QrCode
.scanFile(imageFile, true)
.then((decodedText) => {
res.innerText = "扫码成功结果:\n" + decodedText;
})
.catch((err) => {
res.innerText = "扫码失败:\n" + error;
});
});
}
//相机授权
function useCamera() {
reader.style.display = "block";
res.innerText = "";
Html5Qrcode.getCameras()
.then((devices) => {
if (devices && devices.length) {
let cameraId = "";
if (devices.length == 1) {
cameraId = devices[0].id; //前置摄像头
} else {
cameraId = devices[1].id; //后置摄像头
}
if (cameraId) {
startWithCameraId(cameraId);
}
} else {
startWithoutCameraId();
}
})
.catch((err) => {
console.log("没有获取摄像头设备...");
});
}
//带相机ID扫描
function startWithCameraId(cameraId) {
html5QrCode
.start(
{ deviceId: { exact: cameraId } },
config,
onScanSuccess,
onScanFailure
)
.catch((err) => {
console.log("通过摄像头扫码异常....", err);
});
}
//不带相机ID扫描,允许传递约束来代替相机设备 ID
function startWithoutCameraId() {
//environment 表示后置摄像头 换成user则表示前置摄像头
html5QrCode.start(
{ facingMode: "environment" } || {
facingMode: { exact: "environment" },
},
config,
onScanSuccess,
onScanFailure
);
}
//扫码解析成功后按照自己的需求做后续的操作
function onScanSuccess(decodedText, decodedResult) {
res.innerText = "扫码成功结果:\n" + decodedText;
}
//扫码解析失败后按照自己的需求做后续的操作
function onScanFailure(error) {
res.innerText = "扫码失败:\n" + error;
}
</script>
</body>
</html>