圖片純前端JS壓縮的實現


一、圖片上傳前端壓縮的現實意義

對於大尺寸圖片的上傳,在前端進行壓縮除了省流量外,最大的意義是極大的提高了用戶體驗。

這種體驗包括兩方面:

  1. 由於上傳圖片尺寸比較小,因此上傳速度會比較快,交互會更加流暢,同時大大降低了網絡異常導致上傳失敗風險。
  2. 最最重要的體驗改進點:省略了圖片的再加工成本。很多網站的圖片上傳功能都會對圖片的大小進行限制,尤其是頭像上傳,限制5M或者2M以內是非常常見的。然后現在的數碼設備拍攝功能都非常出眾,一張原始圖片超過2M幾乎是標配,此時如果用戶想把手機或相機中的某個得意圖片上傳作為自己的頭像,就會遇到因為圖片大小限制而不能上傳的窘境,不得不對圖片進行再處理,而這種體驗其實非常不好的。如果可以在前端進行壓縮,則理論上對圖片尺寸的限制是沒有必要的。

二、圖片前端JS壓縮並上傳功能體驗

特意制作了一個圖片前端壓縮並上傳的完整demo,您可以狠狠的點擊這里:使用canvas在前端壓縮圖片並上傳demo

進入demo會看到一個相貌平平的文件輸入框:

相貌平平

啊,不對,應該是這張圖:

相貌平平文件選擇框

點擊文件選擇框,我們不妨選一張尺寸比較大的圖片,例如下面這種2M多的釣魚收獲照:

上傳演示使用的圖片

於是圖片歘歘歘地傳上去了:
上傳相關信息截圖

此時我們點擊最終上傳完畢的圖片地址,會發現原來2M多3000多像素寬的圖片被限制為400像素寬了:
圖片縮小后在瀏覽器中的預覽效果圖

保存到本地會發現圖片尺寸已經變成只有70K了:
保存到本地顯示的圖片尺寸

以上就是圖片前端壓縮並上傳demo的完整演示。

三、HTML5 file API加canvas實現圖片前端JS壓縮

要想使用JS實現圖片的壓縮效果,原理其實很簡單,核心API就是使用canvasdrawImage()方法。

canvasdrawImage()方法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個參數:

img
就是圖片對象,可以是頁面上獲取的DOM對象,也可以是虛擬DOM中的圖片對象。
dx, dy, dWidth, dHeight
表示在 canvas畫布上規划處一片區域用來放置圖片, dx, dy為canvas元素的左上角坐標, dWidth, dHeight指canvas元素上用在顯示圖片的區域大小。如果沒有指定 sx,sy,sWidth,sHeight這4個參數,則圖片會被拉伸或縮放在這片區域內。
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上原理示意:
Canvas drawimage()原理示意

對於本文的圖片壓縮,需要用的是是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);

把一張大的圖片,直接畫在一張小小的畫布上。此時大圖片就天然變成了小圖片,壓縮就這么實現了,是不是簡單的有點超乎想象。

當然,若要落地於實際開發,我們還需要做些其他的工作,就是要解決圖片來源和圖片去向的問題。

1. 如何把系統中圖片呈現在瀏覽器中?

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()方法
語法如下:

 

canvas.toDataURL(mimeType, qualityArgument)

可以把圖片轉換成base64格式信息,純字符的圖片表示法。

其中:
mimeType表示canvas導出來的base64圖片的類型,默認是png格式,也即是默認值是'image/png',我們也可以指定為jpg格式'image/jpeg'或者webp等格式。file對象中的file.type就是文件的mimeType類型,在轉換時候正好可以直接拿來用(如果有file對象)。
qualityArgument表示導出的圖片質量,只要導出為jpgwebp格式的時候此參數才有效果,默認值是0.92,是一個比較合理的圖片質量輸出參數,通常情況下,我們無需再設定。

canvas.toBlob()方法
語法如下:

 

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頁面的左側,如果對其他交互代碼也敢興趣,請參考頁面源代碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM