JavaScript前端實(shí)現(xiàn)壓縮圖片功能
為什么要前端來(lái)壓縮圖片
最近在做一個(gè)移動(dòng)端h5上傳圖片的功能,本來(lái)這個(gè)功能并不復(fù)雜,只需要將圖片文件通過(guò)axios傳到服務(wù)端即可,但是考慮到現(xiàn)在手機(jī)設(shè)配的拍照功能十分強(qiáng)大,隨便一張照片都能動(dòng)輒五六兆,而服務(wù)端的要求是上傳圖片必須小于兩兆,而且直接傳這么大圖片,帶寬它也受不了,所以前端進(jìn)行壓縮圖片就成了一個(gè)必要的環(huán)節(jié)。
壓縮效果
首先介紹下壓縮的大概流程
- 通過(guò)原生的input標(biāo)簽?zāi)玫揭蟼鞯膱D片文件
- 將圖片文件轉(zhuǎn)化成img元素標(biāo)簽
- 在canvas上壓縮繪制該HTMLImageElement
- 將canvas繪制的圖像轉(zhuǎn)成blob文件
- 最后將該blob文件傳到服務(wù)端
- 完成!
接下來(lái)看下詳細(xì)步驟
考慮到文章和步驟的完整性,所以我會(huì)把每個(gè)細(xì)節(jié)都寫(xiě)出來(lái),即使有些東西很基礎(chǔ)。
1. 使用Input標(biāo)簽來(lái)獲取圖片文件資源
這一步大家應(yīng)該最熟悉不過(guò)了吧,原生input標(biāo)簽,通過(guò)設(shè)置 type
屬性為file來(lái)讓用戶可以選擇文件,設(shè)置 accept
限制選擇的文件類(lèi)型,綁定onchange事件,來(lái)獲取確認(rèn)選擇后的文件
<input type="file" accept="image/*" />
點(diǎn)擊控件,觸發(fā)焦點(diǎn),打開(kāi)文件資源管理器,選中文件并確認(rèn)后,會(huì)觸發(fā)change事件,所以可以在change事件的回調(diào)中獲取選中文件,它長(zhǎng)這個(gè)樣
2. 讀取文件轉(zhuǎn)成img標(biāo)簽元素
拿到圖片文件后,先將其轉(zhuǎn)成HTMLImageElement,也就是普通的img標(biāo)簽,具體要使用 FileReader構(gòu)造函數(shù)。
先new出來(lái)一個(gè)img和fileReader的實(shí)例,通過(guò)fileReader的 readAsDataURL這個(gè)api,來(lái)讀取圖片文件,其返回值是一個(gè)編碼后的base64的字符串,然后將這個(gè)字符串賦值給img的src屬性上,這樣就完成了圖片文件到 HTMLImageElement的轉(zhuǎn)化。
// 先new一個(gè)img和fileReader的實(shí)例 const img = new Image() const reader = new FileReader()// 讀取文件資源 reader.readAsDataURL(file) reader.onload = function(e){ img.src = e.target.result }
轉(zhuǎn)化的HTMLImageElement
3. canvas壓縮,核心步驟
拿到轉(zhuǎn)化后的img元素后,先取出該元素的寬高度,這個(gè)寬高度就是實(shí)際圖片文件的寬高度。
const { width: originWidth, height: originHeight } = img
然后定義一個(gè)最大限度的寬高度,如果超過(guò)這個(gè)限制寬高度,則進(jìn)行等比例的縮放
// 最大尺寸限制 const maxWidth = 1000,maxHeihgt = 1000 // 需要壓縮的目標(biāo)尺寸 let targetWidth = originWidth, targetHeight = originHeight // 等比例計(jì)算超過(guò)最大限制時(shí)縮放后的圖片尺寸 if (originWidth > maxWidth || originHeight > maxHeight) { if (originWidth / originHeight > 1) { // 寬圖片 targetWidth = maxWidth targetHeight = Math.round(maxWidth * (originHeight / originWidth)) } else { // 高圖片 targetHeight = maxHeight targetWidth = Math.round(maxHeight * (originWidth / originHeight)) } }
計(jì)算好將要壓縮的尺寸后,創(chuàng)建canvas實(shí)例,設(shè)置canvas的寬高度為壓縮計(jì)算后的尺寸,并將img繪制到上面
// 創(chuàng)建畫(huà)布 const canvas = document.createElement('canvas') const context = canvas.getContext('2d') // 設(shè)置寬高度為等同于要壓縮圖片的尺寸 canvas.width = targetWidth canvas.height = targetHeight context.clearRect(0, 0, targetWidth, targetHeight) //將img繪制到畫(huà)布上 context.drawImage(img, 0, 0, targetWidth, targetHeight)
4. 轉(zhuǎn)成blob文件
canvas繪制完成后,就可以使用 toBlob來(lái)將圖像轉(zhuǎn)成blob文件了,這個(gè)api接受三個(gè)入?yún)?/p>
canvas.toBlob(callback, type, encoderOptions);
回調(diào)函數(shù)中可以得到轉(zhuǎn)化后的blob文件,type為要轉(zhuǎn)成的圖片類(lèi)型,默認(rèn)png。
encoderOptions為當(dāng)設(shè)置的圖片格式為 image/jpeg
或者 image/webp
時(shí)用來(lái)指定圖片展示質(zhì)量。
所以如果我們只是要壓縮jpg或者webp格式的圖片的話,不需要進(jìn)行第3部的操作,直接使用這個(gè)api,然后填入想要的質(zhì)量參數(shù)就可以了。但實(shí)際上,我們還是要考慮多種的圖片格式,因此很有必要使用第三部的過(guò)程。
轉(zhuǎn)成的blob長(zhǎng)這個(gè)樣子
5. 將blob上傳,大功告成。
完整的代碼實(shí)現(xiàn)
因?yàn)檎麄€(gè)過(guò)程中都存在著異步回調(diào)操作,所以我使用了async,實(shí)現(xiàn)異步代碼的同步執(zhí)行
// 壓縮前將file轉(zhuǎn)換成img對(duì)象 function readImg(file) { return new Promise((resolve, reject) => { const img = new Image() const reader = new FileReader() reader.onload = function(e) { img.src = e.target.result } reader.onerror = function(e) { reject(e) } reader.readAsDataURL(file) img.onload = function() { resolve(img) } img.onerror = function(e) { reject(e) } }) }
/** * 壓縮圖片 *@param img 被壓縮的img對(duì)象 * @param type 壓縮后轉(zhuǎn)換的文件類(lèi)型 * @param mx 觸發(fā)壓縮的圖片最大寬度限制 * @param mh 觸發(fā)壓縮的圖片最大高度限制 */ function compressImg(img, type, mx, mh) { return new Promise((resolve, reject) => { const canvas = document.createElement('canvas') const context = canvas.getContext('2d') const { width: originWidth, height: originHeight } = img // 最大尺寸限制 const maxWidth = mx const maxHeight = mh // 目標(biāo)尺寸 let targetWidth = originWidth let targetHeight = originHeight if (originWidth > maxWidth || originHeight > maxHeight) { if (originWidth / originHeight > 1) { // 寬圖片 targetWidth = maxWidth targetHeight = Math.round(maxWidth * (originHeight / originWidth)) } else { // 高圖片 targetHeight = maxHeight targetWidth = Math.round(maxHeight * (originWidth / originHeight)) } } canvas.width = targetWidth canvas.height = targetHeight context.clearRect(0, 0, targetWidth, targetHeight) // 圖片繪制 context.drawImage(img, 0, 0, targetWidth, targetHeight) canvas.toBlob(function(blob) { resolve(blob) }, type || 'image/png') }) }
大致執(zhí)行過(guò)程,具體可根據(jù)需求,自行改動(dòng)
async function upload(file){ const img = await readImg(file) const blob = await compressImg(img, file.type, 1000, 1000) const formData = new FormData() formData.append('file', blob, 'xxx.jpg') axios.post('http://xxx.com/api',formData) } upload(file).catch(e => console.log(e))
到此這篇關(guān)于JavaScript前端實(shí)現(xiàn)壓縮圖片功能的文章就介紹到這了,更多相關(guān)JavaScript 壓縮圖片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文搞懂TypeScript的安裝、使用、自動(dòng)編譯的教程
TypeScript 是一種由微軟開(kāi)發(fā)的開(kāi)源、跨平臺(tái)的編程語(yǔ)言。它是 JavaScript 的超集,最終會(huì)被編譯為 JavaScript 代碼,關(guān)于TypeScript的安裝、使用、自動(dòng)編譯很多朋友不是很清楚,今天抽空給大家普及下,感興趣的朋友一起看看吧2021-06-06微信小程序緩存支持二次開(kāi)發(fā)封裝實(shí)現(xiàn)解析
這篇文章主要介紹了微信小程序緩存支持二次開(kāi)發(fā)封裝實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12javascript form 驗(yàn)證函數(shù) 彈出對(duì)話框形式
javascript合法驗(yàn)證 js數(shù)據(jù)驗(yàn)證、js email驗(yàn)證、js url驗(yàn)證、js長(zhǎng)度驗(yàn)證、js數(shù)字驗(yàn)證等(彈出對(duì)話框形式)2009-06-06javascript獲得服務(wù)器端控件的ID的實(shí)現(xiàn)代碼
javascript獲得服務(wù)器端控件的ID的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2011-12-12JavaScript預(yù)編譯的基本概念和過(guò)程詳解
JavaScript在執(zhí)行代碼前,會(huì)進(jìn)行一個(gè)預(yù)編譯的過(guò)程,這個(gè)過(guò)程主要用于處理變量和函數(shù)聲明,預(yù)編譯分為全局預(yù)編譯和函數(shù)預(yù)編譯,本文將給大家詳細(xì)的介紹一下JavaScript預(yù)編譯的基本概念和過(guò)程,需要的朋友可以參考下2024-05-0512個(gè)非常有創(chuàng)意的JavaScript小游戲
JavaScript 在Web開(kāi)發(fā)過(guò)程中已經(jīng)是必不可少的重要分子,他推動(dòng)著Web的交互性往越來(lái)越高的層次發(fā)展,現(xiàn)在的很多Web游戲也基于這類(lèi)語(yǔ)言開(kāi)發(fā)。2010-03-03