0%

常见的几种压缩图片的思路

在做 H5 开发时,难免会遇到有需要上传图片的需求。这种需要我们去引入一些库,或者自己来实现这么个功能。那么一般是如何去处理压缩图片的需求呢?这里简要的概述一下实现原理~

目前主流对图片进行处理都是使用canvas技术~当我们碰到需要压缩图片的场景时,第一种我们就可以尝试控制图片的尺寸。因为图片的尺寸越大,里面包含的信息就越多,自然体积也随着增加了起来。

我们可以做一个宽高的限制,超出就对其进行尺寸的缩放。那么限制最大值是多少呢?这个应该根据产品或者需求来调整。当然,裁剪图片也是一种思路,那么该如何去裁剪,让用户选择还是我们自定义?这也是需要考虑的一点。

判断缩放的方法主要使用drawImage将图片导入canvas,如果图片超过了指定的宽高,就进行缩放图片。关于这一点,我觉得张鑫旭老师的这篇文章讲的已经足够简洁的了,感兴趣的同学可以看这边~


第二种就是使用canvas提供的另一个接口:canvas.toDataURL(type, encoderOptions)。这是浏览器原生提供可以压缩图片的方法,该方法返回一个包含图片展示的data URI(也就是我们常说的base64)。

它接受两个可选参数,我们可以使用type参数指定其类型,默认为PNG格式。encoderOptions则是压缩图片质量参数,区间在 0~1 之间。值得注意的是,压缩图片质量这个参数只对image/jpegimage/webp有效。所幸的是,其他格式用不了压缩图片的参数,但浏览器还是会对图片进行压缩处理,剔除对 web 展示没啥用的元数据(虽然可能会涉及到版权纠纷的问题)。

1
2
3
var canvas = document.getElementById("canvas");
// 中间处理的过程省略 ...
var fullQuality = canvas.toDataURL("image/jpeg", 0.8);

但单单是转为 base64 是不能满足我们一些需求的,这时我们可能会想要让它再转为对服务端友好的blob类型。
这时我们就需要用到window.atob(), 将已经编译成 base64 的字符串解码为二进制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var type = fullQuality.split(',')[0].split(':')[1].split(';')[0];

// 解码为二进制
var binStr = atob(fullQuality.split(',')[1]);
var length = binStr.length;

// 创建一个缓存区,容器大小可以容纳 binStr.
// 这个缓存区我们是不能直接操作的。
var buffer = new ArrayBuffer(binStr);

// 建立`Uint8Array`类型数组
// 这样我们就可以通过对象的方法或者数组索引读写里面的数据啦
//
// var buff = new ArrayBuffer(4); => ArrayBuffer(4) {}
// var arr = new Uint8Array(buff); => Uint8Array(4) [0, 0, 0, 0]
// arr[2] = 8;
// console.log(buff) => 缓存区和 Uint8Array 同时被修改了
var arr = new Uint8Array(buffer);

for (var i = 0; i < length; i++) {
// 逐个查询 binStr 里的二进制 Unicode 编码,并存入类型数组中
arr[i] = binStr.charCodeAt(i);
}

var blob = new Blob([buffer],{ type: type }

如果是想转为File类型的话,和上面的方法实现的也一致..

1
2
3
// https://developer.mozilla.org/en-US/docs/Web/API/File/File
// 第一个参数传入 ArrayBuffer 也行,blob 也行..
var file = new File([buff], "img.jpg", { type: type });

哇,转个类型都看起来好像挺麻烦的样子呢…那么有没有内置的方法呀?答案是有,canvas 里有个toBlob(callback, type, encoderOptions)的方法, 它接受三个参数,一个是将canvas转为blob后接受的回调函数、一个是指定的图片格式、另一个是图片质量,区间也在 0~1 之间。

看起来这个方法似乎比上面那个更好用呢..然而残念的是,这货还受着兼容性影响,不在乎的兼容性的话倒是可以使用… 不然就需要上一种polyfill 的方式来实现~

1
2
3
4
5
6
7
8
9
10
11
12
canvas.toBlob(function (blob) {
// 接收转换后的 blob 类型
// 此时可以在函数里发起请求
$.ajax({
url: baseURL + '/uploader/',
data: { act_id: actID, pic_1: blob },
type: 'POST',
success: function (data) {
// success
}
});
}, 'image/png');
「请笔者喝杯奶茶鼓励一下」

欢迎关注我的其它发布渠道