對於大尺寸圖片的上傳,在前端進行壓縮除了省流量外,最大的意義是極大的提升了用戶體驗。php
這種體驗包括兩方面:html
特地製做了一個圖片前端壓縮並上傳的完整demo,您能夠狠狠的點擊這裏:使用canvas在前端壓縮圖片並上傳demo前端
進入demo會看到一個相貌平平的文件輸入框:web
啊,不對,應該是這張圖:ajax
點擊文件選擇框,咱們不妨選一張尺寸比較大的圖片,例以下面這種2M多的釣魚收穫照:算法
因而圖片歘歘歘地傳上去了:canvas
此時咱們點擊最終上傳完畢的圖片地址,會發現原來2M多3000多像素寬的圖片被限制爲400像素寬了:後端
保存到本地會發現圖片尺寸已經變成只有70K了:瀏覽器
以上就是圖片前端壓縮並上傳demo的完整演示。網絡
要想使用JS實現圖片的壓縮效果,原理其實很簡單,核心API就是使用canvas
的drawImage()
方法。
canvas
的drawImage()
方法API以下:
context.drawImage(img, dx, dy); context.drawImage(img, dx, dy, dWidth, dHeight); context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
後面最複雜的語法雖然看上去有9大參數,但不用慌,實際上能夠看出就3個參數:
canvas
畫布上規劃處一片區域用來放置圖片,
dx, dy
爲canvas元素的左上角座標,
dWidth, dHeight
指canvas元素上用在顯示圖片的區域大小。若是沒有指定
sx,sy,sWidth,sHeight
這4個參數,則圖片會被拉伸或縮放在這片區域內。
canvas
畫布上顯示的大小和位置。
sx,sy
表示圖片上
sx,sy
這個座標做爲左上角,而後往右下角的
swidth,sheight
尺寸範圍圖片做爲最終在canvas上顯示的圖片內容。
drawImage()
方法有一個很是怪異的地方,你們必定要注意,那就是5參數和9參數裏面參數位置是不同的,這個和通常的API有所不一樣。通常API可選參數是放在後面。可是,這裏的drawImage()
9個參數時候,可選參數sx,sy,swidth,sheight
是在前面的。若是不注意這一點,有些表現會讓你沒法理解。
下圖爲MDN上原理示意:
對於本文的圖片壓縮,須要用的是是5個參數語法。舉個例子,一張圖片(假設圖片對象是img
)的原始尺寸是4000*3000,如今須要把尺寸限制爲400*300大小,很簡單,原理以下代碼示意:
var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = 400; canvas.height = 300; // 核心JS就這個 context.drawImage(img,0,0,400,300);
把一張大的圖片,直接畫在一張小小的畫布上。此時大圖片就自然變成了小圖片,壓縮就這麼實現了,是否是簡單的有點超乎想象。
固然,若要落地於實際開發,咱們還須要作些其餘的工做,就是要解決圖片來源和圖片去向的問題。
HTML5 file API可讓圖片在上傳以前直接在瀏覽器中顯示,一般使用FileReader
方法,代碼示意以下:
var reader = new FileReader(), img = new Image(); // 讀文件成功的回調 reader.onload = function(e) { // e.target.result就是圖片的base64地址信息 img.src = e.target.result; }; eleFile.addEventListener('change', function (event) { reader.readAsDataURL(event.target.files[0]); });
因而,包含圖片信息的context.drawImage()
方法中的img
圖片就有了。
2. 若是把canvas畫布轉換成img圖像canvas
自然提供了2個轉圖片的方法,一個是:
canvas.toDataURL(mimeType, qualityArgument)
能夠把圖片轉換成base64格式信息,純字符的圖片表示法。
其中:mimeType
表示canvas
導出來的base64
圖片的類型,默認是png格式,也便是默認值是'image/png'
,咱們也能夠指定爲jpg格式'image/jpeg'
或者webp等格式。file
對象中的file.type
就是文件的mimeType類型,在轉換時候正好能夠直接拿來用(若是有file對象)。qualityArgument
表示導出的圖片質量,只要導出爲jpg
和webp
格式的時候此參數纔有效果,默認值是0.92
,是一個比較合理的圖片質量輸出參數,一般狀況下,咱們無需再設定。
canvas.toBlob(callback, mimeType, qualityArgument)
能夠把canvas轉換成Blob文件,一般用在文件上傳中,由於是二進制的,對後端更加友好。
和toDataURL()
方法相比,toBlob()
方法是異步的,所以多了個callback
參數,這個callback
回調方法默認的第一個參數就是轉換好的blob
文件信息,本文demo的文件上傳就是將canvas
圖片轉換成二進制的blob
文件,而後再ajax
上傳的,代碼以下:
// canvas轉爲blob並上傳 canvas.toBlob(function (blob) { // 圖片ajax上傳 var xhr = new XMLHttpRequest(); // 開始上傳 xhr.open("POST", 'upload.php', true); xhr.send(blob); });
因而,通過「圖片→canvas壓縮→圖片」三步曲,咱們完成了圖片前端壓縮並上傳的功能。
更加完整的核心代碼請參見demo頁面的左側,若是對其餘交互代碼也敢興趣,請參考頁面源代碼。
就在幾個月前剛寫過一篇文章「使用canvas在前端實現圖片水印合成」,實際上所使用的技術和套路和本文是一模一樣的,也是「圖片→canvas水印→圖片」三步曲,區別在於水印合成是連續執行兩次context.drawImage()
方法,一次是原圖一次水印圖片,以及最後轉換成圖片的時候什麼是toDataURL()
方法,其餘代碼邏輯和原理都是同樣的。
由此及彼,利用一樣的原理和代碼邏輯,咱們還能夠實現其它不少之前前端不太好實現的功能,比方說圖片的真剪裁效果,所謂「真剪裁」指不是使用個overflow:hidden
或者clip
這些CSS屬性的「僞剪裁」,而是真正意義上就這麼大區域圖片信息。甚至配合一些前端算法,咱們能夠直接在前端進行人臉識別,圖片自動美化等一系列功能再上傳等等。
原理都是同樣的,都是利用canvas
做爲中間媒介進行處理。
好,以上就是本文的所有內容,感謝閱讀,歡迎糾錯,歡迎交流!