原文还是在简书上: html5 上传本地图片处理各种问题
这是最近给公司写一个项目,项目要求大概是这样子:
1.上传手机本地图片,然后裁剪(后加的需求)
2.能够旋转图片,用于裁剪(后面加的需求)
3.填写各种文字,选择颜色,之后把文字和2个相关的图片,水印到裁剪的图片上,上传服务器生成一个图片地址,返回,分享出去。
功能就是大概上面这些,其他的也就是各种小功能,不提了,技术选型说下,整体上使用 Vue(包括 router,resource,webpack等等)
那么这几个需求怎么做呢:
1、 本地上传,使用 html5 的 File Api 拿到图片的base64编码,赋值给img的src(坑1,2),然后弹出一个图层,进行裁剪,最开始裁剪是在img的上面套一个div来进行坐标计算,计算完了使用canvas来截取图片,然后取值(坑3)。
2、这个功能就是使用canvas的旋转图片解决,需要注意的是,旋转的时候要保持横纵比,而且要注意宽高的大小(坑4)。
3、使用canvas来叠加水印和图片即可,主要是注意坐标。
那么说说坑:
1、拿到src的base64编码,看似没有问题,实际上有个巨大的问题,很多图片在手机上显示为竖屏,但是拿到的base64编码,直接赋值给img的src后,发现是横屏的。最开始发现这种情况,以为是个别现象,最后不断尝试之后,发现是个非常普遍的情况,特别是IPhone手机,而且还分你选择的图片文件夹,相册和照片流同一张图片,一个横屏,一个竖屏。导致我完全不能理解这是为什么???基本一个下午耗在这个问题上了。
直到晚上回去,问我一个朋友IOS开发的大神,@叶孤城__,他告诉我,因为现在IPhone的摄像头就是横着的,手机里显示竖屏的原因是ios自己做了处理,他们可以根据图片的一个拍摄角度数值来判断横竖问题,但是这个数值在我们web端确拿不到,很是尴尬。那么怎么解决这个问题呢?? ------- 我使用的方案:旋转图片,可以让用户自己去主动旋转图片,选取角度。 还有另外一种解决方案,在坑2也用到,后面讲。
2、除了这个横屏之外,android手机有的上传,选择了图片之后,没有任何反应,我开始一度认为原因是不支持html5的File Api,所以没有显示出上传的图片,后面就各种debugger,发现原因是没有触发Input标签的change事件,而且不管怎么样都没有办法触发,为了解决这个问题,查阅了各种官方文档和stackoverflow之后,发现可以给 type="file"的input添加两个属性来表示手机上传图片。
<input type="file" name="image" class="file-choose" id="file" accept="image/*" v-on:change="chooseFileChange($event)" capture/>
这样添加了 accept 和 capture之后,有问题的android手机,在选择图片的时候,有好几个文件夹,可以选择了,其中有的可以上传,有的不行,经常仔细的测试发现,sd卡上的图片是拿不到的,也就不会触发change事件,因为没有root权限去拿文件数据。又是一个无解问题,因为你的web在浏览器里面,权限就是低啊,(不得不吐槽下web的权限问题,妈蛋)怎么解决问题呢??? 绕过去,也就是说如果你的页面是嵌套在你们公司自己App里面的,就让App帮你,那么我们项目是微信传播的,一定在微信浏览器里面,所以可以调用微信的JSSDK的选择图片接口,他是可以越过这些权限,而且还有一个好处,就是解决坑1的问题,他会处理横屏问题,就是把看着竖屏,实际横屏的上传时都处理为竖屏,但是代价也不小,你要选择图片,拿到一个key,然后继续调用sdk传到微信的服务器,拿到一个serverid,这个id传给自己的服务端,让他们通过这个id,去微信下载图片到自己的服务器,返回给你一个Url。过程很曲折,而且下载次数有限制(可以跟微信申请加载限制);
参考: 微信 js sdk 选择图片接口
3、我们继续说坑,以上问题,解决了之后,就是裁剪了,开始我使用的方案是这样子的,获取到base64之后,赋值给一个img,然后在这个img上进行框选移动,计算坐标然后裁剪,pc端完全没有任何问题,效率很高,但是放到微信上面测试,发现3个问题(妈蛋,手机端就是坑,一个功能,3个不同的问题),第一个问题,大家都知道现在手机像素高,图片不小,上传过来之后,base64也不小,放到img的src中其实就是内存中了,导致整个微信特别容易崩溃(就是崩溃,他就崩溃了,微信就崩溃了---三遍),第二个问题,使用vue的on来绑定touch事件,响应很慢,移动一点都不平滑,而且也会崩溃,没错,又崩溃了。第三个问题,旋转要使用canvas转化,先去图片数据,转完后,在给图片src赋值,很麻烦。
解决方案: 统一使用canvas,不要再用img,知道裁剪完成了,把img的base64拿到就行,而且导出的时候,使用jpeg不要是png,降低一些画质,我觉得完全没有影响,也就是图片的裁剪,旋转都是canvas,事件建议直接原生绑定。
4、旋转的坑,这个的问题是我们必须保存住原始图片的数据,进行canvas先旋转然后drawImage,要不没有旋转出来,canvas自己的imageData,貌似没有办法旋转,我试了矩阵的方式好像都不行(也可能是自己数学不好!!!如果有人知道,就demo)。
以上就是这次项目,遇到的各种大坑,其他都是小的地方,不过总体来说,完成了任务,并且使用了新的技术Vue.js。Vue的component还是非常好用的,注意父子关系,props的继承就没问题了。
欢迎大家交流相关技术, 如果对Vue感兴趣,可以加QQ群: 364912432,240319632。