“The best way to cheer yourself up is to try to cheer somebody else up.”
为什么要前端图片压缩
- 节省流量
- 减少上传时间,增加用户体验
图片压缩实现
先看demo image-compress-qinniu
可以配置图片信息然后上传图片到七牛云一气呵成。是的 看似简单的过程背后是一连串的代码逻辑。
主要实现逻辑就是先用表单控件<input type="file" multiple/>
获取到文件列表信息,然后利用FileReader
这个类将拿到的这个文件列表另存为图片URL,再利用canvas
将图片压缩。
canvas的drawImage()方法
这是图片宽高压缩的关键方法
context.drawImage(img, dx, dy);
context.drawImage(img, dx, dy, dWidth, dHeight);
context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
我们主要用的就是context.drawImage(img, dx, dy, dWidth, dHeight);
各参数功能:
image
- 绘制到上下文的元素。允许任何的 canvas 图像源(
CanvasImageSource
),例如:HTMLImageElement
,HTMLVideoElement
,或者HTMLCanvasElement
。 dx
- 目标画布的左上角在目标canvas上 X 轴的位置。
dy
- 目标画布的左上角在目标canvas上 Y 轴的位置。
dWidth
- 在目标画布上绘制图像的宽度。 允许对绘制的图像进行缩放。 如果不说明, 在绘制时图片宽度不会缩放。
dHeight
- 在目标画布上绘制图像的高度。 允许对绘制的图像进行缩放。 如果不说明, 在绘制时图片高度不会缩放。
sx
- 需要绘制到目标上下文中的,源图像的矩形选择框的左上角 X 坐标。
sy
- 需要绘制到目标上下文中的,源图像的矩形选择框的左上角 Y 坐标。
sWidth
- 需要绘制到目标上下文中的,源图像的矩形选择框的宽度。如果不说明,整个矩形从坐标的sx和sy开始,到图像的右下角结束。
sHeight
- 需要绘制到目标上下文中的,源图像的矩形选择框的高度。
mozilla上的配图:
canvas的toDataURL()方法和toBlob()方法
这两个方法主要是将canvas转换成base64和blob(二进制)图片类型。并进行图片质量压缩。
canvas.toDataURL()
canvas.toDataURL(mimeType, qualityArgument)
这个方法是将图片转换成base64类型
其中: 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)
和toDataURL()方法相比,toBlob()方法是异步的(所以会看到代码逻辑里用到了大量的Promise
),因此多了个callback参数,这个callback回调方法默认的第一个参数就是转换好的blob文件信息,本文demo的文件上传就是将canvas图片转换成二进制的blob文件
FileReader
这是html5里新增的方法,主要是将type="file"
表单控制获取到的图片信息变成可用的图片地址。使用如下:
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
};
因为这个也是异步的所以也会用到Promise
模块化
compress.js
/**
* Created by joubn on 2018/08/01.
*/
const compressImgSource = (img,maxW,maxH,quality) => {
const canvas = document.createElement('canvas');
let width = img.width;
let height = img.height;
const max_width = maxW || 640;
const max_height = maxH || 640;
quality = quality || 1
if (width > max_width) {
height *= max_width / width;
height = Math.round(height);
width = max_width;
}
if(height > max_height){
width *= max_height / height;
width = Math.round(width);
height = max_height;
}
canvas.width = width
canvas.height = height
const wh = {width, height}
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
let resBase64, base64, blob;
return new Promise((resolve, reject) => {
resBase64 = canvas.toDataURL("image/jpeg",quality);
base64 = Object.assign({},wh,{res:resBase64})
canvas.toBlob(data => {
blob = Object.assign({},wh,{res:data})
resolve({base64,blob});
}, "image/jpeg", quality)
})
}
const compressImg = ([...files],maxW,maxH,quality) => {
if(typeof FileReader==='undefined'){
new Error( '不支持图片上传' );
return false;
}
let arr = [],promArr = [];
files.forEach(file => {
if (file.type.indexOf("image") == 0) {
let reader = new FileReader();
reader.readAsDataURL(file);
promArr.push(
new Promise((resolve, reject) => {
reader.onload = (e) => {
let image = new Image();
image.src = e.target.result;
image.onload = async () => {
let res = await compressImgSource(image, maxW, maxH, quality)
resolve(res);
}
};
})
)
}
})
return promArr
}
export {compressImg, compressImgSource}
使用方法:
import { compressImg } from './compress'
Promise.all(compressImg(fileList)).then(imgs => {
// imgs 为所有图片信息 包括了base64和blob
})
上传至七牛云
这一步请看这篇文章elementUI+七牛实现js图片服务器
使用方法也是一样:
import {token,action,domain} from "./qiniuToken";
这就是模块化的好处哇
后言
📍 划重点:看这里