无需Flash实现图片裁剪——HTML5中级进阶

hubuke 8年前
   <h2>前言</h2>    <p>图片裁剪上传,不仅是一个很贴合用户体验的功能,还能够统一特定图片尺寸,优化网站排版,一箭双雕。</p>    <p>需求就是那么简单,在浏览器里裁剪图片并上传到服务器。</p>    <p>我第一个想到的方法就是,将图片和裁剪参数(x,y,scale,rotate)一并上传给服务器,服务器来做图片处理,so easy。<br> 但是,这并不符合潮流发展的方向:<strong><em>能在前端做的处理,就放前端做吧。</em></strong><br> 与潮流妥协的结果就是,前端越来越复杂。</p>    <p>一开始我并不认为浏览器能够读取并生成图片。想想看啊,要做”点击复制”的这样简单的功能,都需要借助 Flash 的浏览器,权限哪有那么大。</p>    <p>参阅各类网站,只要把图片放在本地处理的,基本上都借用了Flash。随便抄一个吧,没有API,就算能修改图片,上传路径都不知道怎么改。更关键的是,我对Flash一窍不通。</p>    <p>好在我们的网站已经完全抛弃了IE9以下的浏览器,只兼容现代HTML5浏览器。(连Opera和微软都开始走Webkit内核的路线了,潮流就是跟着Chrome走)只能寄希望与HTML5,于是钻研了一番,发现如下流程可行。</p>    <p> </p>    <p>以下将对每个环节详解。</p>    <h2>获取原图片 File 对象</h2>    <p>每个图片文件处理的开始,都是由onchange事件开始</p>    <pre>  <code class="language-html"><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></code></pre>    <p> </p>    <h2>初始化Cropper</h2>    <p>在这里介绍一个非常好用的库 cropper.js<br> <a href="/misc/goto?guid=4958857569635936128" rel="nofollow">https://github.com/fengyuanchen/cropper</a><br> 生成遮罩、获取裁剪参数、输出canvas … 而且绝对轻量级,压缩后的css和js代码只有30KB。他是基于JQuery的,引入JQuery可能还要再大点。不过现在哪个网站没有在用JQuery呢?<br> 兼容IE9+,移动端体验良好,能够响应触摸缩放,拖动。以下是安卓4.4 原生浏览器中的预览图</p>    <p><a href="/misc/goto?guid=4959676928439018926"><img alt="2" src="https://simg.open-open.com/show/3c9168963e2856a704781160b078affe.jpg"></a></p>    <pre>  <code class="language-javascript">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);  // 动态设置图片预览    }</code></pre>    <p> </p>    <h2>绘制Canvas</h2>    <p>cropper.js 提供了生成Canvas的方法getCroppedCanvas,可以指定生成画布的大小。<br> 或者根据getData获取裁剪信息(包括旋转和缩放)用ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)进行手动绘制。后者自由性高一点,但是既然有现成的方法,那么就直接用好了。</p>    <pre>  <code class="language-javascript">function cropAndUpload(){        // 此处注意,生成的Canvas长宽比应与之前规定的裁剪比例一致        // 否则生成的图片会有失真        var size = {            width:100,            height:100        }        var croppedCanvas = $('#preview').cropper("getCroppedCanvas",size);  // 生成 canvas 对象        var croppedCanvasUrl = croppedCanvas.toDataURL(originFileType); // Base64        ...    }</code></pre>    <p>应当注意的是width和height的值并不推荐设置成固定值。裁剪框的大小可能是会超过100*100(比如500*500)的,而实际生成的 图片却是100*100,这样的后果就是直接将一个500*500的高清图片,压缩成了100*100的失真图片。同样的,裁剪框小于100*100,生 成的图片就会模糊。</p>    <h2>Base64 转Blob对象</h2>    <p>字符串转为二进制?(前端本来是个做页面的,现在也开始操作文件了。自从有了HTML5,就可以把浏览器当作一个操作系统了)官方并没有出 DataURLtoBlob的方法,所以只能自己写一个,转化也挺简单:拆解文件类型,将字符数据转成16进制数据存数组,并用数据初始化一个Blob对 象。</p>    <pre>  <code class="language-javascript">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);    }</code></pre>    <p><br> 现在就可以像处理FileObject一样处理 这个blob对象了。</p>    <p> </p>    <p>其实在最新的HTML5标准中是支持<em><strong>HTMLCanvasElement.toBlob(callback, mimeType, quality)</strong></em> 的</p>    <pre>  <code class="language-javascript">croppedCanvas.toBlob(function(croppedBlob){        // Upload(croppedBlob);    },originFileType)</code></pre>    <p><br> 绕了一个弯,不过还是学到了东西。</p>    <p> </p>    <p>原文作者来自 MaxLeap 团队_UX成员:John王</p>    <p>来自:https://blog.maxleap.cn/archives/705</p>