最近接到上级通知,文件以下: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. 若是能够接受顿挫感(也就是不怎么流畅),就能够经过减帧来对图像作进一步压缩。
原图: 能够看到原图大小1823814
b
//compressGif方法见附录
compressGif (file,256,classId, uploadId)
复制代码
减帧压缩后: 能够看到减帧压缩后大小为1617818
b
减帧减色压缩后:(极限值只有黑白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,一个前端,热爱技术也热爱生活。
与你相逢,我很开心。
文中若有错误,欢迎在评论区指正,若是这篇文章帮到了你,欢迎点赞和关注😊
本文首发于掘金,未经许可禁止转载💌