taghirsado / components /FlyToHistory.tsx
Opera10's picture
Upload 10 files
a8df197 verified
Raw
History Blame Contribute Delete
3.6 kB
import React, { useEffect, useState } from 'react';
interface FlyToHistoryProps {
onComplete: () => void;
targetRef: React.RefObject<HTMLElement | null>;
mode: 'vc' | 'tts';
}
const FlyToHistory: React.FC<FlyToHistoryProps> = ({ onComplete, targetRef, mode }) => {
const [style, setStyle] = useState<React.CSSProperties>({
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%) scale(1.5)',
opacity: 0,
});
const isVoice = mode === 'vc';
// Distinct visuals for Voice vs Text
const gradientClass = isVoice
? 'from-blue-600 via-indigo-500 to-purple-600'
: 'from-teal-400 via-emerald-500 to-cyan-600';
const shadowColor = isVoice
? 'rgba(79, 70, 229, 0.6)'
: 'rgba(16, 185, 129, 0.6)';
// Distinct icons
const iconClass = isVoice ? 'fa-microphone-lines' : 'fa-keyboard';
useEffect(() => {
// Initial reveal
requestAnimationFrame(() => {
setStyle(prev => ({ ...prev, opacity: 1, transform: 'translate(-50%, -50%) scale(1)' }));
});
if (!targetRef.current) {
setTimeout(onComplete, 800);
return;
}
// 1. Get Target Coordinates
const targetRect = targetRef.current.getBoundingClientRect();
const targetX = targetRect.left + targetRect.width / 2;
const targetY = targetRect.top + targetRect.height / 2;
// 2. Start Flight Animation
const animationTimeout = setTimeout(() => {
setStyle({
top: `${targetY}px`,
left: `${targetX}px`,
// Shrink, rotate, and fade out as it hits the target
transform: 'translate(-50%, -50%) scale(0.1) rotate(-180deg)',
opacity: 0.1,
});
}, 250); // Small pause for user to recognize the icon
// 3. Cleanup
const endTimeout = setTimeout(() => {
onComplete();
}, 1000);
return () => {
clearTimeout(animationTimeout);
clearTimeout(endTimeout);
};
}, [targetRef, onComplete]);
return (
<>
{/* Moving Object */}
<div
style={{
...style,
transition: 'all 0.75s cubic-bezier(0.175, 0.885, 0.32, 1.275)', // Bouncy landing curve
position: 'fixed',
zIndex: 100,
}}
className="pointer-events-none flex items-center justify-center will-change-transform"
>
{/* Main Icon Orb */}
<div
className={`relative w-20 h-20 bg-gradient-to-br ${gradientClass} rounded-2xl flex items-center justify-center z-20 border-2 border-white/40 backdrop-blur-sm`}
style={{ boxShadow: `0 10px 40px ${shadowColor}, inset 0 0 20px rgba(255,255,255,0.3)` }}
>
<i className={`fas ${iconClass} text-3xl text-white drop-shadow-md`}></i>
{/* Inner Ping Effect */}
<div className="absolute inset-0 border-4 border-white/20 rounded-2xl animate-[ping_1.5s_infinite]"></div>
{/* Sparkles */}
<div className="absolute -top-2 -right-2 w-4 h-4 bg-white rounded-full animate-pulse shadow-[0_0_15px_white]"></div>
</div>
{/* Trail Effect */}
<div
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-48 h-16 bg-gradient-to-r from-transparent via-white/30 to-transparent blur-xl rounded-full -z-10 animate-pulse"
style={{ transform: 'translate(-50%, -50%) rotate(-45deg)' }}
></div>
</div>
{/* Screen Flash Overlay */}
<div className="fixed inset-0 bg-white/30 z-[90] animate-[fadeOut_0.5s_ease-out] pointer-events-none"></div>
</>
);
};
export default FlyToHistory;