代码笔记

HTML 5 手机扫描二维码登陆网页

阅读

大概思路就是通过调用手机端的摄像头将视频放入video中,然后将视频中的某一帧放入canvas中,canvas将图片信息转换换成base64码,把base64码传到后台转换成数据流,用java解析就行了。 
    下面就上代码了(调试的时候才发现只有欧朋浏览器支持,而且版本是opera classic的才行): 
1、首先就是页面了 
<!DOCTYPE HTML > 
<html> 
<head> 
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 
<title>扫描二维码</title> 
<script type="text/javascript" > 
var video,canvas; 
window.addEventListener('DOMContentLoaded',function(){ 
'use strict'; 
//调取摄像头 
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; 
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 
if (navigator.getUserMedia) { 
navigator.getUserMedia({video: true}, gotStream, noStream); 
video = $("#video").get(0); 
canvas = $("#canvas").get(0); 
//启动摄像头成功之后开始获取二维码 
scanCode(); 
} else { 
console.log('Native web camera streaming (getUserMedia) not supported in this browser.'); 

//调取摄像头成功的回调函数 
function gotStream(stream) { 
  if (video.mozSrcObject !== undefined) { 
video.mozSrcObject = stream; 
} else { 
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream; 

video.play(); 
  } 
  
  //调取摄像头失败的回调函数 
  function noStream() { 
  console.error('An error occurred: [CODE ' + error.code + ']'); 
  } 
$("#myVideo").bind("play", function () { 
//$("#photo").attr("disabled",false); 
    }); 
},false); 
//抓取video画面放入canvas 
function photograph(){ 
var context = canvas.getContext("2d"); 
//获取抓取图片的区域 
//获取取景框其实坐标位置和宽高 
var cameraAperture_X = $("#td1").width(); 
//var cameraAperture_Y = $("#mid_div").height(); 
var cameraAperture_Y = $("#table_h").offset().top - $(".smtwo").height(); 
var cameraAperture_W = $("#cameraAperture").width(); 
var cameraAperture_H = $("#cameraAperture").height(); 
context.drawImage(video, Math.round(cameraAperture_X/2), Math.round(cameraAperture_Y/2),cameraAperture_W, cameraAperture_H,0,0,cameraAperture_W,cameraAperture_H); 
    
    imageConvertToGray(context); 
    
var imgData =canvas.toDataURL("image/png"); 
$("#code").val(imgData); 

//将图片处理成黑白的(二维码扫描需要处理黑白色图片,如果仅用于拍照这一步就省略了) 
function imageConvertToGray(ctx){ 
var length = canvas.width * canvas.height; 
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); 
for (var i = 0; i < length * 4; i += 4) { 
var myRed = imageData.data[i]; 
var myGreen = imageData.data[i + 1]; 
var myBlue = imageData.data[i + 2]; 
myGray = parseInt((myRed + myGreen + myBlue) / 3); 
imageData.data[i] = myGray; 
imageData.data[i + 1] = myGray; 
imageData.data[i + 2] = myGray; 

ctx.putImageData(imageData, 0, 0); 
  } 
function scanCode(){ 
//生成图片的base64码 
photograph(); 
$("#picForm").ajaxSubmit({ 
url:'${ctx}/xxxx/xxxx.htm', 
type:'post', 
dataType:'text', 
success:function(data){ 
if(data != ""){//扫描出结果 
window.location.href="${ctx}/xxxx/xxxxxxxxxx.htm?data="+data+"&status="+$("#status").val(); 
//alert("扫描信息为:"+data); 
}else{//继续扫描 
setTimeout(function(){ 
scanCode(); 
},2000); 


  }); 

</script> 
</head> 
<body> 
<form id="picForm" action="${ctx}/xxxx/xxxx.htm" method="post" > 
<input type="hidden" value="" id="code" name="code"/> 
<input type="hidden" value="${status }" id="status" name="status"/> 
<section class="smtwo"> 
<h1><a class="back" href="${ctx }/index/index.htm"><img src="${ctx }/images/smtwo_1.png" style="border:none" alt=""></a>${titleMsg }</h1> 
</section> 
<section style="position: relative;"> 
<video  width="100%" id="video" autoplay="autoplay" onclick="photograph();"></video> 
<canvas width="200" height="200" id="canvas" style="display: none;"></canvas> 
<div style="position: absolute;top: 0;left: 0;"> 
<div class="smtw_bg butsmtw" id="mid_div"> 
${content } 
</div> 
<table width="100%" border="0" cellpadding="0" cellspacing="0" id="table_h"> 
<tr> 
<td class="smtw_bg td1" id="td1"></td> 
<td class="td2"> 
<div class="smtw_td" style="width:200px;height:200px;" id="cameraAperture" onclick="photograph()"> 
<span class="br1"></span> 
<span class="br2"></span> 
<span class="br3"></span> 
<span class="br4"></span> 
</div> 
</td> 
<td class="smtw_bg td1"></td> 
</tr> 
</table> 
<div class="smtw_bg butsmtw" id="bottom_div"> 
</div> 
</div> 
</section>   
</form> 
</body> 
</html> 
页面说明: 
window.addEventListener 页面启动的时候调用摄像头,我试过不用这个直接$(document).ready();不好使。 
imageConvertToGray():这个函数主要是把照片变成黑白的,这个很重要,因为取得一帧图片是彩色的话就会影响读取二维码的效果(反正我没有处理成黑白之前没成功过) 
scanCode():这个就是扫描了,2秒抓一张图片传到后台进行处理如果成功会返回二维码包含内容。 
    这里需要说明的是canvas.toDataURL("image/png");canva转换成base64必须放到input隐藏变量里面,因为这个字符串很长。不能通过url提交,这样这个字符串长度会超了。 
    下面着中说取景框的问题,因为微信扫描二维码界面会有一个取景框,所以我们也要模拟这个,也就是说 在video中 的某一个区域截取图片放到canvas里面,所以在photograph()函数里面,我在context.drawImage的时候计算了需要取景的坐标,关于这个参数大家可以查看canvas属性就可以了解。 
    下来就是后台java解析了百度一下,到处都是,下面是我用的,置于jar包,大家自己找找哈。 
package sy.util; 
import java.awt.image.BufferedImage; 
import java.io.ByteArrayInputStream; 
import java.io.File; 
import java.io.InputStream; 
import javax.imageio.ImageIO; 
import jp.sourceforge.qrcode.QRCodeDecoder; 
import sy.bean.TwoDimensionCodeImage; 
import Decoder.BASE64Decoder; 
public class QRCodeDecoderHandlerUtil { 
/** 
* 解析二维码(QRCode) 
* @param input 输入流 
* @return 
*/ 
  public static String decoderQRCode(InputStream input) throws Exception{  
          BufferedImage bufImg = null;  
          String content = null; 
          
          bufImg = ImageIO.read(input);  
          QRCodeDecoder decoder = new QRCodeDecoder();  
          content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8"); 
          
          return content;  
      } 
  
  /** 
   * 解析二维码(QRCode) 
   * @param imgPath 图片路径 
   * @return 
   */ 
      public static String decoderQRCode(String imgPath) throws Exception{  
          // QRCode 二维码图片的文件  
          File imageFile = new File(imgPath);  
          BufferedImage bufImg = null;  
          String content = null; 
          
          bufImg = ImageIO.read(imageFile);  
          QRCodeDecoder decoder = new QRCodeDecoder();  
          content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");  
          
          return content;  
      }  
      /** 
       * 解析二维码 
       * @param imgStr 图片的Base64信息 
       * @return 
       */ 
      public static String decoderQRCodeForBase64(String imgStr) throws Exception{ 
      if (imgStr == null){ 
      return ""; 
      } 
      
      BASE64Decoder decoder = new BASE64Decoder(); 
      
          byte[] b = decoder.decodeBuffer(imgStr); 
          for(int i=0;i<b.length;++i){ 
              if(b[i]<0){//调整异常数据 
                  b[i]+=256; 
              } 
          } 
          
          InputStream input = new ByteArrayInputStream(b); 
          
          String content = decoderQRCode(input); 
          
          return content; 
              
      
      } 
      
      public static void main(String[] args) { 


说明:我是借用decoderQRCode(InputStream input)方法,因为考虑到,如果用decoderQRCode(String imgPath) 就需要在服务器生成临时图片,这样不仅反说,还会产生一些垃圾图片。 
decoderQRCodeForBase64这个方法就是将base64(前台取过来的)变成InputStream 然后交给decoderQRCode(InputStream input)处理 
好了OK ,置于 网页端的二维码里面如何存放信息,这个我想大家根据业务了,具体流程就是手机扫描到二维码里面的信息之后,手机版服务器通知网页版服务器然后网页版服务器登陆并且跳转到相应页面就OK了 



推荐阅读

使用 HTML5 的picture元素处
使用 HTML5 的picture元素...
html5 css3 placeholder属性的
html5 css3 placeholder属性的...
使用jQuery播放/暂停 HTM
使用jQuery播放/暂停 H...
实用的HTML head头标签,记
实用的HTML head头标签,...