Skip to content

🎨 Canvas实现智能图像压缩与尺寸优化 ​

📝 需求背景 ​

在前端开发中,经常需要处理用户上传的图片。过大的图片会影响加载速度和用户体验,因此需要在保证图片质量的同时进行合理压缩。

🔧 核心实现 ​

完整代码实现 ​

js
/**
 * 图片压缩函数
 * @param {File[]} imgFiles - 待压缩的图片文件数组
 * @returns {Promise} 返回压缩后的图片数据
 */
const compressImg = (imgFiles) => {
    return new Promise((resolve, reject) => {
        // 存储压缩后的图片和原始文件信息
        const compressedImages = [];
        const fileData = [];
        let compressedCount = 0;

        imgFiles.forEach((imgFile) => {
            // 创建文件读取器
            const reader = new FileReader();

            reader.onload = function (event) {
                // 创建图片对象
                const img = new Image();
                img.src = event.target.result;

                img.onload = function () {
                    // 创建Canvas环境
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    // 设置最大尺寸阈值(像素)
                    const maxThreshold = 2048;

                    // 初始化画布尺寸
                    canvas.width = img.width;
                    canvas.height = img.height;

                    // 智能调整尺寸,保持宽高比
                    if (Math.max(img.width, img.height) > maxThreshold) {
                        const aspectRatio = img.width / img.height;
                        if (img.width > img.height) {
                            canvas.width = maxThreshold;
                            canvas.height = maxThreshold / aspectRatio;
                        } else {
                            canvas.height = maxThreshold;
                            canvas.width = maxThreshold * aspectRatio;
                        }
                    }

                    // 在画布上绘制调整后的图片
                    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

                    // 将画布内容转换为Blob
                    canvas.toBlob(
                        function (blob) {
                            compressedImages.push(blob);
                            fileData.push({ 
                                fileName: imgFile.name, 
                                data: reader.result 
                            });
                            compressedCount++;

                            // 所有图片处理完成后返回结果
                            if (compressedCount === imgFiles.length) {
                                resolve({ compressedImages, fileData });
                            }
                        },
                        'image/jpeg',  // 输出格式
                        0.8  // 压缩质量(0-1)
                    );
                };
            };

            // 错误处理
            reader.onerror = function (error) {
                reject(error);
            };

            // 以DataURL形式读取文件
            reader.readAsDataURL(imgFile);
        });
    });
};

🔍 核心原理解析 ​

1. 图片加载流程 ​

  1. 使用FileReader读取文件
  2. 创建Image对象加载图片
  3. 使用Canvas进行处理

2. 智能压缩策略 ​

  • 📏 尺寸控制:

    • 设置最大阈值(2048px)
    • 等比例缩放,保持图片比例
    • 智能判断长边进行适配
  • 🎯 质量控制:

    • 使用toBlob方法压缩
    • 质量参数设为0.8(可调整)
    • 输出格式为JPEG(体积更小)

3. 关键技术点 ​

  • ⚡ 异步处理:使用Promise处理多图片压缩
  • 🔄 等比缩放:保持宽高比例不变
  • 📊 批量处理:支持多图片同时压缩

📈 使用示例 ​

js
// 单图片压缩
const handleSingleImage = async (file) => {
    try {
        const result = await compressImg([file]);
        console.log('压缩后的图片:', result.compressedImages[0]);
    } catch (error) {
        console.error('压缩失败:', error);
    }
};

// 多图片压缩
const handleMultipleImages = async (files) => {
    try {
        const result = await compressImg(Array.from(files));
        console.log('压缩后的图片组:', result.compressedImages);
    } catch (error) {
        console.error('压缩失败:', error);
    }
};

💡 优化建议 ​

  1. 内存优化

    • 及时释放Canvas对象
    • 处理大量图片时考虑分批处理
  2. 性能优化

    • 可以添加Worker处理大量图片
    • 考虑使用createImageBitmap优化性能
  3. 体验优化

    • 添加压缩进度提示
    • 提供压缩质量参数配置选项

🎯 应用场景 ​

  • 📱 移动端图片上传
  • 🖼️ 图片预览优化
  • 📂 相册批量处理
  • 🚀 前端性能优化

📚 参考资料 ​