前言
最近項目要求實現純網頁中的掃碼功能,網上搜素了一些資料, 大都不盡如意。
主要使用MediaDevices.getUserMedia()獲取攝像頭然后通過掃碼解析接口實
現二維碼及條形碼的解析,且識別率不高。經過測試后,我最終選擇了html5-qrcode
效果圖如下
scan.gif
主要思路和步驟
- 使用html5-qrcode github:https://github.com/mebjas/html5-qrcode
- 若是vue項目則直接npm i html5-qrcode
- 使用Html5Qrcode自定義面版
- 獲取相機權限并獲取相機ID掃描解析二維碼
- 沒有獲取到相機ID則傳遞約束來代替相機ID
- 使用本地圖庫或者拍照解析二維碼
說明:
- 若要使用相機必須在https協(xié)議之下
- 目前內聯(lián)掃描和基于文件的掃描是互斥的
如果您想同時使用兩者玻驻,請使用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
);
}
//掃碼解析成功后按照自己的需求做后續(xù)的操作
function onScanSuccess(decodedText, decodedResult) {
res.innerText = "掃碼成功結果:\n" + decodedText;
}
//掃碼解析失敗后按照自己的需求做后續(xù)的操作
function onScanFailure(error) {
res.innerText = "掃碼失敗:\n" + error;
}
</script>
</body>
</html>