最新文章
【前端技术】JS压缩图片的思路
在开发中,相信很多人都做过图片上传相关功能。然而,在图片上传过程中,文件体积问题常常被忽视。若直接上传原图片,可能因体积较大导致上传速度慢,且前端渲染时也会比较迟缓,极大地影响用户体验。因此,在不影响清晰度的前提下,前端可以在上传前对图片大小体积进行压缩,调整到合适大小后再进行上传。本文将详细介绍前端 JS 如何实现图片压缩,有需要的小伙伴赶紧收藏起来吧!
原理(必看)
简而言之:主要利用 canvas 的 drawImage 方法先将图片绘制为 canvas 图像,再通过 toDataURL 转化为 DataURL 来存储图片链接。
drawImage 简单介绍
Canvas 2D API 中的 CanvasRenderingContext2D.drawImage () 方法提供了多种在画布(Canvas)上绘制图像的方式。
用法可参考:CanvasRenderingContext2D.drawImage () - Web API 接口参考 | MDN (mozilla.org)。
语法如下:
drawImage (image, dx, dy);
drawImage (image, dx, dy, dWidth, dHeight);
drawImage (image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
我们使用第二种语法进行绘制,参数含义如下:
image:要绘制到上下文的元素。
dx:image 的左上角在目标画布上的 X 轴坐标。
dy:image 的左上角在目标画布上的 Y 轴坐标。
dWidth:image 在目标画布上绘制的宽度,可对绘制的 image 进行缩放。若不指定,绘制时 image 宽度不会缩放。
dHeight:image 在目标画布上绘制的高度,可对绘制的 image 进行缩放。若不指定,绘制时 image 高度不会缩放。
简单示例
注意:随意修改图像尺寸可能导致图像失真。我们可以先获取图像资源的原始尺寸,然后进行等比缩放。即当确定设置宽度后,高度要进行等比调整,可依据交叉相乘积相等的公式进行计算。
例如,如果宽度设置为 500,那么高度也应进行等比缩放。公式为:naturalWidth * X = naturalHeight * 500,由此可计算出高度 X = naturalHeight * 500 /naturalWidth。
以下是代码示例:
javascript
复制
var can = document.querySelector('canvas');
var context = can.getContext('2d');
var imgDom = new Image();
imgDom.src = './img.jpg';
imgDom.onload = function () {
// 注意:图像绘制时,必须保证资源已经加载完成
console.log('图片的原始宽度', imgDom.naturalWidth);
console.log('图片的原始高度', imgDom.naturalHeight);
context.drawImage(
imgDom,
0, 0,
500, imgDom.naturalHeight * 500 / imgDom.naturalWidth
);
};
toDataURL 简单介绍
将图片绘制到 canvas 后,还需将 canvas 转化为 Data URL。转化为 DataURL 后,可以在屏幕上显示,也可存储到后端服务器。使用 canvas 提供的 toDataURL 实例方法即可。
官方解释:HTMLCanvasElement.toDataURL () 方法返回一个包含图片展示的 data URI。
参考:HTMLCanvasElement.toDataURL () - Web API 接口参考 | MDN (mozilla.org)。
语法:canvas.toDataURL (type, encoderOptions);
其中,type(可选)为图片格式,默认为 image/png;encoderOptions(可选)在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。若超出取值范围,将使用默认值 0.92,其他参数会被忽略。
简单示例
javascript
复制
// 获取压缩后的图片数据
can.width = imgDom.naturalWidth;
can.height = imgDom.naturalHeight;
const compressedData = can.toDataURL('image/jpeg', 0.6); // 可调整质量参数
console.log('compressedData: ', compressedData);
转化后的 DataURL 结果如下。
实现
先给出全部代码,再进行解释。
html
复制
<!DOCTYPE html>
<html>
<head>
<title>图片压缩上传</title>
<meta charset="UTF-8">
</head>
<body>
<input type="file" id="fileInput" accept="image/*">
<button onclick="compressAndUpload()">压缩并上传图片</button>
<canvas id="canvas" style="display: none;"></canvas>
<script>
function compressAndUpload() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
alert('请先选择要上传的图片');
return;
}
const reader = new FileReader();
reader.onload = function () {
const img = new Image();
img.src = reader.result;
img.onload = function () {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const maxWidth = 800; // 设置最大宽度为 800 像素
let width = img.width;
let height = img.height;
// 判断是否需要缩放
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
// 设置 canvas 的宽高
canvas.width = width;
canvas.height = height;
// 将图片绘制到 canvas 上
ctx.drawImage(img, 0, 0, width, height);
// 获取压缩后的图片数据
const compressedData = canvas.toDataURL('image/jpeg', 0.7); // 可调整质量参数
// 创建一个新的压缩后的 File 对象
const compressedFile = dataURItoBlob(compressedData, file.type);
compressedFile.lastModifiedDate = file.lastModifiedDate;
compressedFile.name = file.name;
// 上传压缩后的图片文件
uploadImage(compressedFile);
};
};
reader.readAsDataURL(file);
}
function dataURItoBlob(dataURI, mimeType) {
const binary = atob(dataURI.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: mimeType });
}
function uploadImage(compressedFile) {
const formData = new FormData();
formData.append('image', compressedFile);
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => {
if (response.ok) {
console.log('图片上传成功');
} else {
console.error('图片上传失败');
}
})
.catch(error => {
console.error('发生错误:', error);
});
}
</script>
</body>
</html>
下面分析关键代码部分。
首先,初始化一个 reader,它是 FileReader 对象的实例。reader.readAsDataURL(file)这行代码将选择的文件读取为 Data URI 格式的字符串。执行这行代码时,FileReader 对象会异步读取文件数据,读取完成后触发 onload 事件,读取结果存储在 FileReader 对象的 result 属性中,格式为 Data URI 字符串。
接着,将读取出来的 Data URI 字符串赋值给 Image 的 src,等待 img 加载完毕后开始对 img 进行压缩,压缩方法如上文所示。
然后,设置最大宽度为 800,判断当前图片宽度是否大于该值,若大于则进行缩放计算,小于则不进行等比缩放计算。最后将计算出的值使用 drawImage 绘制到 canvas 上面。
现在,将 canvas 转化成 Data URI 字符串,canvas.toDataURL('image/jpeg', 0.7)这行代码将 canvas 上绘制的图像数据导出为 JPEG 格式的 Data URI 字符串,并设置图像质量为 0.7。
之后,创建一个新的压缩后的 File 对象,通过将 Data URI 字符串转化为 Blob 对象来实现。
最后,使用 FormData 进行上传即可。
压缩前后体积对比
我们看一下压缩前后体积对比,压缩前为 550290,压缩后为 31523,缩小了十几倍,压缩效果非常明显。
总结
前端实现图片压缩主要借助 canvas 来完成。实现思路是先使用 canvas 的 drawImage 方法将图片绘制为 canvas 图像,再结合 toDataURL 转化为 DataURL 进行存储图片链接以及压缩图像质量。在 toDataURL 中可以调整图像质量,同时,在压缩图像时要注意等宽高缩放,否则会导致图像失真。