前端图片压缩的意义
现在一些手机设备拍摄的图片往往达到几百k至几十M。但是真实项目需求中并可能不需要用到太精细的图片。例如用户头像上传的图片一般限制在2M或5M以内,如果出现用户手机拍摄的照片超过此限制而无法上传,使得用户体验极差。因此在前端进行图片压缩到相应限制内,提高用户的使用体验。
同时图片压缩后上传,由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅,同时大大降低了网络异常导致上传失败风险。
获取要压缩的图片
在图片上传之前,我们首先要获取需要压缩的图片。通过 HTML 5 file API 的FileReader方法,代码如下:
// 读取图片为base64格式的方法
function getImageToBase64 (file,callback) {
// file可以通过 input file 获取
let reader = new FileReader();
reader.addEventListener('load',function(e){
const base64Image = e.target.result;
// 在回调函数中处理base64
callback && callback(base64Image);
reader = null;
})
// readAsDataURL方法将文件读取为base64格式
reader.readAsDataURL(file);
}
canvas API 压缩图片
思路:我们使用canvas的drawImage()将图片绘制到canvas中,并使用toDataURL()方法将图片压缩
drawImage
根据MDN上的描述:
Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种在画布(Canvas)上绘制图像的方式。
drawImage(image, dx, dy)
drawImage(image, dx, dy, dWidth, dHeight)
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
根据图片示意,参数image代表当前获取的图片。dx,dy,dWidth,dHeight代表图片在画布中的位置和宽高。sx, sy, sWidth, sHeight,代表图片裁剪大小,如果没有设置这四个值图片将会拉伸或缩放在画布上划定的区域中。
canvas提供了两个转换为图片的方法
canvas.toDataURL()
方法,此方法将canvas转换成base64格式图片
canvas.toDataURL(mimeType, qualityArgument)
其中,mimeType表示canvas导出来的base64图片的类型,默认是png格式,也即是默认值是’image/png’,我们也可以指定为jpg格式’image/jpeg’或者webp等格式。file对象中的file.type就是文件的mimeType类型,在转换时候正好可以直接拿来用(如果有file对象)。
qualityArgument表示导出的图片质量,只要导出为jpg和webp格式的时候此参数才有效果,默认值是0.92,是一个比较合理的图片质量输出参数,通常情况下,我们无需再设定。
canvas.toBlob()
canvas.toBlob(callback, mimeType, qualityArgument)
可以把canvas转换成Blob文件,和toDataURL()方法相比,toBlob()方法是异步的,因此多了个callback参数。
注意:image的src中可以接收图片的base64,我们将前面的base64赋予image对象的src属性来获取image参数
代码如下:
const image = new Image();
// base64Image之前读取的base64编码格式的图片
image.src = base64Image
// 通过监听image的load事件防止图片获取不到
image.addEventListener('load',function(event){
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d');
// 获取原图的宽高并设置canvas的宽高
let width = image.naturalWidth
let height = image.naturalHeight
canvas.width = width;
canvas.height = height;
// 将图片添加到画布中
ctx.drawImage(image,0,0,width,height);
const compressImage = canvas.toDataURL('image/jpeg',0.8)
})
此时成功获取到了压缩后的图片的base64,如果需要上传文件格式到后端,可以使用toBlob()方法实现。
限制图片宽高
某些场景下,图片宽度高度同样有限制,假如图片宽度不得超过1024,高度不得超过1024。此时,我们有一张宽度为2048 高度为1024 的图片,图片宽度超出限制,我们将图片宽度缩小到最大限度的1024正好缩小一倍,高度需要同比缩小一倍。
let width = image.naturalWidth,height = image.naturalHeight
const maxW = 1024
const maxH = 1024
// 判断宽度高度是否超过限制并等比缩放
if (width > maxW) {
height = height / ( width / maxW )
width = maxW
}
if (height>maxH) {
width = width / ( height / maxH )
height = maxH
}
待解决的问题
了解了toDataURL方法中的 qualityArgument 参数是用来控制图片压缩的程度,参数范围是0-1数值越小压缩成都越大。
那么图片压缩的具体大小是否是可控呢?如果压缩一次图片后仍然超出大小限制该怎么办?toDataURL的压缩原理是什么是否可以不借用canvas实现图片压缩还有待研究。