前端如何实现图片压缩

245 阅读2分钟

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.应用场景

  1. 图片上传:在网站或应用中,用户经常需要上传图片,但是上传大图片会导致上传时间过长,因此需要压缩图片后再上传。

  2. 相册展示:相册中的图片数量较多,如果每张图片都很大,会导致相册加载时间过长,影响用户体验。因此需要对图片进行压缩。

  3. 网页优化:网页中使用大量图片也会导致网页加载时间过长,影响用户体验。对于网页中使用的图片,可以通过canvas实现压缩,从而优化网页加载速度。

  4. 移动端应用:移动端应用中,用户的流量比较有限,因此需要对图片进行压缩,减少用户的流量消耗。

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();