forked from 77media/video-flow
242 lines
5.6 KiB
TypeScript
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,
|
|
}
|
|
},
|
|
};
|