圖片裁剪上傳,不只是一個很貼合用戶體驗的功能,還可以統一特定圖片尺寸,優化網站排版,一舉兩得。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