78 lines
2.1 KiB
TypeScript
78 lines
2.1 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { motion, useScroll, useSpring } from 'framer-motion';
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
|
||
|
|
export default function BackToTop() {
|
||
|
|
const [isVisible, setIsVisible] = useState(false);
|
||
|
|
const { scrollYProgress } = useScroll();
|
||
|
|
const scaleX = useSpring(scrollYProgress, {
|
||
|
|
stiffness: 100,
|
||
|
|
damping: 30,
|
||
|
|
restDelta: 0.001,
|
||
|
|
});
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const toggleVisibility = () => {
|
||
|
|
if (window.pageYOffset > 300) {
|
||
|
|
setIsVisible(true);
|
||
|
|
} else {
|
||
|
|
setIsVisible(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
window.addEventListener('scroll', toggleVisibility);
|
||
|
|
return () => window.removeEventListener('scroll', toggleVisibility);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const scrollToTop = () => {
|
||
|
|
window.scrollTo({
|
||
|
|
top: 0,
|
||
|
|
behavior: 'smooth',
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
{/* Progress bar */}
|
||
|
|
<motion.div
|
||
|
|
className="fixed top-0 left-0 right-0 h-1 bg-gradient-to-r from-purple-500 to-cyan-500 transform origin-left z-50"
|
||
|
|
style={{ scaleX }}
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Back to top button */}
|
||
|
|
{isVisible && (
|
||
|
|
<motion.button
|
||
|
|
onClick={scrollToTop}
|
||
|
|
className="fixed bottom-8 right-8 p-4 rounded-full glass hover:bg-white/10 text-purple-400 hover:text-purple-300 transition-colors shadow-lg z-40 group"
|
||
|
|
initial={{ opacity: 0, y: 20 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
exit={{ opacity: 0, y: 20 }}
|
||
|
|
whileHover={{ scale: 1.1 }}
|
||
|
|
whileTap={{ scale: 0.9 }}
|
||
|
|
aria-label="Back to top"
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
className="w-6 h-6"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
strokeLinecap="round"
|
||
|
|
strokeLinejoin="round"
|
||
|
|
strokeWidth={2}
|
||
|
|
d="M5 10l7-7m0 0l7 7m-7-7v18"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
|
||
|
|
{/* Tooltip */}
|
||
|
|
<span className="absolute bottom-full right-0 mb-2 px-3 py-1 text-xs text-white bg-gray-900 rounded-lg opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
|
||
|
|
Back to top
|
||
|
|
</span>
|
||
|
|
</motion.button>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|