HTML5下Base64图片处理结合摄像头抓拍裁切

最近要实现一个现场报名功能,采用B/S结构系统实现现场报名,现场采集用户信息录入系统。

前端使用bootstrap框架,后台使用ASP.NET MVC4。

刚开始只需要用户提供身份证,用读卡器读出身份证的信息自动填充到输入框中,然后补充其他信息保存即可。

身份证读卡器就是这货,USB接口。



做了个操作界面:


读身份证信息比较好办,调用读卡器接口对应的方法即可。

通过实验发现接口方法读取头像照片时,得到的是Base64格式的jpg图片,并且不包含“data:image/jpg;base64,”这段头信息,需要自己手工加上。图片尺寸为102*126像素,在图片上右击看属性:



提交报名信息时,后台将获取的Base64字符串转换成jpg并保存在指定路径下:


本来功能也就完成了,后来用户提出没带身份证报名的情况,需要增加从文件选择照片和现场用摄像头拍照两种方式,先感慨一下需求变更,还是要想办法实现功能。

分析后决定将文件选取和摄像头拍照用弹窗来操作,取得的图片直接展示在头像框处,并且为了和身份证读卡器读出的图片格式兼容,都设为Base64格式。这样就形成了统一思路,不论用什么方式获得头像照片(读身份证、文件选取、摄像头拍照),均以Base64格式展示在操作界面,这样提交到后台处理时方式是一样的。



================================= 分割一下 =================================

从文件中选取照片的处理

浏览照片选中时,用js控制在操作界面中展示选中照片,同时父窗口头像框处展示经过Base64处理过的图像。点击右上角关闭按钮或右下角确定按钮时,关闭当前弹窗。


元素"imgFile"为图片选择窗口中要展示的图片,"imgAvatar"为父窗口中的头像框,"hPhoto"为父窗口记录Base64内容的隐藏域,注意去掉了"data:image/jpeg;base64,"这段头信息,只保留图片数据内容。

此时头像框中图片格式与读身份证得到的图片格式一样,后台保存方法都不用改。即使选择的图片格式不是jpg的,仍然会在后台代码中自动转换成jpg格式。


=================================再分割一下=================================

摄像头拍照的处理稍微麻烦一下,主要在于拍照的图片是横版的,但系统要求的头像图片是竖版的,需要进行裁切。

摄像头就是普通那种USB接口的摄像头,免驱动的。


网页调用摄像头拍照有多种方案,对比后采用的是flash方式。

项目中引用需要的资源文件:


页面中调用:

    <script src="/assets/js/jquery-1.8.3.min.js" type="text/javascript"></script>
    <script src="/assets/js/webcam/jquery.webcam.js"></script>

思路是这样的:flash显示摄像头实时画面,点击“拍照”按钮时捕获当前画面,然后按比例裁切中间一部分图像展示在画布中。

摄像头实时画面设定为320*240像素,按照头像实际尺寸计算,应该截取拍照画面中间的194*240区域。

为了方便操作者预览,在摄像头实时画面的左右两侧增加了一个半透明区域,意思是这两块区域要删掉,只保留中间一部分。


弹窗中左侧是摄像头画面,下方有“拍照”按钮,拍照的图片自动裁切后显示在右侧,父窗口同步显示拍照结果。

左右两侧的半透明区域是用css控制的div透明度,注意flash调用摄像头的z-index数值,要将半透明区域叠加到flash之上才行。

摄像头画面只保留中间的194*240区域的内容,因此拍照后要将原始图片从(63,0)位置开始裁切大小为194*240区域的图像,js代码是image = ctx.getImageData(63, 0, 194, 240);


显示摄像头画面和抓拍画面的html:


其中,id为“webcam”的div在页面加载时会append上flash,由flash调用摄像头。


=================================待改进地方 =================================


1、程序中bootstrap框架采用ACE1.3.3版本,打开对话窗口时,是将弹窗文件解析后附加在本页中,因此会受本月的脚本和代码影响。从文件选取图片功能用默认的对话窗口可以实现功能,但摄像头拍照功能则不能生效,我只能用模态窗口来实现效果,并且在点击“拍照”按钮时将抓拍的图片回传给父窗口。

2、因客户只使用IE浏览器,并且版本也固定,因此可以不用考虑过多兼容问题。身份证读卡器使用Activex控件注册,如果是火狐或谷歌浏览器要加载不同的控件。另外实时摄像头画面两侧半透明区域只在IE下起作用,其他浏览器要再处理。


=================================总结=================================

1、通过三种不同方式获取头像信息,传到后台保存时已经处理成同样的格式,这也是代码分离原则的体现。以头像框为中转站,前台js各种处理,达到同样格式的Base64图片;后台获取图片字符串,统一保存成jpg格式。也相当于调用后台的一个方法时,通过各种手段将传入参数统一好格式,服务器端方法只需要接收到参数内容处理即可,不需要再做更多情况判断。

2、不同版本的bootstrap有一定的浏览器兼容问题,特别是jquery版本。某些时候使用原生js是更好的选择。

3、花哨的功能尽量少,用最简单的方式实现功能,稳定性是最好的。


附:代码文件

父页面部分代码

<input type="hidden" id="hPhoto" name="hPhoto" />
<input type="file" id="picFile" name="picFile" οnchange="selectPicFile()" style="display:none;" />

<img id="imgAvatar" alt="" src="/assets/avatars/default.jpg" width="102" height="126" />

<div class="col-sm-4 align-left">

<a href="#" role="button"  οnclick="CapturePicture()"><i class="fa fa-camera fa-1x" title="从摄像头拍照"> 拍照</i></a>

<br />

<a href="FilePicture" role="button" data-toggle="modal" data-target="#FilePicture"><i class="fa fa-folder-open fa-1x" title="从文件中选择"> 文件</i></a>

</div>


<div id="FilePicture" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>


<script type="text/javascript">

jQuery(function ($) {

$("#FilePicture").on("hidden.bs.modal", function () {
                $(this).removeData("bs.modal");
});

        function CapturePicture() {
            return window.showModalDialog("CapturePicture", GetResult, "dialogWidth=620px,dialogHeight=400px");
        }
        function GetResult(imageContent) {
            document.getElementById('imgAvatar').src = imageContent;
            document.getElementById('hPhoto').value = imageContent.substring(imageContent.indexOf(";base64,") + 8);
        }

});

</script>


文件选择子页面(FilePicture)

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta charset="utf-8" />
        <title></title>
        <script type="text/javascript">
            function showImage(file) {
                var imageContent = '';
                if (!file.files || !file.files[0]) {
                    return;
                }
                var reader = new FileReader();
                reader.onload = function (evt) {
                    imageContent = evt.target.result;
                    document.getElementById('imgFile').src = imageContent;
                    document.getElementById('imgAvatar').src = imageContent;
                    document.getElementById('hPhoto').value = imageContent.substring(imageContent.indexOf(";base64,") + 8);
                }
                reader.readAsDataURL(file.files[0]);
            }
        </script>


    </head>
    <body class="no-skin">
        <div class="modal-header">
       <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
       <h3 class="smaller lighter blue no-margin">选择照片</h3><h5>(尺寸:102*126像素或等比例)</h5>
        </div>
        <div class="modal-body">
            <table width="100%" border="0" cellpadding="0" cellspacing="0">
                <tr>
                    <td align="left">
                        <input type="file" id="picFile" name="picFile" οnchange="showImage(this)" />
                    </td>
                </tr>
                <tr>
                    <td>&nbsp;</td>
                </tr>
                <tr>
                    <td>
                        <img id="imgFile" style="height:220px;" alt="" />
                    </td>
                </tr>
            </table>
        </div>
        <div class="modal-footer">
            <button type="button" data-dismiss="modal" data-bb-handler="success" class="btn btn-sm btn-success">
                <i class="ace-icon fa fa-check"></i> 确定
            </button>
        </div>
    </body>
</html>


摄像头抓拍子页面(CapturePicture)

@{
    Layout = null;
}
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>拍照</title>
    <style type="text/css">
    #webcam {
        width: 320px;
        }
    #webcam {
        position:relative;
        }
    object {
        display:block; /* HTML5 fix */
        position:relative;
        z-index:100;
        }        
    .div1{
        width:320px;
        height:240px;
        position:relative;
        border:1px solid #cccccc;
   }
    .div2{
   width:63px;
   height:240px;
   position:absolute;
   left:0;
   top:0;
   background:#000000;
   z-index:9999;
   opacity:0.3;
   filter:alpha(opacity:30);
   moz-opacity:0.3;
   }
    .div3{
   width:63px;
   height:240px;
   position:absolute;
   right:0;
   top:0;
   background:#000000;
   z-index:9999;
   opacity:0.3;
   filter:alpha(opacity:30);
   moz-opacity:0.3;
   }
    </style>
    <script src="/assets/js/jquery-1.8.3.min.js" type="text/javascript"></script>
    <script src="/assets/js/webcam/jquery.webcam.js"></script>
    <script type="text/javascript">
        var pos = 0;
        var ctx = null;
        var cam = null;
        var saveCB;
        var image = [];


        $(document).ready(function () {
            var canvas = document.getElementById("canvas");
            if (canvas.getContext) {
                ctx = document.getElementById("canvas").getContext("2d");
                ctx.clearRect(0, 0, 320, 240);
                var img = new Image();
                img.src = "";
                img.onload = function () {
                    ctx.drawImage(img, 0, 0);
                }
                image = ctx.getImageData(0, 0, 320, 240);
            }


            if (canvas.toDataURL) {
                ctx = canvas.getContext("2d");
                image = ctx.getImageData(63, 0, 194, 240);
                saveCB = function (data) {
                    var col = data.split(";");
                    var img = image;
                    for (var i = 63; i < 257; i++) {
                        var tmp = parseInt(col[i]);
                        img.data[pos + 0] = (tmp >> 16) & 0xff;
                        img.data[pos + 1] = (tmp >> 8) & 0xff;
                        img.data[pos + 2] = tmp & 0xff;
                        img.data[pos + 3] = 0xff;
                        pos += 4;
                    }
                    if (pos >= 4 * 194 * 240) {
                        ctx.putImageData(img, 0, 0);
                        pos = 0;
                    }
                };


            } else {
                saveCB = function (data) {
                    image.push(data);
                    pos += 4 * 194;
                    if (pos >= 4 * 194 * 240) {
                        pos = 0;
                    }
                };
            }


            jQuery("#webcam").webcam({


                mode: "callback",
                swffile: "/assets/js/webcam/jscam_canvas_only.swf",
                onCapture: function () {
                    webcam.save();
                    var canvas = document.getElementById("canvas")
                    var imageContent = canvas.toDataURL("image/png")
                    var callBack = window.dialogArguments;
                    if (callBack != undefined && callBack != null) {
                        callBack(imageContent);
                    }
                },
                onSave: saveCB,
                debug: function (type, string) {
                    jQuery("#status").html(type + ": " + string);
                },
                onLoad: function () {
                    var cams = webcam.getCameraList();
                    for (var i in cams) {
                        jQuery("#cams").append("<li>" + cams[i] + "</li>");
                    }
                }
            });
        });


</script>
</head>


<body class="no-skin" style="margin:10px;">
    <table width="100%" border="0" cellpadding="0" cellspacing="0">
        <tr>
            <td height="56"><ul id="cams"></ul></td>
            <td width="50">&nbsp;</td>
            <td><p id="status"></p></td>
        </tr>
        <tr style="height:3px;">
            <td><hr /></td>
            <td></td>
            <td><hr /></td>
        </tr>
        <tr>
            <td colspan="3">&nbsp;</td>
        </tr>
        <tr>
            <td align="center">
                摄像头画面
            </td>
            <td></td>
            <td align="center">
                拍照结果
            </td>
        </tr>
        <tr>
            <td align="center">            
                <div id="webcam"  class="div1">
                    <div class="div2"></div>
                    <div class="div3"></div>
                </div>
            </td>
            <td></td>
            <td align="center">
                <div>
                    <canvas id="canvas" width="194" height="240"></canvas>
                </div>
            </td>
        </tr>
        <tr>
            <td align="center"><a href="javascript:webcam.capture();void(0);">拍照</a></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td colspan="3" height="20">&nbsp;</td>
        </tr>
        <tr>
            <td colspan="3" height="50" align="center" style="background-color:#cccccc"><input type="button" value="完成" οnclick="window.close();" /></td>
        </tr>
    </table>


</body>
</html>

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值