Html 5 网页扫二维码 支持手机端 OpenCV JS

       开发微网页端或者BS架构的应用系统,有可能遇到支持“扫二维码“的需求,如果是钉钉等平台的内嵌微应用借助于平台App提供的API而获得”扫码“功能支持,而非这类平台级别上的网页端应用又如何通过自已写代码来获得扫码功能,这里通过OpenCV JS类库可以获得。

       OpenCV JS 参考文档 

       代码对有OpenCV基础的人来说非常简单,只要切换一下OpenCV API的运行环境,但本人在安装使用过程中还是遇到一些坑,这里就不再赘述。本人在chrome浏览器和苹果Safari上运行

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Opencv Html 5 扫二维码</title>
</head>
<body>
<video  width="640" height="480" id="videoInput" style="display:none"></video>
<canvas width="640" height="480" id="outputCanvas"></canvas>
<script src="utils.js" type="text/javascript"></script>
<script type="text/javascript">
let utils = new Utils('errorMessage');
let streaming = false;
let videoInput = document.getElementById('videoInput');
let canvasOutput = document.getElementById('outputCanvas');
let canvasContext = canvasOutput.getContext('2d');

utils.loadOpenCv(() => {
	let video = document.getElementById('videoInput');
	let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
	let dst = new cv.Mat(video.height, video.width, cv.CV_8UC1);
	let qrCodeDetector = new cv.QRCodeDetector();
	let cap = new cv.VideoCapture(video);
	let streaming = true;
	const FPS = 30;
	const constraints = {
	        'qvga': {width: {exact: 320}, height: {exact: 240}},
	        'vga': {width: {exact: 640}, height: {exact: 480}}};
	let videoConstraint = constraints['qvga'];
	navigator.mediaDevices.getUserMedia({video: videoConstraint, audio: false})
	.then(function(stream) {
	    video.srcObject = stream;
	    video.play();
	})
	.catch(function(err) {
		console.log('Camera Error: ' + err.name + ' ' + err.message);
	});
	function processVideo() {
	    try {
	        if (!streaming) {
	            // clean and stop.
	            src.delete();
	            dst.delete();
	            return;
	        }
	        let begin = Date.now();
	        // start processing.
	        cap.read(src);
			let result = qrCodeDetector.detectAndDecode(src);
			if (result!="") {
				console.log("结果:" + result);
				cv.putText(src,result,new cv.Point(10,60),cv.FONT_HERSHEY_SIMPLEX,1,new cv.Scalar(255, 255, 255));
			}
	        cv.imshow('outputCanvas', src);
	        // schedule the next one.
	        let delay = 1000/FPS - (Date.now() - begin);
	        setTimeout(processVideo, delay);
	    } catch (err) {
	       	console.log(err);
	    }
	}
	 setTimeout(processVideo, 0);
});
</script>
</body>
</html>

例子运行的结果:

utils.js代码如下

function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
    let self = this;
    this.errorOutput = document.getElementById(errorOutputId);

    const OPENCV_URL = 'opencv.js';
    this.loadOpenCv = function(onloadCallback) {
        let script = document.createElement('script');
        script.setAttribute('async', '');
        script.setAttribute('type', 'text/javascript');
        script.addEventListener('load', async () => {
            if (cv.getBuildInformation)
            {
                console.log(cv.getBuildInformation());
                onloadCallback();
            }
            else
            {
                // WASM
                if (cv instanceof Promise) {
                    cv = await cv;
                    console.log(cv.getBuildInformation());
                    onloadCallback();
                } else {
                    cv['onRuntimeInitialized']=()=>{
                        console.log(cv.getBuildInformation());
                        onloadCallback();
                    }
                }
            }
        });
        script.addEventListener('error', () => {
            self.printError('Failed to load ' + OPENCV_URL);
        });
        script.src = OPENCV_URL;
        let node = document.getElementsByTagName('script')[0];
        node.parentNode.insertBefore(script, node);
    };

    this.createFileFromUrl = function(path, url, callback) {
        let request = new XMLHttpRequest();
        request.open('GET', url, true);
        request.responseType = 'arraybuffer';
        request.onload = function(ev) {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    let data = new Uint8Array(request.response);
                    cv.FS_createDataFile('/', path, data, true, false, false);
                    callback();
                } else {
                    self.printError('Failed to load ' + url + ' status: ' + request.status);
                }
            }
        };
        request.send();
    };

    this.loadImageToCanvas = function(url, cavansId) {
        let canvas = document.getElementById(cavansId);
        let ctx = canvas.getContext('2d');
        let img = new Image();
        img.crossOrigin = 'anonymous';
        img.onload = function() {
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0, img.width, img.height);
        };
        img.src = url;
    };

    this.executeCode = function(textAreaId) {
        try {
            this.clearError();
            let code = document.getElementById(textAreaId).value;
            eval(code);
        } catch (err) {
            this.printError(err);
        }
    };

    this.clearError = function() {
        this.errorOutput.innerHTML = '';
    };

    this.printError = function(err) {
        if (typeof err === 'undefined') {
            err = '';
        } else if (typeof err === 'number') {
            if (!isNaN(err)) {
                if (typeof cv !== 'undefined') {
                    err = 'Exception: ' + cv.exceptionFromPtr(err).msg;
                }
            }
        } else if (typeof err === 'string') {
            let ptr = Number(err.split(' ')[0]);
            if (!isNaN(ptr)) {
                if (typeof cv !== 'undefined') {
                    err = 'Exception: ' + cv.exceptionFromPtr(ptr).msg;
                }
            }
        } else if (err instanceof Error) {
            err = err.stack.replace(/\n/g, '<br>');
        }
        this.errorOutput.innerHTML = err;
    };

    this.loadCode = function(scriptId, textAreaId) {
        let scriptNode = document.getElementById(scriptId);
        let textArea = document.getElementById(textAreaId);
        if (scriptNode.type !== 'text/code-snippet') {
            throw Error('Unknown code snippet type');
        }
        textArea.value = scriptNode.text.replace(/^\n/, '');
    };

    this.addFileInputHandler = function(fileInputId, canvasId) {
        let inputElement = document.getElementById(fileInputId);
        inputElement.addEventListener('change', (e) => {
            let files = e.target.files;
            if (files.length > 0) {
                let imgUrl = URL.createObjectURL(files[0]);
                self.loadImageToCanvas(imgUrl, canvasId);
            }
        }, false);
    };

    function onVideoCanPlay() {
        if (self.onCameraStartedCallback) {
            self.onCameraStartedCallback(self.stream, self.video);
        }
    };

    this.startCamera = function(resolution, callback, videoId) {
        const constraints = {
            'qvga': {width: {exact: 320}, height: {exact: 240}},
            'vga': {width: {exact: 640}, height: {exact: 480}}};
        let video = document.getElementById(videoId);
        if (!video) {
            video = document.createElement('video');
        }

        let videoConstraint = constraints[resolution];
        if (!videoConstraint) {
            videoConstraint = true;
        }

        navigator.mediaDevices.getUserMedia({video: videoConstraint, audio: false})
            .then(function(stream) {
                video.srcObject = stream;
                video.play();
                self.video = video;
                self.stream = stream;
                self.onCameraStartedCallback = callback;
                video.addEventListener('canplay', onVideoCanPlay, false);
            })
            .catch(function(err) {
                self.printError('Camera Error: ' + err.name + ' ' + err.message);
            });
    };

    this.stopCamera = function() {
        if (this.video) {
            this.video.pause();
            this.video.srcObject = null;
            this.video.removeEventListener('canplay', onVideoCanPlay);
        }
        if (this.stream) {
            this.stream.getVideoTracks()[0].stop();
        }
    };
};

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

victorkevin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值