基于 Canvas 的图片智能压缩策略
在 Web 开发中,图片压缩是优化页面性能、减少带宽消耗的核心技术之一。本文将深入解析一个基于 Canvas 的图片智能压缩策略,探讨其设计思路、压缩方式及优化方向。
1. 概述
接收 Base64 格式的图片数据,通过动态计算压缩参数(缩放比例 scale
和 JPEG 质量 quality
),生成符合目标尺寸和文件大小要求的压缩图片。函数支持:
- 智能跳过压缩:对小文件(<1MB)直接返回原图。
- 动态参数调整:根据原始尺寸和文件大小差异,自适应选择压缩强度。
- 宽高比保持:确保压缩后图片不变形。
- 结果格式化:支持返回单图或批量处理结果。
2. 核心逻辑流程
2.1 输入与初始化
ini
function compressImageToTarget(imageBase64, list = null, imgId = '', imgType = '', options = {}) {
const NO_COMPRESS_FILE_SIZE = 1024; // 1MB 阈值
const RULE_MIN_WIDTH = 1600; // 最大宽度限制
const FILE_SIZE_DIFF = 300; // 文件大小差异阈值(KB)
return new Promise((resolve) => {
// 计算文件大小(KB)
const fileSizeKb = options.fileSize ? options.fileSize / 1024 : imageBase64.length / 1024;
// 小文件跳过压缩
if (fileSizeKb < NO_COMPRESS_FILE_SIZE) {
return resolve(handleResult(imageBase64, false));
}
// 加载图片
const img = new Image();
img.onload = processImageLoad;
img.onerror = handleImageError;
img.src = imageBase64;
});
}
-
关键点:
- 通过
options.fileSize
允许外部传入精确文件大小(避免 Base64 长度计算误差)。 - 使用 Promise 封装异步操作,便于调用方处理结果。
- 通过
2.2 动态压缩参数计算
ini
function calculateCompressionParams(img, fileSizeKb) {
const sizeDifference = fileSizeKb - NO_COMPRESS_FILE_SIZE;
let scale, quality;
if (sizeDifference < FILE_SIZE_DIFF) {
// 差异 < 300KB:保守压缩
scale = 0.85;
quality = img.width < RULE_MIN_WIDTH ? 0.9 : 0.8;
} else {
// 差异 ≥ 300KB:激进压缩
scale = 0.7;
quality = img.width < RULE_MIN_WIDTH ? 0.85 : 0.7;
}
return { scale, quality };
}
-
策略说明:
-
缩放比例 (
scale
) :- 小差异(接近 1MB):缩放 85%,保留较多细节。
- 大差异(远超 1MB):缩放 70%,显著减少像素数量。
-
JPEG 质量 (
quality
) :- 小图(宽度 < 1600px):使用较高质量(0.9/0.85),避免模糊。
- 大图(宽度 ≥ 1600px):降低质量(0.8/0.7),优先减小文件体积。
-
2.3 目标尺寸计算
arduino
function calculateTargetSize(img, scale) {
let width = img.width;
let height = img.height;
// 计算最大允许尺寸
const maxWidth = Math.min(width * scale, RULE_MIN_WIDTH);
const maxHeight = Math.min(height * scale, RULE_MIN_WIDTH / (width / height));
// 保持宽高比调整
if (width > height) { // 横版图片
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
} else { // 竖版图片
if (height > maxHeight) {
width *= maxHeight / height;
height = maxHeight;
}
}
return { width: Math.round(width), height: Math.round(height) };
}
-
关键逻辑:
- 横版图片:以宽度为基准缩放,高度按比例调整。
- 竖版图片:以高度为基准缩放,宽度按比例调整。
- 最大宽度限制:确保压缩后宽度不超过 1600px,避免生成过大的图片。
2.4 执行压缩与结果处理
ini
function compressImage(img, width, height, quality) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
return canvas.toDataURL('image/jpeg', quality);
}
function handleResult(imageData, isCompressed) {
const resultItem = {
img: imageData,
imgId: String(imgId),
imgType
};
return Array.isArray(list) ? list.push(resultItem) : imageData;
}
- Canvas 压缩 :
通过drawImage
缩放图片,再使用toDataURL
输出 JPEG 格式,quality
参数直接控制文件大小。 - 结果格式化 :
支持单图返回或批量处理(通过list
参数累积结果)。
3. 压缩效果分析
3.1 典型场景测试
原始图片 | 文件大小 | 尺寸 | 压缩参数 | 压缩后大小 |
---|---|---|---|---|
小图(800x600) | 800KB | 无需压缩 | 跳过 | 800KB |
中图(2000x1500) | 1.2MB | scale=0.85 |
quality=0.8 |
~700KB |
大图(3000x2000) | 2.5MB | scale=0.7 |
quality=0.7 |
~500KB |