说起图片压缩,大家想到的或者平时用到的很多工具都可以实现,例如,客户端类的有图片压缩工具 PPDuck3, JS 实现类的有插件 compression.js ,亦或是在线处理类的 OSS 上传,文件上传后,在访问文件时中也有图片的压缩配置选项,不过,能不能自己撸一套 JS 实现的图片压缩代码呢?当然可以,那我们先来理一下思路。
压缩思路
涉及到 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>
<button @click="downloadImg">兼容 IE 下载</button>
<div>
<img :src="compressImg" />
</div>
</div>
</template>
<script>
export default {
name: 'compress',
data: function() {
return {
compressImg: null,
fileName: null,
};
},
components: {},
methods: {
compress() {
// 获取文件对象
const fileObj = document.querySelector('#input-img').files[0];
// 获取文件名称,后续下载重命名
this.fileName = `${new Date().getTime()}-${fileObj.name}`;
// 获取文件后缀名
const fileNames = fileObj.name.split('.');
const type = fileNames[fileNames.length-1];
// 压缩图片
this.handleCompressImage(fileObj, type);
},
handleCompressImage(img, type) {
const vm = this;
let reader = new FileReader();
// 读取文件
reader.readAsDataURL(img);
reader.onload = function(e) {
let image = new Image(); //新建一个img标签
image.src = e.target.result;
image.onload = function() {
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
// 定义 canvas 大小,也就是压缩后下载的图片大小
let imageWidth = image.width; //压缩后图片的大小
let imageHeight = image.height;
canvas.width = imageWidth;
canvas.height = imageHeight;
// 图片不压缩,全部加载展示
context.drawImage(image, 0, 0);
// 图片按压缩尺寸载入
// let imageWidth = 500; //压缩后图片的大小
// let imageHeight = 200;
// context.drawImage(image, 0, 0, 500, 200);
// 图片去截取指定位置载入
// context.drawImage(image,100, 100, 100, 100, 0, 0, imageWidth, imageHeight);
vm.compressImg = canvas.toDataURL(`image/${type}`);
};
};
},
// base64 图片转 blob 后下载
downloadImg() {
let parts = this.compressImg.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for(let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
const blob = new Blob([uInt8Array], {type: contentType});
this.compressImg = URL.createObjectURL(blob);
if (window.navigator.msSaveOrOpenBlob) {
// 兼容 ie 的下载方式
window.navigator.msSaveOrOpenBlob(blob, this.fileName);
}else{
const a = document.createElement('a');
a.href = this.compressImg;
a.setAttribute('download', this.fileName);
a.click();
}
},
}
};
</script>