1.前言
随着互联网技术的不断发展,图片已经成为了网页设计中不可或缺的一部分。但是,图片的体积往往是比较大的,这会导致网页加载速度变慢,影响用户的使用体验。因此,对于前端开发人员来说,实现图片压缩是一项非常重要的任务。本文将介绍如何通过canvas实现图片压缩,希望对大家有所帮助。
2.前端如何实现图片压缩?
前端实现图片压缩的核心原理,是利用canvas的API来实现的。
2.1 获取源图片数据
const oImgFileSelector = document.querySelector("#imgFileSelector");
const oOriginImgPreview = document.querySelector("#originImgPreview");
const oCompressedImgPreview = document.querySelector("#compressedImgPreview");
// 获取reader实例
const reader = new FileReader();
// 保存图片对象
let imgFile = null;
// 图片压缩质量
let quality = 90;
// 保存压缩后的图片base64地址
let compressedImgSrc = '';
// 添加事件监听函数
oImgFileSelector.addEventListener('change', handleFileSelectorChange, false);
function handleFileSelectorChange(e){
imgFile = e.target.files[0];
if(!imgFile || !IMG_TYPES[imgFile.type]){
alert('请上传正确的图片文件');
setImgFileEmpty();
return;
}
// 设置图片预览
setImgPreview(imgFile);
}
2.2 读取图片数据
function setImgPreview(imgFile){
if(imgFile instanceof File){
// 读取文件
reader.readAsDataURL(imgFile);
// 读取文件load函数
reader.onload = async ()=>{
const originImgSrc = reader.result;
compressedImgSrc = await createCompressedImage({
imgSrc: originImgSrc,
quality,
type: imgFile.type
})
// 设置原图像src地址
oOriginImgPreview.src = originImgSrc;
// 设置压缩后图像src地址
oCompressedImgPreview.src = compressedImgSrc;
// 显示原图像
setPreviewVisible(oOriginImgPreview, true);
// 显示压缩后的图像
setPreviewVisible(oCompressedImgPreview, true);
console.log('===处理前后的结果===>', `原图像大小=${originImgSrc.length} | 压缩图像大小=${compressedImgSrc.length} | 压缩质量=${quality}`);
}
//reader.readAsDataURL(imgFile);
}
}
2.3 创建canvas画布并写入图片
function createCompressedImage({
imgSrc,
quality,
type
}){
const oCan = document.createElement('canvas');
const oImg = document.createElement('img');
const ctx = oCan.getContext('2d');
oImg.src = imgSrc;
return new Promise((resolve)=>{
oImg.onload = ()=>{
const imgWidth = oImg.width;
const imgHeight = oImg.height;
oCan.width = imgWidth;
oCan.height = imgHeight;
ctx.drawImage(oImg, 0, 0, imgWidth, imgHeight);
// 调用压缩函数
doCompress(oCan, imgSrc, type);
// 传递结果
resolve(compressedImgSrc);
}
})
}
2.4 压缩图片获取结果
/**
* @desc 如果压缩后的大小大于压缩前,递归调用继续执行压缩逻辑
*/
function doCompress(canvas,imgSrc, type){
compressedImgSrc = canvas.toDataURL(type, quality / 100);
if(compressedImgSrc.length >= imgSrc.length && quality > 10){
quality -= 10;
doCompress(canvas, imgSrc, type);
}
}
3.应用场景
-
图片上传:在网站或应用中,用户经常需要上传图片,但是上传大图片会导致上传时间过长,因此需要压缩图片后再上传。
-
相册展示:相册中的图片数量较多,如果每张图片都很大,会导致相册加载时间过长,影响用户体验。因此需要对图片进行压缩。
-
网页优化:网页中使用大量图片也会导致网页加载时间过长,影响用户体验。对于网页中使用的图片,可以通过canvas实现压缩,从而优化网页加载速度。
-
移动端应用:移动端应用中,用户的流量比较有限,因此需要对图片进行压缩,减少用户的流量消耗。
4.图片压缩源码
const oImgFileSelector = document.querySelector("#imgFileSelector");
const oOriginImgPreview = document.querySelector("#originImgPreview");
const oCompressedImgPreview = document.querySelector("#compressedImgPreview");
// 获取reader实例
const reader = new FileReader();
// 保存图片对象
let imgFile = null;
// 图片压缩质量
let quality = 90;
// 保存压缩后的图片base64地址
let compressedImgSrc = '';
// 图片类型枚举
const IMG_TYPES = {
'image/jpeg': 'image/jpeg',
'image/png': 'image/png',
'image/gif': 'image/gif'
}
/**
* @desc 初始化函数
*/
const init = ()=>{
bindEvent();
}
/**
* @desc 绑定事件函数
*/
const bindEvent = ()=>{
oImgFileSelector.addEventListener('change', handleFileSelectorChange, false);
}
/**
* @desc 图片上传处理事件
*/
function handleFileSelectorChange(e){
imgFile = e.target.files[0];
if(!imgFile || !IMG_TYPES[imgFile.type]){
alert('请上传正确的图片文件');
setImgFileEmpty();
return;
}
// 设置图片预览
setImgPreview(imgFile);
}
/**
* @desc 清空file对象
*/
function setImgFileEmpty(){
oImgFileSelector.value = '';
imgFile = null;
setPreviewVisible(oOriginImgPreview,false);
setPreviewVisible(oCompressedImgPreview, false);
}
/**
* @desc 设置图片预览函数
*/
function setImgPreview(imgFile){
if(imgFile instanceof File){
// 读取文件
reader.readAsDataURL(imgFile);
// 读取文件load函数
reader.onload = async ()=>{
const originImgSrc = reader.result;
compressedImgSrc = await createCompressedImage({
imgSrc: originImgSrc,
quality,
type: imgFile.type
})
// 设置原图像src地址
oOriginImgPreview.src = originImgSrc;
// 设置压缩后图像src地址
oCompressedImgPreview.src = compressedImgSrc;
// 显示原图像
setPreviewVisible(oOriginImgPreview, true);
// 显示压缩后的图像
setPreviewVisible(oCompressedImgPreview, true);
console.log('===处理前后的结果===>', `原图像大小=${originImgSrc.length} | 压缩图像大小=${compressedImgSrc.length} | 压缩质量=${quality}`);
}
//reader.readAsDataURL(imgFile);
}
}
/**
* @desc 图片压缩处理函数
*/
function createCompressedImage({
imgSrc,
quality,
type
}){
const oCan = document.createElement('canvas');
const oImg = document.createElement('img');
const ctx = oCan.getContext('2d');
oImg.src = imgSrc;
return new Promise((resolve)=>{
oImg.onload = ()=>{
const imgWidth = oImg.width;
const imgHeight = oImg.height;
oCan.width = imgWidth;
oCan.height = imgHeight;
ctx.drawImage(oImg, 0, 0, imgWidth, imgHeight);
// 调用压缩函数
doCompress(oCan, imgSrc, type);
// 传递结果
resolve(compressedImgSrc);
}
})
}
/**
* @desc 压缩函数
*/
function doCompress(canvas,imgSrc, type){
compressedImgSrc = canvas.toDataURL(type, quality / 100);
if(compressedImgSrc.length >= imgSrc.length && quality > 10){
quality -= 10;
doCompress(canvas, imgSrc, type);
}
}
/**
* @desc 设置图片是否展示
*/
function setPreviewVisible(node, visible){
switch(visible){
case true:
node.classList.remove('hide');
node.classList.add('show');
break;
case false:
node.classList.remove('show');
node.classList.add('hide');
break;
default:
break;
}
}
init();