js扫描(上传)图片解析二维码

getUserMedia.访问摄像头(扫描以及上传图片)解析二维码

话不多说先上效果图:
在这里插入图片描述

1.准备工作

引用解析二维码的js插件:qrcode.js
核心:qrcode.js中的qrcode.decode方法和qrcode.callback方法

2.通过getUserMedia方法调用摄像头进行扫描解析

首先你要了解getUserMedia的使用方法,这是MDN上的解释:getUserMedia语法

注意:getUserMedia必须在HTTPS协议下才能访问摄像头,并且有的浏览器不支持getUserMedia方法,在这里推荐大家使用Chrome浏览器。

  1. html代码:
<button type="button" class="btn btn-primary" onclick="showPrintModal()">扫描</button>

<div class="modal fade" id="_printModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h4 class="modal-title"></h4>
            </div>
            <div class="modal-body" style="position:relative">
                <div class="qrcode-box" style="position:absolute;left:50%;transform:translateX(-50%);width:300px;height:300px">
                    <span></span>
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
                <video style="width: 100%;height:300px"></video>
                <canvas class="hidden" width="300" height="300"></canvas>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
            </div>
        </div>
    </div>
</div>
  1. css代码:
.qrcode-box:after {
    -webkit-animation: rotateAnim linear 3.68s infinite;
    animation: rotateAnim linear 3.68s infinite;
    content: '';
    position: absolute;
    display: block;
    width: 100%;
    height: 30px;
    background-image: linear-gradient(rgba(0,0,0,0),rgba(31,162,255,1) 90%);
}
/*二维码上下移动的扫描横线*/
@keyframes rotateAnim {
    from {
        top: 0;
    }
    to {
        top: calc(100% - 30px);
    }
}
@-webkit-keyframes rotateAnim {
    from {
        top: 0;
    }
    to {
        top: calc(100% - 30px);
    }
}
/*二维码扫描框的四角*/
.qrcode-box span {
    width: 24px;
    height: 24px;
    position: absolute;
    border: 4px solid #1fa2ff;
}
.qrcode-box span:nth-child( 1) {
    left: -4px;
    top: -4px;
    border-width: 4px 0 0 4px;
}
.qrcode-box span:nth-child( 2) {
    right: -4px;
    top: -4px;
    border-width: 4px 4px 0 0;
}
.qrcode-box span:nth-child( 3) {
    right: -4px;
    bottom: -4px;
    border-width: 0 4px 4px 0;
}
.qrcode-box span:nth-child( 4) {
    left: -4px;
    bottom: -4px;
    border-width: 0 0 4px 4px;
}
  1. js代码:
//二维码扫描模态框
function showPrintModal() {
    //closeVideo:接收视频流对象序列;canvasImg:接收定时器
    var closeVideo, canvasImg;
    var video = $('#_printModal video')[0];
    var canvas = $('#_printModal canvas')[0];
    var context = canvas.getContext('2d');
    //页面切换关闭模态框
    $(document).on("visibilitychange", function () {
        var page = this.visibilityState;
        if (page == "hidden") {
            $('#_printModal').modal('hide');
        }
    });
    //getUserMedia兼容性处理
    var getUserMedia = (constraints, success, error) => {
        if (navigator.mediaDevices.getUserMedia) {
            //最新的标准API
            navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
        } else if (navigator.webkitGetUserMedia) {
            //webkit核心浏览器
            navigator.webkitGetUserMedia(constraints).then(success).catch(error);
        } else if (navigator.mozGetUserMedia) {
            //firfox浏览器
            navigator.mozGetUserMedia(constraints).then(success).catch(error);
        } else if (navigator.getUserMedia) {
            //旧版API
            navigator.getUserMedia(constraints).then(success).catch(error);
        }
    }
    var capture = () => {
        //canvas上生成video里的图像
        context.drawImage(video, 0, 0, 300, 300);
        //toDataURL会返回一个data-URI(base64)然后对此进行解析
        qrcode.decode(canvas.toDataURL('image/png'));
        //解析图片结果回调
        qrcode.callback = (e) => {
            //解析失败都会返回error decoding QR Code"
            if (e != "error decoding QR Code") {//二维码解析成功
                 //停止访问摄像头
            	 closeVideo.stop();
                 //停止定时器
                 clearInterval(canvasImg);
                 //清空canvas上的图片
                 context.clearRect(0, 0, canvas.width, canvas.height);
                 //显示二维码内容
                 alert(e);
            }
        }
    }
    //调用用户媒体设备, 访问摄像头
    if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
    	//所在浏览器支持getUserMedia就调用
        getUserMedia({
            video: {
                width: 500,
                height: 500,
                //user前置摄像头
                facingMode: { exact: "environment" }
            }//基础配置,MDN上还有其他的介绍
        }, (stream) => {//getUserMedia访问摄像头成功的回调
            //显示模态框
            $('#_printModal').modal("show");
            //video接收摄像头捕获的视频流
            video.srcObject = stream;
            //视频流在video标签中播放
            video.play();
            //使用closeVideo变量接收视频流对象序列,用来之后的关闭操作
            closeVideo = stream.getTracks()[0];
            //定时器:每隔0.5s执行capture方法,在canvas上生成图片进行解析
            canvasImg = setInterval(capture, 500);
        }, () => {//getUserMedia访问摄像头失败的回调
            alert("访问用户媒体设备失败");
        });
    } else {//浏览器不存在getUserMedia方法
        alert('不支持访问用户媒体');
    }
}

核心的思路是:在html界面创建video标签和canvas标签,使用getUserMedia方法调用摄像头在video标签中播放,然后每个0.5s获取video标签上的图像,在canvas标签上生成当时获取到的图像图片,通过toDataURL得到图片的data-URI(base64)。最后使用解析二维码插件qrcode.js中的qrcode.decode方法进行解析,解析之后qrcode.callback会返回解析的结果。

我们可以封装成一个函数,在实际应用中直接使用new Promise方法获取解析的结果,其他地方要使用扫描二维码的时候,直接通过new Promise调用此方法即可,方便快捷:

//二维码扫描模态框
function showPrintModal(resolve) {
    var modalId = '_printModal';
    var html =
        `<div class="modal fade" id="${modalId}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
            <div class="modal-dialog">
               <div class="modal-content">
                   <div class="modal-header">
                       <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                       <h4 class="modal-title"></h4>
                   </div>
                   <div class="modal-body" style="position:relative">
                        <div class="qrcode-box" style="position:absolute;left:50%;transform:translateX(-50%);width:300px;height:300px">
                            <span></span>
                            <span></span>
                            <span></span>
                            <span></span>
                        </div>
                        <video style="width: 100%;height:300px"></video>
                        <canvas class="hidden" width="300" height="300"></canvas>
                   </div>
                   <div class="modal-footer">
                       <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                   </div>
               </div>
           </div>
        </div>`;
    var modal = $(html);
    $("body").append(modal);
    var closeVideo, canvasImg, qrCode = null;
    var video = $('#_printModal video')[0];
    var canvas = $('#_printModal canvas')[0];
    var context = canvas.getContext('2d');
    modal.on('hidden.bs.modal',
        function () {
            if (closeVideo) {
                closeVideo.stop();
            }
            if (canvasImg) {
                clearInterval(canvasImg);
            }
            resolve(qrCode);
            modal.remove();
        });
    $(document).on("visibilitychange", function () {
        var page = this.visibilityState;
        if (page == "hidden") {
            modal.modal('hide');
        }
    });
    var getUserMedia = (constraints, success, error) => {
        if (navigator.mediaDevices.getUserMedia) {
            //最新的标准API
            navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
        } else if (navigator.webkitGetUserMedia) {
            //webkit核心浏览器
            navigator.webkitGetUserMedia(constraints).then(success).catch(error);
        } else if (navigator.mozGetUserMedia) {
            //firfox浏览器
            navigator.mozGetUserMedia(constraints).then(success).catch(error);
        } else if (navigator.getUserMedia) {
            //旧版API
            navigator.getUserMedia(constraints).then(success).catch(error);
        }
    }
    var capture = () => {
        context.drawImage(video, 0, 0, 300, 300);
        qrcode.decode(canvas.toDataURL('image/png'));
        qrcode.callback = (e) => {
            //结果回调
            if (e != "error decoding QR Code") {
                qrCode = e;
                clearInterval(canvasImg);
                context.clearRect(0, 0, canvas.width, canvas.height);
                modal.modal('hide');
            }
        }
    }
    if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
        //调用用户媒体设备, 访问摄像头
        getUserMedia({
            video: {
                width: 500,
                height: 500,
                //user前置摄像头
                facingMode: { exact: "environment" }
            }
        }, (stream) => {
            modal.modal("show");
            video.srcObject = stream;
            video.play();
            closeVideo = stream.getTracks()[0];
            canvasImg = setInterval(capture, 500);
        }, () => {
            modal.modal('hide');
            layer.msg("访问用户媒体设备失败");
        });
    } else {
        modal.modal('hide');
        layer.msg('不支持访问用户媒体');
    }
}

new Promise调用:

	new Promise(function (resolve) {
	    showPrintModal(resolve);
	}).then((result) => {
		alert(result);//扫描解析成功返回的数据
	});

3.上传图片解析二维码

html代码:

<input type="file" id="file" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg">

js代码:

//点击按钮上传图片之后
$("#file").on('change', function () {
    new Promise(function (resolve) {
        printImgUp(resolve, this);
    }.bind(this)).then((result) => {
        $(this).val("");
        //上传图片解析的结果
        result != null ? alert(result) : alert('识别失败');
    });
});

//createObjectURL兼容性处理,返回file对象的URL
function getObjectURL(file) {
    var url = null;
    if (window.createObjectURL != undefined) {          // basic
        url = window.createObjectURL(file);
    } else if (window.URL != undefined) {               // mozilla(firefox)
        url = window.URL.createObjectURL(file);
    } else if (window.webkitURL != undefined) {         // webkit or chrome
        url = window.webkitURL.createObjectURL(file);
    }
    return url;
}

//上传图片识别二维码
function printImgUp(resolve, fileId) {
	//得到图片的URL
    var file = getObjectURL(fileId.files[0]);
    //解析URL
    qrcode.decode(file);
    //结果回调
    qrcode.callback = function (e) {
        URL.revokeObjectURL(file);
        resolve(e != "error decoding QR Code" ? e : null);
    }
}

实现逻辑:点击file按钮上传图片后,通过new Promise调用printImgUp方法进行解析,在printImgUp方法中能获取到上传图片的URL,然后通过qrcode.decode解析这个URL,最后qrcode.callback会返回解析的结果,比扫描简单多了!!!
在这里插入图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值