Files
Nano-Banana-AI-Image-Editor/src/utils/imageUtils.ts
2025-09-21 14:43:59 +08:00

146 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export function base64ToBlob(base64: string, mimeType: string = 'image/png'): Blob {
const byteCharacters = atob(base64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
}
export function blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result as string;
const base64 = result.split(',')[1]; // Remove data:image/png;base64, prefix
resolve(base64);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
// 将URL转换为Blob
export async function urlToBlob(url: string): Promise<Blob> {
const response = await fetch(url);
return await response.blob();
}
export function createImageFromBase64(base64: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = `data:image/png;base64,${base64}`;
});
}
export function resizeImageToFit(
image: HTMLImageElement,
maxWidth: number,
maxHeight: number
): { width: number; height: number } {
const ratio = Math.min(maxWidth / image.width, maxHeight / image.height);
return {
width: image.width * ratio,
height: image.height * ratio
};
}
export function generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
export function downloadImage(imageData: string, filename: string): void {
if (imageData.startsWith('blob:')) {
// 对于Blob URL我们需要获取实际的Blob数据
fetch(imageData)
.then(response => response.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
} else if (imageData.startsWith('data:')) {
// 对于数据URL直接下载
const a = document.createElement('a');
a.href = imageData;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} else {
// 对于其他URL获取并转换为blob
fetch(imageData)
.then(response => response.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
}
}
// 优化的图像压缩函数
export async function compressImage(blob: Blob, quality: number = 0.8): Promise<Blob> {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('无法获取canvas上下文'));
return;
}
const img = new Image();
img.onload = () => {
// 设置canvas尺寸
canvas.width = img.width;
canvas.height = img.height;
// 绘制图像
ctx.drawImage(img, 0, 0);
// 转换为Blob
canvas.toBlob(
(compressedBlob) => {
if (compressedBlob) {
resolve(compressedBlob);
} else {
reject(new Error('图像压缩失败'));
}
},
'image/jpeg',
quality
);
};
img.onerror = reject;
// 将Blob转换为URL以便加载到图像中
const url = URL.createObjectURL(blob);
img.src = url;
// 清理URL
img.onload = () => {
URL.revokeObjectURL(url);
// 调用原始的onload处理程序
if (img.onload) {
(img.onload as (() => void)).call(img);
}
};
});
}