前言
图片压缩在许多交互场景下具有广泛的应用,html5标准还没有普及之前图片压缩主要通过服务器端编程实现,而随着浏览器的快速发展对许多先进属性的支持致使前端技术也可以实现相同的功能.本篇文章以实战角度去深入研究前端技术是如何压缩图片并生成出来.
案例效果图如下,通过选择文件按钮打开本地的某张图片,并在页面上预览出来.随后选择压缩率,下方会展现压缩后的图片效果,最终点击生成下载被压缩的图片.
功能实现
html结构
updateFile方法实现
用户点击按钮上传本地图片时触发updateFile函数,file为上传图片的文件对象.
/**
* 给input绑定事件
*/
async function updateFile(e) {
const file = e.target.files[0];
if (!verify(file)) {
//参数校验
return false;
}
const base64Code = await getBase64(file); //获取base64编码
placeImg(base64Code); //放置图片
}
verify函数通过获取file对象,可以知道文件的类型和大小,从而制定一些规范来约束上传行为.
/**
* 参数校验
* @param {*} file
*/
function verify(file) {
const { size, type } = file;
if (size > 5 * 1024 * 1024) {
alert('上传图片大小不能超过5M');
return false;
}
if (!img_types.includes(type)) {
alert('请上传图片');
return false;
}
return true;
}
getBase64函数利用FileReader这个AP将图片转换成base64编码
function getBase64(file) {
return new Promise((resolve) => {
const fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
fileReader.readAsDataURL(file);
});
}
placeImg函数主要功能是计算出预览图片(原始图)合适的宽高并渲染到页面上.code为base64编码,target是准备盛放预览图的dom元素,它的宽度和高度就是图片所能允许设置的最大宽和最大高.图片对象image通过naturalWidth和naturalHeight可以获取图片原始的宽和高.先使用图片的原始宽除以原始高,得到宽高比radio.假如原始宽比高大,取最大宽max_width和图片原始宽naturalWidth较小的那个值赋值给width,width利用宽高比radio算出height.如果此时发现height仍然大于最大高,那么宽高比例还需要进一轮缩小.最终得到宽高就能保证图片大小既不会溢出外层的dom容器,又能按照宽高比不失真的展示.最后执行target.appendChild(image); 就能使设置好宽高的预览图片显示在页面上了.
/**
* 给图片设置合适的宽高放置在容器中
*/
function placeImg(code) {
const target = document.getElementById('original');
const max_width = parseInt(getComputedStyle(target).width);
const max_height = parseInt(getComputedStyle(target).height);
let width, height;
const image = new Image();
image.src = code;
image.onload = () => {
const naturalWidth = image.naturalWidth;
const naturalHeight = image.naturalHeight;
const radio = naturalWidth / naturalHeight;
if (radio >= 1) {
//宽比高大
width = naturalWidth < max_width ? naturalWidth : max_width;
height = (width * 1) / radio;
if (height > max_height) {
height = max_height;
width = height * radio;
}
} else {
height = naturalHeight < max_height ? naturalHeight : max_height;
width = height * radio;
if (width > max_width) {
width = max_width;
height = (width * 1) / radio;
}
}
width = parseInt(width);
height = parseInt(height);
image.style.width = `${width}px`;
image.style.height = `${height}px`;
target.innerHTML = '';
target.appendChild(image);
img = image; //将预览图对象赋值给全局变量img
compress();
};
}
预览图已经渲染页面上了,接下来要在预览图下面展现压缩图.compress函数实现此功能.value为select标签当前选中的值,即图片要设置的压缩比.在内存中创建一个canvas标签,将它的宽高设置成预览图对象的宽高,并将预览图绘制到该canvas上,最后通过一个关键API canvas.toDataURL就能生成图片压缩后的base64编码了,canvas.toDataURL第二参数填入的正是压缩比.压缩后的base64编码赋值给Image对象并在页面下面渲染出来,如此压缩后的图片也顺利展现了.
/**
* 压缩图片
*/
function compress() {
if (!img) { //img是预览图对象
return false;
}
const value = Number(document.getElementById('sel').value);
const canvas = document.createElement('CANVAS');
const w = img.width,
h = img.height;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, w, h);
const code = canvas.toDataURL('image/jpeg', value);
const image = new Image();
image.src = code;
image.onload = () => {
const des = document.getElementById('production');
des.innerHTML = '';
des.appendChild(image);
compress_img = image;
};
}
生成图片
compress_img为压缩后的图片对象,当用户点击生成时,generate函数会触发.它先创建一个A标签,给href属性赋值上压缩图片的base64编码,以及给download属性加一个值,该值对应的是图片下载后的名称.最后执行A标签的单击行为触发下载.
/**
* 下载图片
* @param {*}
*/
function generate() {
if (!compress_img) {
return false;
}
const a = document.createElement('A');
a.href = compress_img.src;
a.download = 'download';
a.click();
}
总结
整个案例的流程从用户上传图片开始,首先会判断上传的文件是否合乎规范,随后将图片转换成base64编码并设置合适的宽高在本地的网页上展现出来.原始图已经顺利渲染,接下来就要在下方渲染压缩图.在内存中新建一个canvas对象,将原始图渲染到canvas上,通过一个关键的API canvas.toDataURL 对图片进行压缩,该API会返回压缩后图片的base64编码,再将压缩图片也展现到页面上.最后用户点击生成时就能顺利的将压缩图下载到本地.