最近接到上級通知,文件以下:html
奉天承運,公司詔曰:介於前端組表現優異,後端組忙裏偷不出閒,GIF壓縮這一塊特賜予前端組。前端
欽此,git
不一會後端羣衆就送來了親切的問候,千萬個草泥馬在個人心中奔跑github
吃瓜不嫌事大web
罵罵咧咧事後,平復下心情,想一想思路canvas
以前作過圖片壓縮,無非是把file->base64->新建canvas畫圖->導出canvas->轉file後端
這是常規思路,照這個走走看結果如何跨域
實現本地圖片預覽有兩種方法瀏覽器
FileReader
對象容許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩衝區)的內容,使用 File
或 Blob
對象指定要讀取的文件或數據。安全
其中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);
}
複製代碼
URL.createObjectURL()
靜態方法會建立一個 DOMString
,其中包含一個表示參數中給出的對象的URL
。這個 URL
的生命週期和建立它的窗口中的 document 綁定。這個新的URL
對象表示指定的 File
對象或 Blob
對象。
objectURL = window.URL.createObjectURL(blob);
在每次調用 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() 方法返回一個包含圖片展現的 data URI 。可使用 type 參數其類型,默認爲 PNG 格式。圖片的分辨率爲96dpi。
canvas.toDataURL(type, encoderOptions);
type:圖片格式
encoderOptions:在指定圖片格式爲 image/jpeg 或 image/webp的狀況下,能夠從 0 到 1 的區間內選擇圖片的質量。若是超出取值範圍,將會使用默認值 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圖具備下面的特徵
(1)一張圖像最多隻會包含256個RGB值。
(2)在一張連續動態GIF裏,每一幀之間信息差別不大,顏色是被大量重複使用的。
在存儲時,咱們用一個公共的索引表,把圖片中用到的顏色提取出來,組成一個調色盤,這樣,在存儲真正的圖片點陣時,只須要存儲每一個點在調色盤裏的索引值。
若是調色盤放在文件頭,做爲全部幀公用的信息,就是公共(全局)調色盤,若是放在每一幀的幀信息中,就是局部調色盤。GIF格式容許兩種調色盤同時存在,在沒有局部調色盤的狀況下,使用公共調色盤來渲染。
這樣,咱們能夠用調色盤裏的索引來表明實際的顏色值。
一個256色的調色盤,24bit的顏色只須要用9bit就能夠表達了。
調色盤還能夠進一步減小,128色,64色,etc,相應的壓縮率就會愈來愈大……
所謂的每一幀,其實和分辨率差很少,幀數過快也就越清晰同時文件也也大
通過查閱資料和分析能夠得出想要壓縮GIF無非從兩方面着手
1. 若是能夠接受犧牲圖像的部分視覺效果,就能夠經過減色來對圖像作進一步壓縮。
2. 若是能夠接受頓挫感(也就是不怎麼流暢),就能夠經過減幀來對圖像作進一步壓縮。
原圖: 能夠看到原圖大小1823814b
//compressGif方法見附錄
compressGif (file,256,classId, uploadId)
複製代碼
減幀壓縮後: 能夠看到減幀壓縮後大小爲1617818b
減幀減色壓縮後:(極限值只有黑白2種顏色)
//compressGif方法見附錄
compressGif (file,2,classId, uploadId)
複製代碼
能夠看到減幀壓縮後大小爲48653b
,壓縮結果仍是很是可觀的!
gifmin.js
:一個GIF圖片壓縮庫,若是對你有幫助,請點亮你的小星星⭐⭐⭐哦~(瘋狂暗示)
超級簡單的API,提供手把手教你使用的實例!
因爲壓縮須要必定時間,建議加個loading效果
gifmin.min.js
到本地,放入靜態資源文件夾index.html
引入<script src="/lib/gifmin.js"></script>
複製代碼
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()
}
}
}
複製代碼
/** * 下載方法 * @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')
}
}
}
複製代碼
/** * 壓縮方法 * @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,一個前端,熱愛技術也熱愛生活。
與你相逢,我很開心。
文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊
本文首發於掘金,未經許可禁止轉載💌