使用Canvas封裝圖片壓縮功能!
作者:愛(ài)泡澡的小蘿ト 原文:https://juejin.cn/post/7220310687481495612
最近在學(xué)習(xí)和工作中遇到這樣一個(gè)場(chǎng)景:如何將前端上傳的圖片進(jìn)行壓縮傳遞給服務(wù)端?因?yàn)榇饲皬奈戳私膺^(guò)圖片壓縮的功能,所以也是帶著好奇進(jìn)行了一番學(xué)習(xí),那么我的解決思路如下展示
整體思路
-
創(chuàng)建input框?qū)崿F(xiàn)圖片上傳 -
將上傳的文件轉(zhuǎn)成base64格式 -
前端通過(guò)base64進(jìn)行原始圖片展示,并將此圖片進(jìn)行壓縮 -
將壓縮后的圖片傳給服務(wù)器
代碼實(shí)現(xiàn)
首先我們要實(shí)現(xiàn)input上傳文件并做一些細(xì)節(jié)處理,細(xì)節(jié)處理要對(duì)上傳的文件類型和上傳的文件大小做限制,類型和大小這里我們可以根據(jù)規(guī)則自定義。
<div class="container">
<input type="file" id="upload" />
</div>
const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'] // 所能接受的類型(自定義)
const MAXSIZE = 3 * 1024 * 1024 // 上傳圖片大小限制(自定義)
const MAXSIZE_STR = '3MB'
const upload = document.getElementById('upload')
upload.addEventListener('change', (e) => {
const [file] = e.target.files
if (!file) return // 上傳文件為空時(shí)終止
const { type: fileType, size: fileSize } = file
// 類型判斷
if (!ACCEPT.includes(fileType)) {
alert('不支持' + fileType + '文件類型')
upload.value = ''
return
}
// 圖片大小判斷
if (fileSize > MAXSIZE) {
alert(`文件超出${MAXSIZE_STR}限制`)
upload.value = ''
return
}
// 壓縮圖片
covertImageToBase64(file, (base64Image) =>
compress(base64Image, uploadServer)
)
})
當(dāng)所有限制都通過(guò)之后,就要將圖片轉(zhuǎn)為base64格式,也就是進(jìn)入到covertImageToBase64函數(shù),我們來(lái)看看這個(gè)函數(shù)做了什么
function covertImageToBase64(file, callback) {
let reader = new FileReader()
reader.readAsDataURL(file) // 讀取二進(jìn)制數(shù)據(jù),并將其編碼為 base64 的 data url
// 讀取完成進(jìn)行下面操作
reader.addEventListener('load', function (e) {
const base64Image = reader.result
callback && callback(base64Image) // 將讀取到的結(jié)果傳遞給回調(diào)函數(shù)中
reader = null
})
}
這里我們要將上傳獲取的_file_進(jìn)行編碼轉(zhuǎn)換,通過(guò)FileReaader[1]構(gòu)造函數(shù)中的 readAsDataURL[2]方法進(jìn)行轉(zhuǎn)換,將編碼后的格式文件傳遞給回調(diào)函數(shù)中準(zhǔn)備進(jìn)行壓縮處理
到這里這里為了方便大家的理解,我做了一個(gè)動(dòng)圖方便觀看效果,樣式比較簡(jiǎn)陋??

重點(diǎn)的壓縮圖片函數(shù)
上面的代碼都比較好理解,這里較為復(fù)雜的邏輯就都在covertImageToBase64函數(shù)的的回調(diào)compress中,這個(gè)功能也是壓縮圖片中最為核心的部分。
先擼為敬
function compress(base64Image, callback) {
let maxW = 800 // 圖片最大寬度
let maxH = 800 // 圖片最大高度
const image = new Image()
image.src = base64Image
document.body.appendChild(image)
image.addEventListener('load', function (e) {
let ratio // 圖片壓縮比
let needCompress = false // 是否需要壓縮
// 按照比例進(jìn)行圖片寬高的修改
if (image.naturalWidth > maxW ) {
needCompress = true
ratio = image.naturalWidth / maxW
maxH = image.naturalHeight / ratio
}
if (maxH < image.naturalHeight) {
needCompress = true
ratio = image.naturalHeight / maxH
maxW = image.naturalWidth / ratio
}
if (!needCompress) {
maxH = image.naturalHeight
maxW = image.naturalWidth
}
const canvas = document.createElement('canvas')
canvas.setAttribute('id', '__compress__')
canvas.width = maxW
canvas.height = maxH
canvas.style.visibility = 'hidden'
document.body.appendChild(canvas)
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, maxW, maxH)
ctx.drawImage(image, 0, 0, maxW, maxH)
const compressImage = canvas.toDataURL('image/png', 0.9)
callback && callback(compressImage)
// console.log(compressImage)
canvas.remove()
})
}
function uploadServer(compressImage) {
console.log(`upload to server ${compressImage}`)
}
代碼分析
當(dāng)我們接收到傳來(lái)的base64編碼的圖片時(shí),可以對(duì)其進(jìn)行壓縮,根據(jù)自定義的寬高與圖片實(shí)際寬高對(duì)比進(jìn)行比例計(jì)算,通過(guò)創(chuàng)建canvas元素畫出該圖片,并通過(guò)toDataURL[3] 方法將計(jì)算寬高后也就是壓縮后的base64返回,傳入回調(diào)中就可以進(jìn)行服務(wù)器上傳了,那么通過(guò)檢驗(yàn)也是能夠看到圖片確實(shí)進(jìn)行了壓縮

總結(jié)
要想更好的理解圖片壓縮,還是要把_FileReader_和_canvas_的一些api弄清楚,這樣才會(huì)更加容易理解代碼的邏輯,希望這篇文章能夠幫助到各位大佬
參考資料
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FFileReader
[2]https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsDataURL: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FFileReader%2FreadAsDataURL
[3]https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FHTMLCanvasElement%2FtoDataURL

