图片上传是前端中常见的的业务场景,但是一旦图片过大就会导致不好的用户体验。适当的对图片进行压缩处理, 可以显著的提升用户体验。
压缩思路
涉及到 JS 的图片压缩,我的想法是需要用到 Canvas 的绘图能力,通过调整图片的分辨率或者绘图质量来达到图片压缩的效果,实现思路如下
- 获取上传 Input 中的图片对象 File
- 将图片转换成 base64 格式
- base64 编码的图片通过 Canvas 转换压缩,这里会用到的 Canvas 的 drawImage(调节图片的分辨率) 以及 toDataURL(调节图片压缩质量并且输出) 这两个 Api,下面会有详细介绍
- 转换后的图片生成对应的新图片,然后输出图片对象
优缺点介绍
Canvas 压缩的方式也有着自己的优缺点:
- 优点:实现简单,参数可以配置化,自定义图片的尺寸,指定区域裁剪等等。
- 缺点:只有 jpeg 、webp 支持原图尺寸下图片质量的调整来达到压缩图片的效果,其他图片格式,仅能通过调节尺寸来实现
压缩效果
代码实现
<template>
<div class="container">
<input type="file" id="input-img" @change="compress" />
<a :download="fileName" :href="compressImg" >下载</a>
<div>
<div>压缩前图片的大小:{{compressImageInfo.beforeKB}}</div>
<div>压缩后图片的大小:{{compressImageInfo.afterKB}}</div>
<img referrerPolicy="no-referrer" referrerPolicy="no-referrer" referrerPolicy="no-referrer" referrerPolicy="no-referrer" :src="compressImg" />
</div>
</div>
</template>
<script>
export default {
name: 'compress',
data() {
return {
compressImg: null,
fileName: null,
compressImageInfo: {},
};
},
methods: {
compress() {
// 获取文件对象
const fileObj = document.querySelector('#input-img').files[0];
// 获取文件名称,后续下载重命名
this.fileName = `${new Date().getTime()}-${fileObj.name}`;
// 压缩图片
this.compressImage(fileObj, 0.2, 100, 100).then((res) => {
console.log(res);
this.compressImageInfo = res;
this.compressImg = window.URL.createObjectURL(res.file);
});
},
/**
* 压缩方法
* @param {string} file 文件
* @param {Number} quality 0~1之间
* @param {Number} compressImageWidth 压缩后图片的宽度,默认原图片的宽度
* @param {Number} compressImageHeight 压缩后图片的高度,默认原图片的高度
* @param {string} compressImageType 压缩后图片的类型,默认原图片的类型 png、jpeg...
*/
compressImage(file, quality, compressImageWidth, compressImageHeight, compressImageType) {
console.log('file', file);
return new Promise((resolve) => {
const reader = new FileReader(); // 创建 FileReader
reader.onload = ({
target: {
result: src,
},
}) => {
const image = new Image(); // 创建 img 元素
image.onload = async () => {
const canvas = document.createElement('canvas'); // 创建 canvas 元素
const imageType = compressImageType ? `image/${compressImageType}` : file.type; // 压缩后图片类型
const imageWidth = compressImageWidth || image.width;
const imageHeight = compressImageHeight || image.height;
canvas.width = imageWidth;
canvas.height = imageHeight;
canvas.getContext('2d').drawImage(image, 0, 0, imageWidth, imageHeight); // 绘制 canvas
// 调用 `canvas` 的 `toDataURL` 方法可以输出 `base64` 格式的图片。
const canvasURL = canvas.toDataURL(imageType, quality);
// 将 canvas 生成的 base64 数据拆分后,通过 `atob` 方法解码
const buffer = atob(canvasURL.split(',')[1]);
let length = buffer.length;
const bufferArray = new Uint8Array(new ArrayBuffer(length));
while (length--) {
bufferArray[length] = buffer.charCodeAt(length);
}
const miniFile = new File([bufferArray], file.name, {
type: imageType,
});
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2)),
});
};
image.src = src;
};
reader.readAsDataURL(file);
});
},
},
};
</script>
Input 上传 File 处理
将 File 对象通过 FileReader 的 readAsDataURL 方法转换为URL格式的字符串(base64 编码)
const fileObj = document.querySelector('#input-img').files[0];
const reader = new FileReader();
// 读取文件
reader.readAsDataURL(fileObj);
Canvas 处理 File 对象
建立一个 Image 对象,一个 canvas 画布,设定自己想要下载的图片尺寸,调用 drawImage 方法在 canvas 中绘制上传的图片
const image = new Image(); // 创建 img 元素
const canvas = document.createElement('canvas'); // 创建 canvas 元素
...
canvas.getContext('2d').drawImage(image, 0, 0, imageWidth, imageHeight); // 绘制 canvas
drawImage
context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
img:图片对象,可以是页面上获取的 DOM 对象,也可以是虚拟 DOM 中的图片对象。
dx、dy、dWidth、dHeight:表示在 canvas 画布上规划出一片区域用来放置图片,dx, dy 为绘图位置在 Canvas 元素的 X 轴、Y 轴坐标,dWidth, dHeight 指在 Canvas 元素上绘制图像的宽度和高度(如果不说明, 在绘制时图片的宽度和高度不会缩放)。
sx、sy、swidth、sheight:这 4 个参数是用来裁剪源图片的,表示图片在 canvas 画布上显示的大小和位置。sx, sy 表示在源图片上裁剪位置的 X 轴、Y 轴坐标,然后以 swidth, sheight 尺寸来选择一个区域范围,裁剪出来的图片作为最终在 Canvas 上显示的图片内容( swidth, sheight 不说明的情况下,整个矩形(裁剪)从坐标的 sx 和 sy 开始,到图片的右下角结束)。
Canvas 输出图片
调用 canvas 的 toDataURL 方法可以输出 base64 格式的图片。
canvas.toDataURL(`image/${type}`);
toDataURL
canvas.toDataURL(type, encoderOptions);
type:指定转换为base64编码后图片的格式,如:image/png、image/jpeg、image/webp等等,默认为image/png格式。
encoderOptions:可选参数。用于设置转换为base64编码后图片的质量,取值范围为0-1,超出取值范围用默认值0.92代替。
a 标签的下载
调用 <a> 标签的 download 属性,即可完成图片的下载。
// href 下载必填
<a download="filename" href="href"> 下载 </a>
filename:指定文件名称。
href:文件的下载地址。