mixvideo-v2/apps/desktop/src/components/PageTransition.tsx

210 lines
4.2 KiB
TypeScript

import React, { useEffect, useState } from 'react';
interface PageTransitionProps {
children: React.ReactNode;
className?: string;
delay?: number;
}
/**
* 页面过渡动画组件
* 提供平滑的页面切换效果
*/
export const PageTransition: React.FC<PageTransitionProps> = ({
children,
className = '',
delay = 0
}) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(true);
}, delay);
return () => clearTimeout(timer);
}, [delay]);
return (
<div
className={`
transition-all duration-500 ease-out
${isVisible
? 'opacity-100 translate-y-0'
: 'opacity-0 translate-y-4'
}
${className}
`}
>
{children}
</div>
);
};
/**
* 交错动画容器
* 为子元素提供依次出现的动画效果
*/
export const StaggeredContainer: React.FC<{
children: React.ReactNode;
staggerDelay?: number;
className?: string;
}> = ({ children, staggerDelay = 100, className = '' }) => {
return (
<div className={className}>
{React.Children.map(children, (child, index) => (
<PageTransition delay={index * staggerDelay}>
{child}
</PageTransition>
))}
</div>
);
};
/**
* 滑入动画组件
*/
export const SlideIn: React.FC<{
children: React.ReactNode;
direction?: 'left' | 'right' | 'up' | 'down';
delay?: number;
className?: string;
}> = ({ children, direction = 'up', delay = 0, className = '' }) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(true);
}, delay);
return () => clearTimeout(timer);
}, [delay]);
const getTransformClasses = () => {
const transforms = {
left: isVisible ? 'translate-x-0' : '-translate-x-8',
right: isVisible ? 'translate-x-0' : 'translate-x-8',
up: isVisible ? 'translate-y-0' : 'translate-y-8',
down: isVisible ? 'translate-y-0' : '-translate-y-8'
};
return transforms[direction];
};
return (
<div
className={`
transition-all duration-500 ease-out
${isVisible ? 'opacity-100' : 'opacity-0'}
${getTransformClasses()}
${className}
`}
>
{children}
</div>
);
};
/**
* 缩放动画组件
*/
export const ScaleIn: React.FC<{
children: React.ReactNode;
delay?: number;
className?: string;
}> = ({ children, delay = 0, className = '' }) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(true);
}, delay);
return () => clearTimeout(timer);
}, [delay]);
return (
<div
className={`
transition-all duration-300 ease-out
${isVisible
? 'opacity-100 scale-100'
: 'opacity-0 scale-95'
}
${className}
`}
>
{children}
</div>
);
};
/**
* 悬停放大组件
*/
export const HoverScale: React.FC<{
children: React.ReactNode;
scale?: number;
className?: string;
}> = ({ children, scale = 1.05, className = '' }) => {
return (
<div
className={`
transition-transform duration-200 ease-out
hover:scale-[${scale}]
${className}
`}
>
{children}
</div>
);
};
/**
* 悬停倾斜组件
*/
export const HoverTilt: React.FC<{
children: React.ReactNode;
degree?: number;
className?: string;
}> = ({ children, degree = 2, className = '' }) => {
return (
<div
className={`
transition-transform duration-200 ease-out
hover:rotate-[${degree}deg]
${className}
`}
>
{children}
</div>
);
};
/**
* 脉冲动画组件
*/
export const Pulse: React.FC<{
children: React.ReactNode;
className?: string;
}> = ({ children, className = '' }) => {
return (
<div className={`animate-pulse ${className}`}>
{children}
</div>
);
};
/**
* 呼吸动画组件
*/
export const Breathe: React.FC<{
children: React.ReactNode;
className?: string;
}> = ({ children, className = '' }) => {
return (
<div className={`animate-breathe ${className}`}>
{children}
</div>
);
};