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