前端圖片最優(yōu)化壓縮方案
作者:涼城a
原文:https://juejin.cn/post/6940430496128040967
前言
上傳圖片/視頻/文件是我們經(jīng)常會(huì)遇到的問題,但是一旦圖片過大就會(huì)導(dǎo)致不好的操作體驗(yàn)。圖片上傳是前端中常見的的業(yè)務(wù)場(chǎng)景。無(wú)論是前臺(tái)還是后臺(tái),適當(dāng)?shù)膶?duì)圖片進(jìn)行壓縮處理, 可以顯著的提升用戶體驗(yàn)。而在后臺(tái)管理系統(tǒng)中,圖片壓縮不僅僅能夠提升后臺(tái)管理員操作體驗(yàn),更是可以防止后臺(tái)設(shè)置過大的圖片導(dǎo)致前臺(tái)圖片加載過久,從而影響用戶體驗(yàn)。
關(guān)于壓縮圖片
思考
想想壓縮圖片基本流程
input 讀取到 文件 ,使用 FileReader 將其轉(zhuǎn)換為 base64 編碼
新建 img ,使其 src 指向剛剛的 base64
新建 canvas ,將 img 畫到 canvas 上
利用 canvas.toDataURL/toBlob 將 canvas 導(dǎo)出為 base64 或 Blob
將 base64 或 Blob 轉(zhuǎn)化為 File
將這些步驟逐個(gè)拆解,我們會(huì)發(fā)現(xiàn)似乎在canvas.toDataURL時(shí)涉及到圖片質(zhì)量,那咱們就從這里下手。
準(zhǔn)備
HTMLCanvasElement.toDataURL()
HTMLCanvasElement.toDataURL() 方法返回一個(gè)包含圖片展示的 data URI 。可以使用 type 參數(shù)其類型,默認(rèn)為 PNG 格式。圖片的分辨率為96dpi。
如果畫布的高度或?qū)挾仁?,那么會(huì)返回字符串“data:,”。
如果傳入的類型非“image/png”,但是返回的值以“data:image/png”開頭,那么該傳入的類型是不支持的。
語(yǔ)法
canvas.toDataURL(type, encoderOptions);
參數(shù)
type 可選
圖片格式,默認(rèn)為 image/png
encoderOptions 可選
在指定圖片格式為 image/jpeg 或 image/webp的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量。如果超出取值范圍,將會(huì)使用默認(rèn)值 0.92。其他參數(shù)會(huì)被忽略。Chrome支持“image/webp”類型。
猜想
可能toDataURL(type,quality)的第二個(gè)參數(shù)(quality)越小,文件體積越小
實(shí)踐
先看下原圖信息大小是7.31kb
toDataURL(type,quality)quality默認(rèn)0.92看看壓縮結(jié)果如何
<input id="fileInput" type="file" />
<img id="img" src="" alt="">
復(fù)制代碼let fileId = document.getElementById('fileInput')
let img = document.getElementById('img')
fileId.onchange = function (e) {
let file = e.target.files[0]
compressImg(file, 0.92).then(res => {//compressImg方法見附錄
console.log(res)
img.src = window.URL.createObjectURL(res.file);
})
}
復(fù)制代碼compressImg方法見附錄
可以看到圖片大小為
8.83kb壓縮后圖片反而變大了,這是怎么回事?
看來(lái)起初的猜想不完全正確,咱們繼續(xù)看看。
fileId.onchange = function (e) {
let file = e.target.files[0]
compressImg(file, 0.1).then(res => {//compressImg方法見附錄
console.log(res)
img.src = window.URL.createObjectURL(res.file);
})
}
復(fù)制代碼 當(dāng)
quality為0.1的時(shí)候,圖片僅有1.63kb,同樣的質(zhì)量也下降了
繼續(xù)......A long time later
咱們用折線圖來(lái)看吧更直觀
我又拿了幾個(gè)圖片讓他們使用默認(rèn)值0.92,結(jié)果都比原圖大
所以說(shuō)默認(rèn)值得到的圖片往往比原圖大
下面看看當(dāng)quality為多少時(shí)對(duì)圖片的壓縮效率可以最大化
壓縮效率最大化,即:在不影響圖片質(zhì)量的情況下最大化壓縮
嘗試了一系列的圖片之后我發(fā)現(xiàn) 當(dāng)quality在0.2~0.5之間,圖片質(zhì)量變化并不大,quality的值越小,壓縮效率越可觀(也就是在0.2左右時(shí),壓縮圖片可以最大化,同時(shí)并不對(duì)圖片質(zhì)量造成太大影響)
結(jié)論
經(jīng)過實(shí)踐,可以得出結(jié)論這個(gè)默認(rèn)值得到的圖片往往比原圖的圖片質(zhì)量要高。
當(dāng)quality在0.2~0.5之間,圖片質(zhì)量變化并不大,quality的值越小,壓縮效率越可觀(也就是在0.2左右時(shí),壓縮圖片可以最大化,同時(shí)并不對(duì)圖片質(zhì)量造成太大影響)
附錄
/**
* 壓縮方法
* @param {string} file 文件
* @param {Number} quality 0~1之間
*/
function compressImg(file, quality) {
if (file[0]) {
return Promise.all(Array.from(file).map(e => compressImg(e,
quality))) // 如果是 file 數(shù)組返回 Promise 數(shù)組
} else {
return new Promise((resolve) => {
const reader = new FileReader() // 創(chuàng)建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 創(chuàng)建 img 元素
image.onload = async () => {
const canvas = document.createElement('canvas') // 創(chuàng)建 canvas 元素
canvas.width = image.width
canvas.height = image.height
canvas.getContext('2d').drawImage(image, 0, 0, image.width, image.height) // 繪制 canvas
const canvasURL = canvas.toDataURL('image/jpeg', quality)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
})
}
}
復(fù)制代碼1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的「點(diǎn)贊,在看」是我創(chuàng)作的動(dòng)力。
2.關(guān)注公眾號(hào)
程序員成長(zhǎng)指北,回復(fù)「1」加入高級(jí)前端交流群!「在這里有好多 前端 開發(fā)者,會(huì)討論 前端 Node 知識(shí),互相學(xué)習(xí)」!3.也可添加微信【ikoala520】,一起成長(zhǎng)。
“在看轉(zhuǎn)發(fā)”是最大的支持
