首页 > HTML > HTML5 拍照应用开发经历的那些坑儿
2014
12-04

HTML5 拍照应用开发经历的那些坑儿

一、项目简介

1.1、项目背景:
这是一个在移动终端创新应用的项目,用户在浏览器端(微信/手Q)即可完成与金秀贤的合影,希望通过这样一种趣味体验,引发用户的分享与转发的热潮。 HTML5 拍照应用开发经历的那些坑儿 - 小东 - 1

1.2、系统要求:
ios6-ios7、android3.0-android4.3、android4.4+(非webview内)

1.3、体验地址:
HTML5 拍照应用开发经历的那些坑儿 - 小东 - 2

二、初步技术方案确定

在项目前期首先启动了技术预演,确定初步技术方案(*非最终解决方案):

2.1、获取用户照片数据
2.1.1、首先放弃了主动获取用户摄像头的getUserMedia,因为移动终端支持率太低;
2.1.2、确定使用Input控件获取照片文件、使用FileReader读取照片数据,android3.0+、ios6.0+都可以支持。

2.2、编辑合成照片
2.2.1、放弃使用canvas编辑(即将图像数据读取到canvas内进行处理)照片,考虑到开发成本成高;
2.2.2、选用dom编辑(img标签),然后使用html2canvas,方便保存数据。

2.3、保存并上传照片
确定使用canvas的toDataURL接口,提交base64数据到服务器。

三、碰到的那些坑儿

按照既定的技术方案开始执行,开始碰到一个个问题,有些问题可以绕过,有些问题只能推倒重来。

3.1、照片方向反了(如下图所示)
HTML5 拍照应用开发经历的那些坑儿 - 小东 - 3
问题描述:
手持设备不同方向所拍摄的照片方向不同,照片的方向都会不同,但相册中会自动纠正,这一问题在ios和android中都存在。
问题解决:
3.1.1、将图片数据转换成二进制数据,方便获取图片的exif信息;(这里我引入了 Binary Ajax
3.1.2、获取图片的exif信息;(这里我使用了 Javascript EXIF Reader
3.1.3、通过图片exif信息,获取图片拍摄时所持设备方向orientation。
关键代码:

// 读取图片数据 var fr = new FileReader(); fr.readAsDataURL(file); fr.onload = function(fe){ var result = this.result; var img = new Image(); var exif; img.onload = function() { var orientation = exif ? exif.Orientation : 1; // 判断拍照设备持有方向调整照片角度 switch(orientation) { case 3: imgRotation = 180; break; case 6: imgRotation = 90; break; case 8: imgRotation = 270; break; } }; // 转换二进制数据 var base64 = result.replace(/^.*?,/,''); var binary = atob(base64); var binaryData = new BinaryFile(binary); // 获取exif信息 exif = EXIF.readFromBinaryFile(binaryData); img.src = result; };

3.2、html2canvas问题重重
问题背景:
为什么要用html2canvas呢,因为我们需要将用户合成照片后的base64数据提交服务器,所以我们需要通过转换为canvas获取照片数据。
问题详情:
3.2.1、图片使用css3 transform旋转了图片方向,但最终html2canvas渲染结果却未保存旋转信息;
3.2.2、html2canvas的渲染起点为网页右上角,而且不能更改设置;
3.2.3、ios大图被压扁了。
问题解决:
但最终因为碰到太多无法绕过的问题,不得不放弃html2canvas的方案,全部转为canvas开发。

3.3、ios大图被压扁了
问题详情: 当照片超过2M时,ios会出现压扁的情况(如下图所示)
HTML5 拍照应用开发经历的那些坑儿 - 小东 - 4
问题解决:
获取图片实际比例,重置图片的比例。(stack overflow讨论帖) 需要注意的是,ratio的获取是通过检测像素alpha,需要过滤png图片,这在stack overflow的讨论上没有人提出。
关键代码:
var getRatio = function(img) { if(/png$/i.test(img.src)) { return 1; } var iw = img.naturalWidth, ih = img.naturalHeight; var canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = ih; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); var data = ctx.getImageData(0, 0, 1, ih).data; var sy = 0; var ey = ih; var py = ih; while (py > sy) { var alpha = data[(py - 1) * 4 + 3]; if (alpha === 0) { ey = py; } else { sy = py; } py = (ey + sy) >> 1; } var ratio = (py / ih); return (ratio===0)?1:ratio; }

3.4、照片太模糊啦,我想提高精度!
问题描述:
HTML5 拍照应用开发经历的那些坑儿 - 小东 - 5
如上图所示,为了减少本地内存消耗,项目最初采用尺寸是320×270。在项目上线后,在确保内存占用不过高的情况下,开始尝试开发高清方案,测试地址如下:
HTML5 拍照应用开发经历的那些坑儿 - 小东 - 6
在主流设备上测试,性能并无太大问题,但当网络切换为3g时,测试图片合并上传时间8-12s,是原来时间的3倍左右,于是测试了一下3g网络的上传速度:

 

下载速度
上传速度
联通3g
220kb/s
80kb/s
电信3g
180kb/s
60kb/s
移动3g
100kb/s
13kb/s
移动2g
15kb/s
12kb/s

平常会留意用户的下载速度,但对上传速度没太在意,640×540图片的base64数据大小为120kb左右,加上延时,3g环境下平均上传时间是5s左右。于是,上传速度成为了高清方案的瓶颈。

解决方案:
3.4.1、在微信和手Q环境中检测用户环境如果为wifi,则启用高清方案,但由于在这个网站推广的渠道很多,环境复杂,并不能完全解决问题,所以放弃了该解决方式;
3.4.2、在上传前对base64数据进行文本压缩,目前正在尝试lz77压缩,未上线。

3.5、canvas toDataURL bug
问题描述:
已测试,至少在手机QQ浏览器中,canvas对象使用toDataURL方法获取不到任何数据。
问题解决:
使用JPEGEncoder将图片像素数据转换为base64数据。
关键代码:

_public.toDataURL = function(callback){ var self = this; // 去除编辑状态的元素 self.unSelect(); // 已测手机QQ浏览器canvas.toDataURL有问题,使用jeegEncoder window.setTimeout(function(){ var encoder = new JPEGEncoder(); var data = encoder.encode(self.canvas.getContext('2d').getImageData(0,0,self.stage.width,self.stage.height), 90); callback.call(self, data); }, 1000/self.config.fps) } 

3.6、当getElementOffset遇上transform
问题代码:
Quark.getElementOffset = function(elem) { var left = elem.offsetLeft, top = elem.offsetTop; while((elem = elem.offsetParent) && elem != document.body && elem != document) { left += elem.offsetLeft; top += elem.offsetTop; } return {left:left, top:top}; };
问题描述:
当目标元素的上级元素中有使用transform:translate(x,y)时,用如上的方法都会导致offset计算错误,这一bug在常用canvas框架EaseJSQuarkJS,DOM类库Zepto中都存在。我项目中使用的是QuarkJS,碰到具体问题是舞台事件坐标不正确,由于是框架中的bug,足足花了半天时间才追查到。
问题解决:
offsetLeft或offsetTop需要减去translate的差值。

四、项目总结

4.1、最终技术方案
4.1.1、获取用户照片数据 使用Input控件获取照片文件、使用FileReader读取照片数据,android3.0+、ios6.0+都可以支持。
4.1.2、编辑合成照片
4.1.2.1、使用canvas编辑图片,使用canvas框架为QuarkJS;
4.1.2.2、使用binaryajax和exif获取照片信息,用于解决ios bug和照片方向调整;
4.1.3、保存并上传照片
4.1.3.1、使用JPEGEncoder转换为base64数据;
4.1.3.2、使用lz77进行数据压缩
4.2、心得

这个项目进行得并不顺利,经历过1次推翻整体方案重写、1次框架bug纠错、多次系统和浏览器的bug修复,由于线上并没有此类相对成熟的应用,找不到可参考案例,吐槽之余,也总结出一些心得:

4.2.1、对于创新类的应用,前期技术预演很关键,不能只是探索可行性;
4.2.2、选择一个成熟的框架很关键,QuarkJS虽然本身架构不错并且很轻量,但使用它的过程中还是碰到过不少bug或不完善之处,并且文档不详细;
4.2.3、需要善于利用现有技术。这个项目中使用了不少第三方框架来解决特定问题,如果没有这些,项目周期将会相当长。
4.2.4、H5从图像到音频到视频,还有太多领域值得探索,有很大可挖掘的价值,想想都有点小兴奋呢!

4.3、图片编辑类整体代码

转载自QQ TGideas

最后编辑:
作者:管理员
这个作者貌似有点懒,什么都没有留下。