video-flow-b/components/ui/progressive-reveal.tsx
2025-06-29 00:04:15 +08:00

242 lines
5.6 KiB
TypeScript

'use client';
import React from 'react';
import { motion } from 'framer-motion';
interface ProgressiveRevealProps {
/** 要显示的内容 */
children: React.ReactNode;
/** 容器类名 */
className?: string;
/** 是否为视频内容 */
isVideo?: boolean;
/** 动画延迟时间(秒) */
delay?: number;
/** 渐进显示动画时长(秒) */
revealDuration?: number;
/** 模糊过渡动画时长(秒) */
blurDuration?: number;
/** 初始模糊度 */
initialBlur?: number;
/** 自定义动画变体 */
customVariants?: {
hidden: any;
visible: any;
};
/** 自定义过渡效果 */
customTransition?: any;
/** 是否显示加载背景 */
showLoadingBg?: boolean;
/** 加载背景配置 */
loadingBgConfig?: {
/** 渐变色起始颜色 */
fromColor?: string;
/** 渐变色中间颜色 */
viaColor?: string;
/** 渐变色结束颜色 */
toColor?: string;
/** 光效不透明度 */
glowOpacity?: number;
/** 背景动画持续时间 */
duration?: number;
};
}
export const ProgressiveReveal: React.FC<ProgressiveRevealProps> = ({
children,
className = '',
isVideo = false,
delay = 0,
revealDuration = 0.8,
blurDuration = 0.5,
initialBlur = 20,
customVariants,
customTransition,
showLoadingBg = true,
loadingBgConfig = {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.5,
duration: 5,
},
}) => {
// 默认动画变体
const defaultVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
};
// 默认过渡效果
const defaultTransition = {
duration: 0.3,
delay
};
return (
<motion.div
className={`relative overflow-hidden ${className}`}
initial="hidden"
animate="visible"
variants={customVariants || defaultVariants}
transition={customTransition || defaultTransition}
>
{/* 加载背景 */}
{showLoadingBg && (
<>
{/* 动态渐变背景 */}
<motion.div
className={`absolute inset-0 bg-gradient-to-r ${loadingBgConfig.fromColor} ${loadingBgConfig.viaColor} ${loadingBgConfig.toColor}`}
animate={{
backgroundPosition: ["0% 50%", "100% 50%", "0% 50%"],
}}
transition={{
duration: loadingBgConfig.duration,
repeat: Infinity,
ease: "linear"
}}
style={{
backgroundSize: "200% 200%",
}}
/>
{/* 动态光效 */}
<motion.div
className="absolute inset-0"
style={{
background: "radial-gradient(circle at center, rgba(255,255,255,0.8) 0%, transparent 50%)",
opacity: loadingBgConfig.glowOpacity,
}}
animate={{
scale: [1, 1.2, 1],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
/>
</>
)}
{/* 内容显示动画 */}
<motion.div
className="relative w-full h-full"
initial={{ clipPath: "inset(0 100% 0 0)" }}
animate={{ clipPath: "inset(0 0% 0 0)" }}
transition={{
duration: revealDuration,
ease: "easeInOut",
delay
}}
>
<motion.div
className="w-full h-full"
initial={{ filter: `blur(${initialBlur}px)` }}
animate={{ filter: "blur(0px)" }}
transition={{
duration: blurDuration,
delay: delay + revealDuration,
ease: "easeOut"
}}
>
{children}
</motion.div>
</motion.div>
</motion.div>
);
};
// 预设配置
export const presets = {
// 快速显示
fast: {
revealDuration: 0.4,
blurDuration: 0.3,
initialBlur: 10,
loadingBgConfig: {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.5,
duration: 3,
}
},
// 标准显示
standard: {
revealDuration: 0.8,
blurDuration: 0.5,
initialBlur: 20,
loadingBgConfig: {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.5,
duration: 5,
}
},
// 缓慢显示
slow: {
revealDuration: 1.2,
blurDuration: 0.7,
initialBlur: 30,
loadingBgConfig: {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.5,
duration: 7,
}
},
// 缩略图
thumbnail: {
revealDuration: 0.6,
blurDuration: 0.4,
initialBlur: 10,
loadingBgConfig: {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.3,
duration: 4,
}
},
// 主内容
main: {
revealDuration: 0.8,
blurDuration: 0.5,
initialBlur: 20,
loadingBgConfig: {
fromColor: 'from-cyan-300',
viaColor: 'via-sky-400',
toColor: 'to-blue-500',
glowOpacity: 0.5,
duration: 5,
}
},
// 温暖色调
warm: {
revealDuration: 0.8,
blurDuration: 0.5,
initialBlur: 20,
loadingBgConfig: {
fromColor: 'from-orange-300',
viaColor: 'via-red-400',
toColor: 'to-rose-500',
glowOpacity: 0.5,
duration: 5,
}
},
// 冷色调
cool: {
revealDuration: 0.8,
blurDuration: 0.5,
initialBlur: 20,
loadingBgConfig: {
fromColor: 'from-emerald-300',
viaColor: 'via-teal-400',
toColor: 'to-cyan-500',
glowOpacity: 0.5,
duration: 5,
}
},
};