图片裁剪上传,不只是一个很贴合用户体验的功能,还可以统一特定图片尺寸,优化网站排版,一举两得。css
需求就是那么简单,在浏览器里裁剪图片并上传到服务器。html
我第一个想到的方法就是,将图片和裁剪参数(x,y,scale,rotate)一并上传给服务器,服务器来作图片处理,so easy。
可是,这并不符合潮流发展的方向:能在前端作的处理,就放前端作吧。
与潮流妥协的结果就是,前端愈来愈复杂。前端
一开始我并不认为浏览器可以读取并生成图片。想一想看啊,要作"点击复制"的这样简单的功能,都须要借助 Flash 的浏览器,权限哪有那么大。git
参阅各种网站,只要把图片放在本地处理的,基本上都借用了Flash。随便抄一个吧,没有API,就算能修改图片,上传路径都不知道怎么改。更关键的是,我对Flash一窍不通。github
好在咱们的网站已经彻底抛弃了IE9如下的浏览器,只兼容现代HTML5浏览器。(连Opera和微软都开始走Webkit内核的路线了,潮流就是跟着Chrome走)只能寄但愿与HTML5,因而钻研了一番,发现以下流程可行。web
st=>start: 原图片 File 对象 e=>end: 上传裁剪后的Blob对象 op=>operation: 初始化Cropper 图片Base64预览 op1=>operation: 根据Cropper裁剪参数绘制Canvas(Base64) op2=>operation: Base64转Blob对象 st->op->op1->op2->e
如下将对每一个环节详解。canvas
每一个图片文件处理的开始,都是由onchange事件开始数组
<script> function handler(e){ var originPhoto = e.target.files[0]; // IE10+ 单文件上传取第一个 window.originFileType = originPhoto.type; //暂存图片类型 window.originFileName = originPhoto.name; //暂存图片名称 ... } </script> <input type="file" name="demo" onchange='handler(event)' accept="image/*" > <img id="preview"> <button onclick="cropAndUpload()">肯定并上传</button>
在这里介绍一个很是好用的库 cropper.js
https://github.com/fengyuanchen/cropper
生成遮罩、获取裁剪参数、输出canvas ... 并且绝对轻量级,压缩后的css和js代码只有30KB。他是基于JQuery的,引入JQuery可能还要再大点。不过如今哪一个网站没有在用JQuery呢?
兼容IE9+,移动端体验良好,可以响应触摸缩放,拖动。如下是安卓4.4 原生浏览器中的预览图浏览器
function handler(event){ ... var URL = window.URL || window.webkitURL , originPhotoURL; originPhotoURL = URL.createObjectURL(originPhoto); //Base64 $('#preview').cropper({ aspectRatio: 1 / 1, // 固定裁剪比例1:1,裁剪后的图片为正方形 }).cropper('replace', originPhotoURL); // 动态设置图片预览 }
cropper.js 提供了生成Canvas的方法getCroppedCanvas
,能够指定生成画布的大小。
或者根据getData
获取裁剪信息(包括旋转和缩放)用ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
进行手动绘制。后者自由性高一点,可是既然有现成的方法,那么就直接用好了。服务器
function cropAndUpload(){ // 此处注意,生成的Canvas长宽比应与以前规定的裁剪比例一致 // 不然生成的图片会有失真 var size = { width:100, height:100 } var croppedCanvas = $('#preview').cropper("getCroppedCanvas",size); // 生成 canvas 对象 var croppedCanvasUrl = croppedCanvas.toDataURL(originFileType); // Base64 ... }
应当注意的是width
和height
的值并不推荐设置成固定值。裁剪框的大小多是会超过100100(好比500500)的,而实际生成的图片倒是100100,这样的后果就是直接将一个500500的高清图片,压缩成了100100的失真图片。一样的,裁剪框小于100100,生成的图片就会模糊。
字符串转为二进制?(前端原本是个作页面的,如今也开始操做文件了。自从有了HTML5,就能够把浏览器看成一个操做系统了)官方并无出DataURLtoBlob
的方法,因此只能本身写一个,转化也挺简单:拆解文件类型,将字符数据转成16进制数据存数组,并用数据初始化一个Blob对象。
function dataURLtoBlob(dataurl) { var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type:mime}); } function cropAndUpload(){ ... var croppedBlob = dataURLtoBlob(croppedCanvasUrl); croppedBlob.name = originFileName; // Blob对象没有name // Upload(croppedBlob); }
如今就能够像处理FileObject同样处理 这个blob对象了。
其实在最新的HTML5标准中是支持HTMLCanvasElement.toBlob(callback, mimeType, quality)
的
croppedCanvas.toBlob(function(croppedBlob){ // Upload(croppedBlob); },originFileType)
绕了一个弯,不过仍是学到了东西。
原文做者来自 MaxLeap 团队_UX成员:John王
原文连接:https://blog.maxleap.cn/archives/705