lovart3/components/qianwen-video-loading.tsx
2025-12-17 20:15:18 +08:00

138 lines
5.3 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";
/**
* 千问视频生成加载占位符组件
* 显示一个带有一闪一闪头像效果的动态卡片
*/
export const QianwenVideoLoading = memo(() => {
return (
<div
className="group/message fade-in w-full animate-in duration-300"
data-role="assistant"
data-testid="message-assistant-video-loading"
>
<div className="flex items-start justify-start gap-3">
{/* 头像 - 和图片生成一样的一闪一闪效果 */}
<div className="relative -mt-1 flex size-8 shrink-0 items-center justify-center">
<div className="absolute inset-0 rounded-full bg-blue-500/20 animate-ping" />
<div className="absolute inset-0 rounded-full bg-blue-500/10 animate-pulse" />
<div className="relative flex size-8 items-center justify-center rounded-full bg-background ring-2 ring-blue-500/30">
<div className="animate-spin text-blue-500">
<SparklesIcon size={14} />
</div>
</div>
</div>
<div className="flex flex-col gap-3">
{/* 文本提示 */}
<div className="flex items-center gap-1.5">
<span className="text-base font-medium text-foreground">
正在生成视频
</span>
<span className="flex gap-1">
<span
className="h-2 w-2 rounded-full bg-blue-500 animate-bounce"
style={{ animationDelay: "0ms" }}
/>
<span
className="h-2 w-2 rounded-full bg-blue-500 animate-bounce"
style={{ animationDelay: "150ms" }}
/>
<span
className="h-2 w-2 rounded-full bg-blue-500 animate-bounce"
style={{ animationDelay: "300ms" }}
/>
</span>
</div>
{/* 视频占位符 - 170x200 像素,带动态效果 */}
<div
className="relative overflow-hidden rounded-xl"
style={{ width: "170px", height: "200px" }}
>
{/* 底层渐变背景 */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-400 via-purple-400 to-pink-400 opacity-60" />
{/* 动态模糊叠加层 1 */}
<div
className="absolute inset-0 bg-gradient-to-tr from-blue-500/40 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 to-blue-500/30 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" }} />
{/* 视频图标 */}
<svg
className="h-10 w-10 text-white/80 animate-pulse"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
style={{ animationDuration: "1.5s" }}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
</div>
</div>
{/* 流光效果 */}
<div
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full animate-shimmer"
style={{
animation: "shimmer 2s infinite",
}}
/>
</div>
{/* 提示文字 */}
<span className="text-xs text-muted-foreground">
⏱️ 视频生成需要 30-60 秒,请耐心等待...
</span>
</div>
</div>
{/* 流光动画样式 */}
<style jsx>{`
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
`}</style>
</div>
);
});
QianwenVideoLoading.displayName = "QianwenVideoLoading";