feat: 在图片标注组件中添加处理后图片并排显示和下载功能,支持抠图和变清晰
This commit is contained in:
parent
c68c1a1964
commit
9719570897
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef, useState, useEffect, useCallback } from "react";
|
||||
import { X, Pencil, Circle, ArrowRight, Type, Check, Undo, Trash2, Scissors, Sparkles } from "lucide-react";
|
||||
import { X, Pencil, Circle, ArrowRight, Type, Check, Undo, Trash2, Scissors, Sparkles, Download } from "lucide-react";
|
||||
import { Button } from "./ui/button";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@ -59,10 +59,18 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isRemovingBg, setIsRemovingBg] = useState(false);
|
||||
const [isEnhancing, setIsEnhancing] = useState(false);
|
||||
const [originalImageUrl, setOriginalImageUrl] = useState<string>(imageUrl);
|
||||
const [processedImageUrl, setProcessedImageUrl] = useState<string | null>(null);
|
||||
const [processedImageBase64, setProcessedImageBase64] = useState<string | null>(null);
|
||||
const [processType, setProcessType] = useState<"rmbg" | "enhance" | null>(null);
|
||||
|
||||
// 加载图片
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
setOriginalImageUrl(imageUrl);
|
||||
setProcessedImageUrl(null);
|
||||
setProcessedImageBase64(null);
|
||||
setProcessType(null);
|
||||
const img = new window.Image();
|
||||
img.crossOrigin = "anonymous";
|
||||
img.onload = () => {
|
||||
@ -344,28 +352,16 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.image_base64) {
|
||||
// 将返回的Base64图片加载为新图片
|
||||
const processedImage = new window.Image();
|
||||
processedImage.crossOrigin = "anonymous";
|
||||
|
||||
processedImage.onload = () => {
|
||||
// 更新图片和画布
|
||||
setImage(processedImage);
|
||||
// 清空所有标注(因为背景已移除)
|
||||
setActions([]);
|
||||
toast.success(`抠图完成!图片已保存到相册。处理时间: ${result.processing_time?.toFixed(2) || "未知"}秒`);
|
||||
};
|
||||
|
||||
processedImage.onerror = () => {
|
||||
throw new Error("加载处理后的图片失败");
|
||||
};
|
||||
|
||||
// 优先使用S3 URL(如果可用),否则使用Base64
|
||||
// 保存处理后的图片URL和Base64
|
||||
if (result.original_url) {
|
||||
processedImage.src = result.original_url;
|
||||
setProcessedImageUrl(result.original_url);
|
||||
} else {
|
||||
processedImage.src = `data:image/png;base64,${result.image_base64}`;
|
||||
setProcessedImageUrl(null);
|
||||
}
|
||||
setProcessedImageBase64(result.image_base64);
|
||||
setProcessType("rmbg");
|
||||
|
||||
toast.success(`抠图完成!图片已保存到相册。处理时间: ${result.processing_time?.toFixed(2) || "未知"}秒`);
|
||||
} else {
|
||||
throw new Error(result.error || "抠图处理失败");
|
||||
}
|
||||
@ -410,28 +406,16 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.image_base64) {
|
||||
// 将返回的Base64图片加载为新图片
|
||||
const processedImage = new window.Image();
|
||||
processedImage.crossOrigin = "anonymous";
|
||||
|
||||
processedImage.onload = () => {
|
||||
// 更新图片和画布
|
||||
setImage(processedImage);
|
||||
// 清空所有标注(因为图片已更新)
|
||||
setActions([]);
|
||||
toast.success(`变清晰完成!图片已保存到相册。处理时间: ${result.processing_time?.toFixed(2) || "未知"}秒`);
|
||||
};
|
||||
|
||||
processedImage.onerror = () => {
|
||||
throw new Error("加载处理后的图片失败");
|
||||
};
|
||||
|
||||
// 优先使用S3 URL(如果可用),否则使用Base64
|
||||
// 保存处理后的图片URL和Base64
|
||||
if (result.original_url) {
|
||||
processedImage.src = result.original_url;
|
||||
setProcessedImageUrl(result.original_url);
|
||||
} else {
|
||||
processedImage.src = `data:image/jpeg;base64,${result.image_base64}`;
|
||||
setProcessedImageUrl(null);
|
||||
}
|
||||
setProcessedImageBase64(result.image_base64);
|
||||
setProcessType("enhance");
|
||||
|
||||
toast.success(`变清晰完成!图片已保存到相册。处理时间: ${result.processing_time?.toFixed(2) || "未知"}秒`);
|
||||
} else {
|
||||
throw new Error(result.error || "变清晰处理失败");
|
||||
}
|
||||
@ -443,6 +427,33 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
}
|
||||
};
|
||||
|
||||
// 下载处理后的图片
|
||||
const handleDownloadProcessed = () => {
|
||||
if (!processedImageBase64) return;
|
||||
|
||||
try {
|
||||
// 确定文件类型和MIME类型
|
||||
const isPng = processType === "rmbg";
|
||||
const mimeType = isPng ? "image/png" : "image/jpeg";
|
||||
const fileExtension = isPng ? "png" : "jpg";
|
||||
const fileName = processType === "rmbg" ? `抠图-${Date.now()}.${fileExtension}` : `变清晰-${Date.now()}.${fileExtension}`;
|
||||
|
||||
// 创建下载链接
|
||||
const dataUrl = `data:${mimeType};base64,${processedImageBase64}`;
|
||||
const link = document.createElement("a");
|
||||
link.href = dataUrl;
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
toast.success("图片下载成功");
|
||||
} catch (error) {
|
||||
console.error("[下载] 错误:", error);
|
||||
toast.error("下载失败,请稍后重试");
|
||||
}
|
||||
};
|
||||
|
||||
// 确认
|
||||
const handleConfirm = () => {
|
||||
const canvas = canvasRef.current;
|
||||
@ -473,7 +484,7 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
{/* 弹窗容器 */}
|
||||
<div
|
||||
className="flex flex-col overflow-hidden rounded-xl border border-border bg-background shadow-2xl"
|
||||
style={{ width: "850px", height: "550px" }}
|
||||
style={{ width: "1200px", height: "550px" }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* 顶部标题栏 */}
|
||||
@ -587,8 +598,8 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 画布区域 */}
|
||||
<div ref={containerRef} className="flex flex-1 items-center justify-center overflow-hidden bg-muted/30 p-4">
|
||||
{/* 画布区域 - 并排显示原图和处理后的图片 */}
|
||||
<div ref={containerRef} className="flex flex-1 items-center justify-center gap-4 overflow-hidden bg-muted/30 p-4">
|
||||
{isLoading ? (
|
||||
/* 加载动画 */
|
||||
<div className="flex flex-col items-center justify-center gap-3">
|
||||
@ -609,17 +620,51 @@ export function ImageAnnotator({ imageUrl, onConfirm, onCancel }: ImageAnnotator
|
||||
<span className="text-sm text-muted-foreground">正在加载图片...</span>
|
||||
</div>
|
||||
) : (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="cursor-crosshair rounded-lg shadow-lg"
|
||||
onMouseDown={handleStart}
|
||||
onMouseMove={handleMove}
|
||||
onMouseUp={handleEnd}
|
||||
onMouseLeave={handleEnd}
|
||||
onTouchStart={handleStart}
|
||||
onTouchMove={handleMove}
|
||||
onTouchEnd={handleEnd}
|
||||
/>
|
||||
<>
|
||||
{/* 原图画布 */}
|
||||
<div className="flex flex-1 flex-col items-center justify-center">
|
||||
<div className="mb-2 text-xs text-muted-foreground">原图</div>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="cursor-crosshair rounded-lg shadow-lg max-w-full"
|
||||
onMouseDown={handleStart}
|
||||
onMouseMove={handleMove}
|
||||
onMouseUp={handleEnd}
|
||||
onMouseLeave={handleEnd}
|
||||
onTouchStart={handleStart}
|
||||
onTouchMove={handleMove}
|
||||
onTouchEnd={handleEnd}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 处理后的图片展示区域 */}
|
||||
{processedImageBase64 && (
|
||||
<div className="flex flex-1 flex-col items-center justify-center">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{processType === "rmbg" ? "抠图结果" : "清晰结果"}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleDownloadProcessed}
|
||||
className="h-7 px-2"
|
||||
title="下载处理后的图片"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="relative rounded-lg border-2 border-primary/50 bg-background p-2 shadow-lg">
|
||||
<img
|
||||
src={processedImageUrl || `data:image/${processType === "rmbg" ? "png" : "jpeg"};base64,${processedImageBase64}`}
|
||||
alt={processType === "rmbg" ? "抠图结果" : "清晰结果"}
|
||||
className="max-h-[350px] max-w-full rounded-lg"
|
||||
style={{ maxWidth: canvasRef.current?.width || "400px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user