GIF圖片裁剪出指定大小的GIF圖片


前言

最近在博客后台上傳圖片的時候,突然發現上傳gif圖片的時候裁剪圖片有問題。既沒法裁剪gif指定區域的圖片,又沒法裁剪指定區域生成一個新的指定大小的gif圖。本來想直接去找個裁剪的庫直接放上去的,但是找了半天也沒找到能夠裁剪gif然后生成裁剪區域的gif的庫,於是就自己動手了。

探索

如果只是單純的在Gif上裁剪第一幀圖片,倒是有插件能實現,我用的就是react-cropper來進行圖片裁剪的。但是這個插件沒法裁剪GIF生成另一個GIF圖。

我要的效果是下面這樣的效果

原圖

原圖

裁剪后的gif圖

原圖

然后就去查了下如何實現gif圖到gif圖的裁剪,雖然沒有找到對應的插件,但是找到了兩個開源的庫。

  1. libgif-js 通過解析GIF,生成Canvas
  2. gif.js 把canvas轉換成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對象,所以需要對這個文件進行部分修改

  1. 首先應該接收的是一個url路徑,可以把File文件通過URL.createObjectURL(file)轉成成url,讓其進行XMLHttpRequest請求。 也可以直接傳gif的鏈接。
  2. 然后需要傳裁剪的區域范圍。裁剪的范圍大小需要適配原gif的尺寸比例
  3. 去除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群 公眾號
前端打雜群
QQ群:810018802
冬瓜書屋
公眾號:冬瓜書屋


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM