getUserMedia了解 HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头、麦克风)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。 点击查看getUserMedia的api
代码 Vue.js版
HTML <template> <div id="scanner"> <div class="model"> <div class="scanner-view"> <div class="scanner-view-arrow arrow1"></div> <div class="scanner-view-arrow arrow2"></div> <div class="scanner-view-arrow arrow3"></div> <div class="scanner-view-arrow arrow4"></div> <div class="scanner-line"></div> </div> </div> <video class="video-view" ref="video" autoplay playsinline="true" webkit-playsinline="true"></video> <canvas ref="canvas" width="478" height="850" style="display: none"></canvas> </div> </template> JS <script> import jsQR from "jsqr"; import Quagga from "quagga"; export default { name: '', data() { return { cameraWidth: 0, cameraHeight: 0 } }, methods: { initVideo(constrains){ let _this = this; if(navigator.mediaDevices.getUserMedia){ //最新标准API navigator.mediaDevices.getUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError); } else if (navigator.webkitGetUserMedia){ //webkit内核浏览器 navigator.webkitGetUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError); } else if (navigator.mozGetUserMedia){ //Firefox浏览器 navagator.mozGetUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError); } else if (navigator.getUserMedia){ //旧版API navigator.getUserMedia(constrains).then(_this.videoSuccess).catch(_this.videoError); } }, videoSuccess(stream){ let video = this.$refs.video, _this = this; //将视频流设置为video元素的源 video.srcObject = stream; //播放视频 video.play(); video.oncanplay = function () { // 摄像头分辨率,手机480x640 console.log('摄像头分辨率'); console.log(video.videoWidth,video.videoHeight); _this.cameraWidth = video.videoWidth; _this.cameraHeight = video.videoHeight; // 发送图片进行识别 _this.readImg(); }; }, videoError(error){ console.log("访问用户媒体设备失败:",error.name,error.message); }, readImg(){ let video = this.$refs.video, canvas = this.$refs.canvas, context = canvas.getContext("2d"), _this = this; let timer = setInterval(function () { context.drawImage(video,0,0,_this.cameraWidth,_this.cameraHeight,0,0,478,850); // 扫码条形码 let imgUri = canvas.toDataURL(); _this.readBarcode(imgUri,timer); // 扫码二维码 let imageData = context.getImageData(0, 0, 478, 850); _this.readQrcode(imageData.data,timer); },1000) }, readBarcode(imgBase64,timer){ let _this = this; Quagga.decodeSingle({ inputStream: { size: 1920 }, locator: { patchSize: "medium", halfSample: false }, decoder: { readers: [{ format: "code_128_reader", config: {} }] }, locate: true, src: imgBase64 }, function(result){ if (result){ if(result.codeResult) { console.log(result.codeResult); clearInterval(timer); _this.$emit('ondata',result.codeResult.code); // alert("扫码成功,结果是..."+result.codeResult.code); } else { console.log("正在扫条形码...not detected"); } }else { console.log("正在扫条形码...not detected"); } }); }, readQrcode(data,timer){ let _this = this; let code = jsQR(data, 478, 850, { inversionAttempts: "dontInvert", }); if (code){ clearInterval(timer); _this.$emit('ondata',code.data); // alert('扫码成功,结果是...' + code.data); }else { console.log('正在扫二维码...'); } } }, mounted(){ if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia){ //调用用户媒体设备,访问摄像头 this.initVideo({ video:{ height: 800, facingMode: { // 强制后置摄像头 exact: "environment" } } }); } else { alert("你的浏览器不支持访问用户媒体设备"); } } } </script> CSS <style scoped> #scanner { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; } .model{ box-sizing: border-box; width: 100vw; height: 100vh; position: relative; z-index: 88; border-top: calc((100vh - 60vw)/2) solid rgba(0,0,0,.2); border-bottom: calc((100vh - 60vw)/2) solid rgba(0,0,0,.2); border-right: 20vw solid rgba(0,0,0,.2); border-left: 20vw solid rgba(0,0,0,.2); } .scanner-view{ width: 100%; height: 100%; position: relative; border: 1px solid rgba(255,255,255,.3); z-index: 89; } .scanner-line{ position: absolute; width: 100%; height: 1px; background: #49FF46; border-radius: 20px; z-index: 90; animation: myScan 1s infinite alternate; } @keyframes myScan{ from { top: 0; } to { top: 34vh; } } .scanner-view-arrow{ position: absolute; width: 5vw; height: 5vw; border: 2px solid #09bb07; } .scanner-view-arrow.arrow1{ top: -1px; left: 0px; z-index: 99; border-right: none; border-bottom: none; } .scanner-view-arrow.arrow2{ top: -1px; right: 0px; z-index: 99; border-left: none; border-bottom: none; } .scanner-view-arrow.arrow3{ bottom: -1px; left: 0px; z-index: 99; border-right: none; border-top: none; } .scanner-view-arrow.arrow4{ bottom: -1px; right: 0px; z-index: 99; border-left: none; border-top: none; } .video-view{ position: absolute; width: 100vw; height: 100vh; object-fit: cover; top: 0px; left: 0px; z-index: 80; } </style> 注意事项 video标签里面的视频会用黑边,可以在video标签的css中加入 object-fit: cover;
...