ai-chatbot/components/image-processing-loading.tsx

191 lines
6.4 KiB
TypeScript
Raw Permalink 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.

"use client";
import { memo } from "react";
import { SparklesIcon } from "./icons";
import { Scissors, Expand, Droplet } from "lucide-react";
import { BloubAvatar } from "./bloub-avatar";
interface ImageProcessingLoadingProps {
type: "enhance" | "rmbg" | "extend" | "watermark"; // 处理类型:变清晰、抠图、图像拓展或去水印
}
/**
* 图片处理加载占位符组件
* 显示类似 Gemini 生图的动态卡片效果
*/
export const ImageProcessingLoading = memo(({ type }: ImageProcessingLoadingProps) => {
const isEnhance = type === "enhance";
const isRmbg = type === "rmbg";
const isExtend = type === "extend";
const isWatermark = type === "watermark";
let text: string;
let icon: any;
let colorClasses: any;
if (isEnhance) {
text = "正在变清晰";
icon = SparklesIcon;
colorClasses = {
ping: "bg-blue-500/20",
pulse: "bg-blue-500/10",
ring: "ring-blue-500/30",
text: "text-blue-500",
dot: "bg-blue-500",
gradientFrom: "from-blue-400",
gradientFromOverlay: "from-blue-500/40",
gradientToOverlay: "to-blue-500/30",
};
} else if (isRmbg) {
text = "正在抠图";
icon = Scissors;
colorClasses = {
ping: "bg-green-500/20",
pulse: "bg-green-500/10",
ring: "ring-green-500/30",
text: "text-green-500",
dot: "bg-green-500",
gradientFrom: "from-green-400",
gradientFromOverlay: "from-green-500/40",
gradientToOverlay: "to-green-500/30",
};
} else if (isWatermark) {
text = "正在去水印";
icon = Droplet;
colorClasses = {
ping: "bg-cyan-500/20",
pulse: "bg-cyan-500/10",
ring: "ring-cyan-500/30",
text: "text-cyan-500",
dot: "bg-cyan-500",
gradientFrom: "from-cyan-400",
gradientFromOverlay: "from-cyan-500/40",
gradientToOverlay: "to-cyan-500/30",
};
} else {
text = "正在图像拓展";
icon = Expand;
colorClasses = {
ping: "bg-orange-500/20",
pulse: "bg-orange-500/10",
ring: "ring-orange-500/30",
text: "text-orange-500",
dot: "bg-orange-500",
gradientFrom: "from-orange-400",
gradientFromOverlay: "from-orange-500/40",
gradientToOverlay: "to-orange-500/30",
};
}
const Icon = icon;
return (
<div
className="group/message fade-in w-full animate-in duration-300"
data-role="assistant"
data-testid="message-assistant-image-processing-loading"
>
<div className="flex items-start justify-start gap-3">
<BloubAvatar
className="-mt-1"
label={`${text}中`}
size={36}
state="thinking"
/>
<div className="flex flex-col gap-3">
{/* 文本提示 */}
<div className="flex items-center gap-1.5">
<span className="text-base font-medium text-foreground">
{text}
</span>
<span className="flex gap-1">
<span
className={`h-2 w-2 rounded-full ${colorClasses.dot} animate-bounce`}
style={{ animationDelay: "0ms" }}
/>
<span
className={`h-2 w-2 rounded-full ${colorClasses.dot} animate-bounce`}
style={{ animationDelay: "150ms" }}
/>
<span
className={`h-2 w-2 rounded-full ${colorClasses.dot} animate-bounce`}
style={{ animationDelay: "300ms" }}
/>
</span>
</div>
{/* 图片占位符 - 170x200px */}
<div className="relative overflow-hidden rounded-xl" style={{ width: "170px", height: "200px" }}>
{/* 底层渐变背景 */}
<div className={`absolute inset-0 bg-gradient-to-br ${colorClasses.gradientFrom} via-purple-400 to-pink-400 opacity-60`} />
{/* 动态模糊叠加层 1 */}
<div
className={`absolute inset-0 bg-gradient-to-tr ${colorClasses.gradientFromOverlay} via-transparent to-purple-500/40 animate-pulse`}
style={{ animationDuration: "2s" }}
/>
{/* 动态模糊叠加层 2 */}
<div
className={`absolute inset-0 bg-gradient-to-bl from-purple-500/30 via-transparent ${colorClasses.gradientToOverlay} animate-pulse`}
style={{ animationDuration: "2.5s", animationDelay: "0.5s" }}
/>
{/* 噪点纹理效果 */}
<div
className="absolute inset-0 opacity-30"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 400 400' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
}}
/>
{/* 模糊叠加 */}
<div className="absolute inset-0 backdrop-blur-sm" />
{/* 中心图标 */}
<div className="absolute inset-0 flex items-center justify-center">
<div className="relative">
{/* 外圈光晕 */}
<div className="absolute -inset-4 rounded-full bg-white/20 animate-ping" style={{ animationDuration: "1.5s" }} />
{/* 图标 */}
<Icon
className="h-10 w-10 text-white/80 animate-pulse"
style={{ animationDuration: "1.5s" }}
/>
</div>
</div>
{/* 流光效果 */}
<div
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full"
style={{
animation: "shimmer 2s infinite",
}}
/>
</div>
{/* 提示文字 */}
<span className="text-xs text-muted-foreground">
正在处理图片,请稍候...
</span>
</div>
</div>
{/* 流光动画样式 */}
<style jsx>{`
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
`}</style>
</div>
);
});
ImageProcessingLoading.displayName = "ImageProcessingLoading";