HTML5 CANVAS 實現圖片壓縮和裁切


原文地址:http://leonshi.com/2015/10/31/html5-canvas-image-compress-crop/?utm_source=tuicool&utm_medium=referral

 

前面的話

早些時候用 Node-webkit(現在叫 nw.js) 編寫過一個輔助前端切圖的工具,其中圖片處理部分用到了 gm,gm 雖然功能強大,但用於 Node-webkit 卻有點發揮不了用處,gm 強依賴於用戶的本地環境安裝 imagemagick 和 graphicsmagick,而安裝 imagemagick 和 graphicsmagick 非常不方便,有時候還需要翻牆,所以這個工具大多數時候是我自己在玩。

為了降低安裝成本,這兩天開始研究去掉圖片處理功能中的 gm 依賴,替換為 HTML5 Canvas 來實現。

在這之前沒有深入研究過 canvas,通過這兩天的查資料過程,發現 canvas 的 API 非常豐富,實現本文的功能可以說只用到了 canvas 的冰山一角。

功能實現主要用到了 CanvasRenderingContext2D.drawImage 和 HTMLCanvasElement.toDataURL兩個方法,接下來先介紹一下這兩個方法,如果想直接看結果,可以跳到文章結尾查看完整的例子和代碼。


CanvasRenderingContext2D.drawImage()

drawImage 方法是 Canvas 2D 對象的方法,作用是將一張圖片繪制到 canvas 畫布中。

創建一個 Canvas 2D 對象:

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

drawImage 有 3 種調用方式:

ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

各個參數的意義:

  • image 圖片元素,除了圖片,還支持其他 3 種格式,分別是 HTMLVideoElement HTMLCanvasElement ImageBitmap,本文只涉及圖片,如果想了解其余格式可以參考 這里
  • sx 要繪制到 canvas 畫布的源圖片區域(矩形)在 X 軸上的偏移量(相對源圖片左上角)
  • sy 與 sx 同理,只是換成 Y 軸
  • sWidth 要繪制到 canvas 畫布中的源圖片區域的寬度,如果沒有指定這個值,寬度則是 sx 到圖片最右邊的距離
  • sHeight 要繪制到畫布中的源圖片區域的高度,如果沒有指定這個值,高度則是 sy 到圖片最下邊的距離
  • dx 源圖片左上角在 canvas 畫布 X 軸上的偏移量
  • dy 源圖片左上角在畫布 Y 軸上的偏移量
  • dWidth 繪制圖片的 canvas 畫布寬度
  • dHeight 繪制圖片的畫布高度

是不是有點暈了?下面這張圖可以直觀地說明它們的關系:

還是不好理解?那換個姿勢,可以這么理解:首先用 sx 和 sy 這兩個值去定位圖片上的坐標,再根據這個坐標點去圖片中挖出一個矩形,矩形的寬高就是 sWidth 和 sHeight 了。矩形挖出來了,現在要把它繪制到畫布中去,這時用 dx 和 dy 兩個值來確定矩形在畫布中的坐標位置,再用 dWidth 和 dHeight 確定划出多少畫布區域給這個矩形。

HTMLCanvasElement.toDataURL()

toDataURL 是 canvas 畫布元素的方法,返回指定圖片格式的 data URI,也就是 base64 編碼串。

toDataURL 方法最多接受兩個參數,並且這兩個參數都是可選的:

  • type 圖片格式。支持 3 種格式,分別是 image/jpeg image/png image/webp,默認是 image/png。其中 image/webp 只有 chrome 才支持。
  • quality 圖片質量。0 到 1 之間的數字,並且只在格式為 image/jpeg 或 image/webp 時才有效,如果參數值格式不合法,將會被忽略並使用默認值。

另外,如果對應的 canvas 畫布寬度或高度為 0,將會得到字符串 data:,,若圖片格式不是 image/png,卻得到一個以 data:image/png 開頭的值,則說明不支持此圖片格式。

圖片質量

對於圖片質量參數的默認值,官方文檔並沒有說明,這里 提到 Firefox 的默認值是 0.92,我在最新 chrome 瀏覽器中測試發現大概也是這個數字。不過要想達到各平台統一表現,最好的辦法是手動設置此參數。


實現圖片壓縮的關鍵代碼

HTML:

<canvas id="canvas"></canvas> <img id="preview" src=""> <img id="source" src="" style="display: none;"> 

JS:

var canvas = document.getElementById('canvas'); var source = document.getElementById('source'); var preview = document.getElementById('preview'); source.onload = function() { var width = source.width; var height = source.height; var context = canvas.getContext('2d'); // draw image params var sx = 0; var sy = 0; var sWidth = width; var sHeight = height; var dx = 0; var dy = 0; var dWidth = width; var dHeight = height; var quality = 0.92; canvas.width = width; canvas.height = height; context.drawImage(source, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); var dataUrl = canvas.toDataURL('image/jpeg', quality); preview.src = dataUrl; }; source.src = 'house.jpg'; 

編寫了一個簡單的 Demo,可輸入質量參數查看壓縮結果。

圖片壓縮結果

用作測試的是一張大小為 146 KB 的 JPG 圖片。

canvas-image-compress-crop

測試過程分別使用了 50%, 80%, 92%, 95%, 96%, 97%, 98%, 99%, 100% 這八個質量參數,結果如下:

canvas-image-compress-crop

查看原圖

換算成圖表:

canvas-image-compress-crop

問題

從圖表中可以看到,壓縮比為 95% 時與原圖大小最接近,此后,隨着壓縮參數增大直到 98%,增長比較規律,但從 98 到 99 尤其是 99 到 100,增長突然變陡,比原圖大小翻了將近 3 倍!

這里存在兩個問題:

  • 為什么 95% 是最接近原圖的壓縮比?這是否普遍規律?
  • 為什么 100% 比原圖增大了這么多?

在網上查了一些資料,但並沒有找到確切的原因,也沒有找到與之相匹配的類似問題。或許是我搜索的方式不對?如果你正好知道,歡迎留言告知。

實現圖片裁切的不完全代碼

function cropImage(targetCanvas, x, y, width, height) { var targetctx = targetCanvas.getContext('2d'); var targetctxImageData = targetctx.getImageData(x, y, width, height); // sx, sy, sWidth, sHeight var c = document.createElement('canvas'); var ctx = c.getContext('2d'); c.width = width; c.height = height; ctx.rect(0, 0, width, height); ctx.fillStyle = 'white'; ctx.fill(); ctx.putImageData(targetctxImageData, 0, 0); // imageData, dx, dy document.getElementById('source2').src = c.toDataURL('image/jpeg', 0.92); document.getElementById('source2').style.display = 'initial'; } 

以上代碼中,getImageData 和 putImageData 都是 Canvas 2D 對象的方法,前者用於獲取畫布上根據參數指定矩形的像素數據,返回的是一個多維數組。后者則用於將這些像素數據繪制到畫布中,同樣可以指定畫布中的繪制位置。

裁切的原理是通過 canvas A 的 getImageData 方法取出圖片中指定區域的像素數據,再用 canvas B 的 putImageData 方法將像素數據繪制到 canvas B 中,並保持 canvas B 的尺寸與取出區域的尺寸一致。canvas B 中的圖片就是裁切得到的圖片區域塊。

比如要裁切女帝的左耳環:

canvas-image-compress-crop

簡單量一下距離,就可以用下面的代碼實現:

cropImage(canvas, 250, 250, 90, 80) 

好了,差不多就是這些。

 

 


免責聲明!

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



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