前端玩轉GIF圖片壓縮

前言

最近接到上級通知,文件以下:html

奉天承運,公司詔曰:介於前端組表現優異,後端組忙裏偷不出閒,GIF壓縮這一塊特賜予前端組。前端

欽此,git

不一會後端羣衆就送來了親切的問候,千萬個草泥馬在個人心中奔跑github

吃瓜不嫌事大web

罵罵咧咧事後,平復下心情,想一想思路canvas

拋磚引玉-壓縮圖片

以前作過圖片壓縮,無非是把file->base64->新建canvas畫圖->導出canvas->轉file後端

  • input 讀取到 image/file ,使用 FileReader 將其轉換爲 base64 編碼
  • 新建 img ,使其 src 指向剛剛的 base64
  • 新建 canvas ,將 img 畫到 canvas 上
  • 利用 canvas.toDataURL/toBlob 將 canvas 導出爲 base64 或 Blob
  • 將 base64 或 Blob 轉化爲 File

這是常規思路,照這個走走看結果如何跨域

預覽圖片

實現本地圖片預覽有兩種方法瀏覽器

1.new FileReader

FileReader 對象容許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩衝區)的內容,使用 FileBlob 對象指定要讀取的文件或數據。安全

語法

其中File對象能夠是來自用戶在一個<input>元素上選擇文件後返回的FileList對象,也能夠來自拖放操做生成的 DataTransfer對象,還能夠是來自在一個HTMLCanvasElement上執行mozGetAsFile()方法後返回結果。

附註

重要提示: FileReader僅用於以安全的方式從用戶(遠程)系統讀取文件內容 它不能用於從文件系統中按路徑名簡單地讀取文件。 要在JavaScript中按路徑名讀取文件,應使用標準Ajax解決方案進行服務器端文件讀取,若是讀取跨域,則使用CORS權限。

function handleFile(e) {
  var file = e.files[0];
  var fileReader = new FileReader();
  var img = document.getElementById(id);
  fileReader.onload = function(e) {
    img.src = e.target.result;
  }
  fileReader.readAsDataURL(file);
}
複製代碼

2.createObjectURL

window.URL.createObjectURL

概述

URL.createObjectURL() 靜態方法會建立一個 DOMString,其中包含一個表示參數中給出的對象的URL。這個 URL 的生命週期和建立它的窗口中的 document 綁定。這個新的URL 對象表示指定的 File 對象或 Blob 對象。

語法

objectURL = window.URL.createObjectURL(blob);

  • blob參數是一個File對象或者Blob對象.
  • objectURL是生成的對象URL.經過這個URL,能夠獲取到所指定文件的完整內容.

附註

在每次調用 createObjectURL() 方法時,都會建立一個新的 URL 對象,即便你已經用相同的對象做爲參數建立過。當再也不須要這些 URL 對象時,每一個對象必須經過調用 URL.revokeObjectURL() 方法來釋放。

瀏覽器在 document 卸載的時候,會自動釋放它們,可是爲了得到最佳性能和內存使用情況,你應該在安全的時機主動釋放掉它們。

function handleFile() {
  var url = window.URL.createObjectURL(file);
  var img = document.getElementById(id);
  img.src = url
  img.onload = function () {
    // 加載完成後銷燬url,節省性能
    window.URL.revokeObjectURL(videoUrl);
  }
}
複製代碼

壓縮圖片

HTMLCanvasElement.toDataURL()

概述

HTMLCanvasElement.toDataURL() 方法返回一個包含圖片展現的 data URI 。可使用 type 參數其類型,默認爲 PNG 格式。圖片的分辨率爲96dpi。

  • 若是畫布的高度或寬度是0,那麼會返回字符串「data:,」。
  • 若是傳入的類型非「image/png」,可是返回的值以「data:image/png」開頭,那麼該傳入的類型是不支持的。
  • Chrome支持「image/webp」類型。

語法

canvas.toDataURL(type, encoderOptions);
type:圖片格式
encoderOptions:在指定圖片格式爲 image/jpeg 或 image/webp的狀況下,能夠從 01 的區間內選擇圖片的質量。若是超出取值範圍,將會使用默認值 0.92。其餘參數會被忽略。
var mediumQuality = canvas.toDataURL("image/jpeg", 0.5);
var lowQuality = canvas.toDataURL("image/jpeg", 0.1);
複製代碼

附註

function image2Base64(img) {
  var canvas = document.createElement("canvas");
  let w = img.width;
  let h = img.height;
  let quality = 0.5; 
  canvas.width = w;
  canvas.height = h;
  var ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, w, h);
  var dataURL = canvas.toDataURL("image/png",quality);
  return dataURL;
}
// 將dataURL轉化爲file對象的函數
function dataURLtoFile(dataurl, filename) {
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, { type: mime });
}
複製代碼

以這種方式壓縮GIF,會讓動圖變靜圖...顯然是要不走尋常路

真知灼見-GIF圖片壓縮

準備

查閱資料

在實際狀況中,GIF圖具備下面的特徵

(1)一張圖像最多隻會包含256個RGB值。

(2)在一張連續動態GIF裏,每一幀之間信息差別不大,顏色是被大量重複使用的。

思考分析

  • 在存儲時,咱們用一個公共的索引表,把圖片中用到的顏色提取出來,組成一個調色盤,這樣,在存儲真正的圖片點陣時,只須要存儲每一個點在調色盤裏的索引值。

  • 若是調色盤放在文件頭,做爲全部幀公用的信息,就是公共(全局)調色盤,若是放在每一幀的幀信息中,就是局部調色盤。GIF格式容許兩種調色盤同時存在,在沒有局部調色盤的狀況下,使用公共調色盤來渲染。

  • 這樣,咱們能夠用調色盤裏的索引來表明實際的顏色值。

  • 一個256色的調色盤,24bit的顏色只須要用9bit就能夠表達了。

  • 調色盤還能夠進一步減小,128色,64色,etc,相應的壓縮率就會愈來愈大……

  • 所謂的每一幀,其實和分辨率差很少,幀數過快也就越清晰同時文件也也大

得出結論

通過查閱資料和分析能夠得出想要壓縮GIF無非從兩方面着手

1. 若是能夠接受犧牲圖像的部分視覺效果,就能夠經過減色來對圖像作進一步壓縮。

2. 若是能夠接受頓挫感(也就是不怎麼流暢),就能夠經過減幀來對圖像作進一步壓縮。

實踐

1.經過減幀來對圖像作進一步壓縮。

原圖: 原圖 能夠看到原圖大小1823814b

//compressGif方法見附錄
compressGif (file,256,classId, uploadId)
複製代碼

減幀壓縮後: 減幀壓縮後 能夠看到減幀壓縮後大小爲1617818b

2.在減幀的基礎上經過減色來對圖像作進一步壓縮。

減幀減色壓縮後:(極限值只有黑白2種顏色)

//compressGif方法見附錄
compressGif (file,2,classId, uploadId)
複製代碼

能夠看到減幀壓縮後大小爲48653b,壓縮結果仍是很是可觀的!

gifmin.js:一個GIF圖片壓縮庫,若是對你有幫助,請點亮你的小星星⭐⭐⭐哦~(瘋狂暗示)

超級簡單的API,提供手把手教你使用的實例!

gifmin.js下載地址

實戰

Vue使用Demo

因爲壓縮須要必定時間,建議加個loading效果

  1. 下載gifmin.min.js到本地,放入靜態資源文件夾
  2. index.html引入
<script src="/lib/gifmin.js"></script>
複製代碼
  1. 使用Demo
gifImg(file) {
      const loadings = this.$loading({
        lock: true,
        text: 'GIF圖片壓縮中...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0,0.7)'
      })
      const that = this
      if (window.FileReader) {
        var fr = new FileReader()
        fr.readAsArrayBuffer(file)
        fr.onload = function(e) {
          var colors = 70 // 介於0~256之間數值越小壓縮後文件越小
          // eslint-disable-next-line no-undef
          var result = gifmin(fr.result, colors) // 二進制文件流
          console.log(result)
          var obj = new Blob([result], { // 轉換成Blob對象
            type: 'application/octet-stream'
          })
          that.gifFile = new window.File([obj], file.name, { // 轉換成file文件
            type: file.type
          })
          that.gifUrl = window.URL.createObjectURL(file)
          that.formData.gifUrl = 'yes'
          loadings.close()
        }
      }
    }
複製代碼

壓縮GIF後下載,實如今線壓縮

/** * 下載方法 * @param {String} blobUrl 須要壓縮的GIF文件Blob對象轉化的url * @param {String} filename 下載文件的文件名 */
function downloadFileByBlob(blobUrl, filename) {
  const eleLink = document.createElement('a')
  eleLink.download = filename
  eleLink.style.display = 'none'
  eleLink.href = blobUrl
  // 觸發點擊
  document.body.appendChild(eleLink)
  eleLink.click()
  // 而後移除
  document.body.removeChild(eleLink);
  // 圖片下載後銷燬url,節省性能
  window.URL.revokeObjectURL(blobUrl);
}
/** * 壓縮方法 * @param {Object} file 須要壓縮的GIF文件 * @param {Number} colors 0~256 */
function compressGif (file,colors) {
    var _file = file;
    console.log(_file)
    if (window.FileReader) {
      var fr = new FileReader();
      fr.readAsArrayBuffer(_file);
      fr.onload = function (e) {
        console.log(fr.result)//fr.result===e.target.result爲ArrayBuffer文件
        var result = gifmin(fr.result, colors);
        console.log(result)
        var obj = new Blob([result], {//轉化Blob對象
          type: 'application/octet-stream'
        });
        var blobUrl = window.URL.createObjectURL(obj);
        //調用下載方法
        downloadFileByBlob(blobUrl,'1.gif')
      }
    }
}
複製代碼

jq壓縮demo見附錄

附錄

/** * 壓縮方法 * @param {Object} file 須要壓縮的GIF文件 * @param {Number} colors 0~256 * @param {String} classId 回顯div的Id * @param {String} uploadId 選擇文件input的Id */
function compressGif (file,colors,classId, uploadId) {
    var _file = file;
    console.log(_file)
    if (window.FileReader) {
      var fr = new FileReader();
      fr.readAsArrayBuffer(_file);
      fr.onload = function (e) {
        console.log(fr.result)//fr.result===e.target.result爲ArrayBuffer文件
        var result = gifmin(fr.result, colors);
        console.log(result)
        var obj = new Blob([result], {//轉化Blob對象
          type: 'application/octet-stream'
        });
        console.log(obj)
        const Blob2ImageFileForWXBrowser = new window.File([obj], _file.name, {//將壓縮好的GIF轉化成file文件
          type: _file.type
        });
        console.log(Blob2ImageFileForWXBrowser)
        var gifUrl = window.URL.createObjectURL(file); //GIF圖片回顯用
        // 回顯圖片
        $('.' + classId).siblings().remove();
        if (classId == 'upload-btn') {
          $('.' + classId).before(`<div class="upload-item upload-item-one" style="display:inline-block;margin-right:5px;"> <img id="upload-item-one" src="${gifUrl}" index-video='1'> </div>`);
        }
        uploadImgs.push(Blob2ImageFileForWXBrowser);//添加到上傳參數中
        $('#' + uploadId).val('');
        $('#upload-item-one').onload = function () {
          // 若是不生效,請在模態框關閉時銷燬
          // 加載完成後銷燬url,節省性能
          window.URL.revokeObjectURL(gifUrl);
        }
      }
    }
}
複製代碼

寫在最後

我是涼城a,一個前端,熱愛技術也熱愛生活。

與你相逢,我很開心。

若是你想了解更多,請點這裏,期待你的小⭐⭐

  • 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊

  • 本文首發於掘金,未經許可禁止轉載💌

相關文章
相關標籤/搜索