前言
最近在博客后台上傳圖片的時候,突然發現上傳gif圖片的時候裁剪圖片有問題。既沒法裁剪gif指定區域的圖片,又沒法裁剪指定區域生成一個新的指定大小的gif圖。本來想直接去找個裁剪的庫直接放上去的,但是找了半天也沒找到能夠裁剪gif然后生成裁剪區域的gif的庫,於是就自己動手了。
探索
如果只是單純的在Gif上裁剪第一幀圖片,倒是有插件能實現,我用的就是react-cropper來進行圖片裁剪的。但是這個插件沒法裁剪GIF生成另一個GIF圖。
我要的效果是下面這樣的效果
原圖

裁剪后的gif圖

然后就去查了下如何實現gif圖到gif圖的裁剪,雖然沒有找到對應的插件,但是找到了兩個開源的庫。
發現這兩個功能一組合不就可以實現我要的那個效果了么。
上傳GIF => 通過解析GIF每一幀在Canvas上生成對應的圖像 => canvas轉成GIF
實現
libgif-js的實現過程
libgif-js是通過實現對gif路徑發起一個請求,然后通過解析請求回來的gif數據來生成GIF實例(包括每一幀的動畫,以及大小之類的基礎數據),然后通過GIF實例生成對應的canvas
gif.js的實現過程
通過收集libgif-js轉換到canvas上面的每一幀的變化,來生成最終的GIF。
gif轉換到canvas的實現過程
首先到libgif-js這個項目中下載對應的js文件,因為這個庫並沒有上傳npm,所以需要自己去項目中下載。
libgif-js他這個封裝的是對HTML節點的操作,沒法直接去用,因為我是上傳文件,獲取的File對象,所以需要對這個文件進行部分修改
- 首先應該接收的是一個
url路徑,可以把File文件通過URL.createObjectURL(file)轉成成url,讓其進行XMLHttpRequest請求。 也可以直接傳gif的鏈接。 - 然后需要傳裁剪的區域范圍。裁剪的范圍大小需要適配原gif的尺寸比例
- 去除
libgif-js文件里面不需要的代碼,只需要其中每一幀的圖像集合跟尺寸大小就行
canvas轉換到gif的實現過程
監聽gif繪制到canvas上的每一幀變化,然后gif.js收集每一幀的canvas變化,最后生成新的gif
// 導出gif實例, GifToCanvas實例是對libgif-js封裝的修改,通過調用init方法,觸發gif到canvas的繪制
const gifToCanvas = new GifToCanvas(url, {
targetOffset: {
dx: cropBoxData.left - canvasData.left,
dy: cropBoxData.top - canvasData.top,
width: canvasData.width,
height: canvasData.height,
sWidth: cropBoxData.width,
sHeight: cropBoxData.height
}
})
// 啟動gif轉canvas
gifToCanvas.init()
// 通過 gif.js 庫來收集由 GifToCanvas繪制出來的canvas里面的每一幀,最后生成gif的Blob源。
const gif = new GIF({
workers: 2,
quality: 10,
workerScript: '/static/js/gif.worker.js'
})
const addFrame = (canvas: HTMLCanvasElement, delay: number) => {
gif.addFrame(canvas, { copy: true, delay })
}
// 監聽每一幀的變化,收集每一幀的變化
gifToCanvas.on('progress', (canvas, delay) => {
addFrame(canvas, delay)
})
// 動畫執行完畢,執行gif.render
gifToCanvas.on('finished', (canvas, delay) => {
addFrame(canvas, delay)
gif.render()
})
// canvas生成gif完畢,導出blob, 生成新的文件
gif.on('finished', (blob) => {
const newFile = new File([blob], 'new.gif', { type: blob.type })
// 上傳新的gif文件
const formDate = new FormData()
formDate.append('file', newFile)
...
})
這樣就生成了一個裁剪后的gif文件。
參考資源
本項目完整的代碼:GitHub 倉庫
總結
這個項目也沒有做太多復雜的設置,滿足裁剪GIF的功能就行,因為我目前只需要把gif裁剪成指定大小的gif就行,所以並沒有做太多特制化的功能
個人博客源碼這個項目也上線了這個功能 | 博客源碼項目地址
我自己新創建了一個相互學習的群,無論你是准備入坑的小白,還是半路入行的同學,希望我們能一起分享與交流。
QQ群:810018802, 點擊加入
| QQ群 | 公眾號 |
|---|---|
前端打雜群![]() |
冬瓜書屋![]() |


