JavaScript純前端實(shí)現(xiàn)在線GIF壓縮
前言
原因是我在寫公眾號(hào)文章的時(shí)候,公眾號(hào)編輯器只能上傳最大10M
的GIF
,而我剛好需要上傳的GIF
超過了這個(gè)閾值。
所以我就隨便搜了一個(gè)壓縮GIF
的工具,有一些壓縮完了下載需要付費(fèi),有一些不付費(fèi)就只能壓縮低于某個(gè)閾值的文件。
不過也能理解吧,畢竟別人也是需要賺錢的,這種文件的處理如果放在服務(wù)端做,那必然是要耗費(fèi)不少資源的,如果放在純前端做,又不能保證文件處理的速度。
然后我就想著GIF
壓縮應(yīng)該不難吧,應(yīng)該有現(xiàn)成的工具庫吧,于是我就開始動(dòng)手去實(shí)現(xiàn)一個(gè)GIF
壓縮工具。這里我很執(zhí)拗地要去實(shí)現(xiàn)一個(gè)純客戶端的GIF
壓縮(也不知道為啥我這么執(zhí)拗。)
初探GIF壓縮
我們都知道GIF
是一張張靜態(tài)的圖片播放形成的動(dòng)圖,所以我一開始就想著,如果我有一個(gè)庫,能幫我把GIF
所有幀都抽取出來,然后我對(duì)所有幀的圖片進(jìn)行一個(gè)縮放的有損壓縮,壓縮完之后再把所有圖片合成一個(gè)新的GIF
,那么我不就把GIF
給壓縮了么。
然后我就找到了gif.js
和omggif.js
這兩個(gè)庫:
omggif
負(fù)責(zé)解析GIF
,獲取GIF
的每一幀圖像- 把獲取到的每一幀圖像進(jìn)行處理了之后,使用
gif.js
合成GIF
那么可以寫出下面的代碼,以下的代碼就是一個(gè)分離GIF
幀+合成GIF
的代碼,還沒有加上壓縮每一幀的邏輯:
fetch("/test.gif") .then((response) => response.arrayBuffer()) .then((buffer) => { // 用omggif解析GIF let reader = new omggif.GifReader(new Uint8Array(buffer)); // 創(chuàng)建新的gif.js實(shí)例 let gif = new GIF({ workers: 2, quality: 5, width: reader.width, height: reader.height, }); const width = reader.width; const height = reader.height; // 創(chuàng)建canvas元素 const imgs = []; // 遍歷所有的frames,并添加到gif.js實(shí)例中 for (let i = 0; i < reader.numFrames(); i++) { let frameInfo = reader.frameInfo(i); let canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; let ctx = canvas.getContext("2d", { willReadFrequently: true }); let imageData = ctx.createImageData(width, height); reader.decodeAndBlitFrameRGBA(i, imageData.data); ctx.putImageData(imageData, 0, 0); gif.addFrame(ctx, { delay: frameInfo.delay }); imgs.push(canvas.toDataURL()); } //預(yù)覽分離出來的幀 const div = document.createElement("div"); imgs.forEach((url) => { const img = document.createElement("img"); img.src = url; div.appendChild(img); }); document.body.appendChild(div); // 生成GIF并下載 gif.on("finished", function (blob) { console.log("finish"); let url = URL.createObjectURL(blob); document.querySelector("#img").src = url; const a = document.createElement("a"); a.download = true; a.href = url; a.click(); }); gif.render(); });
但是我分離幀之后發(fā)現(xiàn),分離出來的幀相當(dāng)奇怪:
而且合并之后的GIF
感覺完全被破壞掉了,我不清楚是不是我的這種思路本身就是不可行的,還是說這兩個(gè)庫不能這么用。是不是我這樣做了之后導(dǎo)致描述GIF
的信息丟了?(比如調(diào)色盤或者其他的一些全局信息,評(píng)論區(qū)有大神可以指導(dǎo)一下么)
這種方式不可行之后,我又嘗試了一下別的方式。這一次我不再寄希望于純js
實(shí)現(xiàn)的庫,而是往wasm
方向去探索。
然后我嘗試使用Rust
去解析GIF
,并返回所有解析后的圖片給前端,前端再去做有損壓縮,但這一做法耗時(shí)太長(zhǎng),也不是成功的嘗試,所以代碼就不放出來了。
接著我就想到了ffmpeg
,我知道它有壓縮GIF
的功能,而且他也有成熟的wasm
版本,可以便捷地在前端引入使用,但是它壓縮后近10M
的體積依舊讓人望而生畏。
gifsicle
在繼續(xù)搜索GIF
壓縮等關(guān)鍵詞時(shí),發(fā)現(xiàn)了gifsicle
這個(gè)庫。它是一個(gè)用于處理GIF
圖像的命令行工具和庫,提供了很多功能包括創(chuàng)建、編輯、優(yōu)化和調(diào)整GIF
。
與ffmpeg
對(duì)比起來,它是一個(gè)更專注于GIF處
理的庫,所以體積會(huì)比ffmpeg
肯定小不少,所以我就想著能不能弄一個(gè)它的wasm
版本移植到瀏覽器中使用。
后面還真讓我找到了它對(duì)應(yīng)的wasm
版本,具體鏈接可以查看:gifsicle-wasm-browse,這個(gè)庫GZip
壓縮之后只有150K
左右,完全不用擔(dān)心體積問題。
使用gifsicle
的時(shí)候,常常使用如下的手段去減少GIF
的體積:
-O
參數(shù):
- O1:該選項(xiàng)對(duì)應(yīng)于輕度優(yōu)化,它會(huì)執(zhí)行一些基本的優(yōu)化步驟,刪除無用的圖像數(shù)據(jù)和元數(shù)據(jù),以減小文件大小。這個(gè)級(jí)別的優(yōu)化通常執(zhí)行較快,但可能不會(huì)最大限度地減小文件大小。
- O2:中度優(yōu)化比輕度優(yōu)化更深入,它可能會(huì)花費(fèi)更多的時(shí)間來尋找更多的優(yōu)化機(jī)會(huì)。這通常會(huì)導(dǎo)致更好的壓縮比和更小的文件大小。
- O3:最大優(yōu)化級(jí)別會(huì)執(zhí)行最深入的優(yōu)化,可能需要更多的時(shí)間來完成,但通常會(huì)實(shí)現(xiàn)最大的文件大小減小。這個(gè)級(jí)別的優(yōu)化可能對(duì)CPU和內(nèi)存的需求更高。
--loosy
:
--lossy
會(huì)減少圖像中使用的顏色數(shù)量,從而減小顏色表的大小。這會(huì)導(dǎo)致顏色的近似和失真,因?yàn)樵嫉念伾畔?huì)被近似為顏色表中的顏色。- 高
--lossy
程度下,一些微小的圖像細(xì)節(jié)可能會(huì)被去除,以進(jìn)一步減小文件大小。這可能導(dǎo)致圖像的一些細(xì)節(jié)在視覺上的損失
減少顏色數(shù):
GIF
中每個(gè)像素可以使用圖像調(diào)色板中的一種顏色,而顏色數(shù)量表示這些不同顏色的總數(shù)。GIF
使用的顏色調(diào)色板通常是8
位色,允許最多256
種不同的顏色。- 減少GIF的顏色數(shù)量可以降低圖像的文件大小,因?yàn)閳D像中使用的顏色越少,每個(gè)像素的顏色信息所需的位數(shù)就越少。然而,減少顏色數(shù)量也可能導(dǎo)致圖像的視覺質(zhì)量下降,尤其是對(duì)于包含大量顏色細(xì)節(jié)的圖像。
刪除注釋和元數(shù)據(jù)
裁剪和縮?。鹤钪庇^的壓縮手段,屬于有損壓縮
調(diào)整幀速率:相當(dāng)于減少組成這個(gè)GIF
文件的圖片數(shù)量,也是有損壓縮
具體實(shí)現(xiàn)
首先安裝gifsicle-wasm-browser
這個(gè)包,然后簡(jiǎn)單搭建一個(gè)表單如下:
gifsicle
的具體文檔,可以點(diǎn)擊這里查看。
在前端中可以參照以下方式來使用gifsicle
:
gifsicle .run({ input: [ { file: buffer, name: "input.gif", }, ], command: [command], }) .then(async (res) => { });
在GIF
的壓縮過程中,常常會(huì)把多種壓縮方式結(jié)合起來使用。如果單純的想壓縮GIF
的體積而不考慮GIF
的質(zhì)量,那就直接使用縮放進(jìn)行有損壓縮就好了;如果想保留GIF
的質(zhì)量的同時(shí)壓縮GIF
的體積,那還是需要多方參數(shù)的組合嘗試。
壓縮前的圖像:
以下是我一些嘗試壓縮的參數(shù)以及壓縮前后的對(duì)比:
壓縮參數(shù): -O2 --lossy=180 input.gif --colors 64 --scale 0.8 -o /out/out.gif
- 壓縮后:體積:4.0MB,顏色數(shù)量:64,幀數(shù)不變,分辨率不變
- 執(zhí)行時(shí)間:72s
壓縮參數(shù):-O2 --lossy=180 input.gif --colors 32 --scale 0.8 -o /out/out.gif
- 壓縮后:體積:3MB,顏色數(shù)量:32,幀數(shù)不變,分辨率不變
- 執(zhí)行時(shí)間:60s
- 可見圖像顏色已經(jīng)嚴(yán)重變形
壓縮參數(shù):input.gif --colors 64 --scale 0.5 -o /out/out.gif
- 壓縮后:體積:3MB,顏色數(shù)量:64,幀數(shù)不變,分辨率變?yōu)橹耙话?/li>
- 執(zhí)行時(shí)間:19s
- 圖像較為模糊,顏色也有少許變形
可以看到在大多數(shù)的壓縮參數(shù)下,都需要較長(zhǎng)的執(zhí)行時(shí)間,這是因?yàn)檫@個(gè)wasm包還沒有實(shí)現(xiàn)多核處理,還沒辦法利用多核CPU的優(yōu)勢(shì),所以處理起來時(shí)間會(huì)比較長(zhǎng)。
總結(jié)
如果這個(gè)包實(shí)現(xiàn)了多核處理,執(zhí)行時(shí)間能縮短一些的話,那么我覺得會(huì)有更多的GIF處理會(huì)放在前端來做。
我一直覺得純前端處理是一件很酷的事情,對(duì)于開發(fā)者來說,無需承擔(dān)昂貴的計(jì)算資源成本,如果不想承擔(dān)服務(wù)器成本,這個(gè)時(shí)候還有一種解決方法就是做成桌面應(yīng)用,把任務(wù)處理的二進(jìn)制文件打包下載到用戶本地。
但如果是純前端實(shí)現(xiàn)的話,對(duì)于使用者來說,無需下載任何東西,點(diǎn)開即用。
所以后面也理解了網(wǎng)上一些壓縮GIF
需要收費(fèi)的工具,一個(gè)好的在線GIF
壓縮產(chǎn)品它能同時(shí)保證執(zhí)行速度、壓縮體積、壓縮質(zhì)量,這還是相當(dāng)不容易的。
以上就是JavaScript純前端實(shí)現(xiàn)在線GIF壓縮的詳細(xì)內(nèi)容,更多關(guān)于JavaScript GIF壓縮的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解讀CocosCreator源碼之引擎啟動(dòng)與主循環(huán)
這篇文章主要介紹了CocosCreator源碼解讀之引擎啟動(dòng)與主循環(huán),對(duì)CocosCreator感興趣的同學(xué),可以研究參考一下2021-04-04bootstrap3使用bootstrap datetimepicker日期插件
這篇文章主要為大家詳細(xì)介紹了bootstrap3中使用bootstrap datetimepicker日期插件的用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05一文搞懂TypeScript的安裝、使用、自動(dòng)編譯的教程
TypeScript 是一種由微軟開發(fā)的開源、跨平臺(tái)的編程語言。它是 JavaScript 的超集,最終會(huì)被編譯為 JavaScript 代碼,關(guān)于TypeScript的安裝、使用、自動(dòng)編譯很多朋友不是很清楚,今天抽空給大家普及下,感興趣的朋友一起看看吧2021-06-06LayUI+Shiro實(shí)現(xiàn)動(dòng)態(tài)菜單并記住菜單收展的示例
這篇文章主要介紹了LayUI+Shiro實(shí)現(xiàn)動(dòng)態(tài)菜單并記住菜單收展的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05小程序按鈕避免多次調(diào)用接口和點(diǎn)擊方案實(shí)現(xiàn)(不用showLoading)
這篇文章主要介紹了小程序按鈕避免多次調(diào)用接口和點(diǎn)擊方案實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04原生JS實(shí)現(xiàn)輪播效果+學(xué)前端的感受(防止走火入魔)
下面小編就為大家?guī)硪黄鶭S實(shí)現(xiàn)輪播效果+學(xué)前端的感受(防止走火入魔)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08JavaScript實(shí)現(xiàn)的簡(jiǎn)單拖拽效果
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的簡(jiǎn)單拖拽效果,涉及javascript針對(duì)鼠標(biāo)事件與頁面樣式的操作技巧,需要的朋友可以參考下2015-06-06初探j(luò)s和簡(jiǎn)單隱藏效果的實(shí)例
下面小編就為大家分享一篇初探j(luò)s和簡(jiǎn)單隱藏效果的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-11-11JS彈窗 JS彈出DIV并使整個(gè)頁面背景變暗功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了JS彈窗 JS彈出DIV并使整個(gè)頁面背景變暗功能的實(shí)現(xiàn)代碼,需要的朋友可以參考下2018-04-04