mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-03 08:10:51 +08:00
128 lines
3.2 KiB
TypeScript
128 lines
3.2 KiB
TypeScript
import { ElMessage } from 'element-plus';
|
||
|
||
export interface CompressionLevel {
|
||
maxWidth: number;
|
||
maxHeight: number;
|
||
quality: number;
|
||
}
|
||
|
||
export const DEFAULT_COMPRESSION_LEVELS: CompressionLevel[] = [
|
||
{ maxWidth: 800, maxHeight: 800, quality: 0.6 },
|
||
{ maxWidth: 600, maxHeight: 600, quality: 0.5 },
|
||
{ maxWidth: 400, maxHeight: 400, quality: 0.4 },
|
||
];
|
||
|
||
/**
|
||
* 压缩图片
|
||
* @param file - 要压缩的图片文件
|
||
* @param maxWidth - 最大宽度
|
||
* @param maxHeight - 最大高度
|
||
* @param quality - 压缩质量 (0-1)
|
||
* @returns 压缩后的 Blob
|
||
*/
|
||
export function compressImage(
|
||
file: File,
|
||
maxWidth = 1024,
|
||
maxHeight = 1024,
|
||
quality = 0.8,
|
||
): Promise<Blob> {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
const img = new Image();
|
||
img.onload = () => {
|
||
const canvas = document.createElement('canvas');
|
||
let width = img.width;
|
||
let height = img.height;
|
||
|
||
if (width > maxWidth || height > maxHeight) {
|
||
const ratio = Math.min(maxWidth / width, maxHeight / height);
|
||
width = width * ratio;
|
||
height = height * ratio;
|
||
}
|
||
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
|
||
const ctx = canvas.getContext('2d')!;
|
||
ctx.drawImage(img, 0, 0, width, height);
|
||
|
||
canvas.toBlob(
|
||
(blob) => {
|
||
if (blob) {
|
||
resolve(blob);
|
||
}
|
||
else {
|
||
reject(new Error('压缩失败'));
|
||
}
|
||
},
|
||
file.type,
|
||
quality,
|
||
);
|
||
};
|
||
img.onerror = reject;
|
||
img.src = e.target?.result as string;
|
||
};
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 将 Blob 转换为 base64
|
||
* @param blob - Blob 对象
|
||
* @returns base64 字符串
|
||
*/
|
||
export function blobToBase64(blob: Blob): Promise<string> {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = () => {
|
||
resolve(reader.result as string);
|
||
};
|
||
reader.onerror = reject;
|
||
reader.readAsDataURL(blob);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 尝试多级压缩直到满足大小限制
|
||
* @param file - 图片文件
|
||
* @param currentTotalLength - 当前已使用的总长度
|
||
* @param maxTotalLength - 最大允许总长度
|
||
* @returns 压缩结果或 null(如果无法满足限制)
|
||
*/
|
||
export async function tryCompressToLimit(
|
||
file: File,
|
||
currentTotalLength: number,
|
||
maxTotalLength: number,
|
||
compressionLevels = DEFAULT_COMPRESSION_LEVELS,
|
||
): Promise<{ blob: Blob; base64: string; estimatedLength: number } | null> {
|
||
for (const level of compressionLevels) {
|
||
const compressedBlob = await compressImage(
|
||
file,
|
||
level.maxWidth,
|
||
level.maxHeight,
|
||
level.quality,
|
||
);
|
||
const base64 = await blobToBase64(compressedBlob);
|
||
const estimatedLength = Math.floor(base64.length * 0.5);
|
||
|
||
if (currentTotalLength + estimatedLength <= maxTotalLength) {
|
||
return { blob: compressedBlob, base64, estimatedLength };
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Composable: 使用图片压缩
|
||
*/
|
||
export function useImageCompression() {
|
||
return {
|
||
compressImage,
|
||
blobToBase64,
|
||
tryCompressToLimit,
|
||
DEFAULT_COMPRESSION_LEVELS,
|
||
};
|
||
}
|