2025-12-04 20:05:13 +08:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
|
|
|
|
|
|
|
|
export default function ScrollToTop() {
|
|
|
|
|
|
const [showScrollTop, setShowScrollTop] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
let ticking = false;
|
|
|
|
|
|
|
|
|
|
|
|
const handleScroll = () => {
|
|
|
|
|
|
if (!ticking) {
|
|
|
|
|
|
window.requestAnimationFrame(() => {
|
|
|
|
|
|
const currentScrollY = window.scrollY;
|
|
|
|
|
|
// 显示返回顶部按钮(滚动超过300px)
|
|
|
|
|
|
setShowScrollTop(currentScrollY > 300);
|
|
|
|
|
|
ticking = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
ticking = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
|
|
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const scrollToTop = () => {
|
|
|
|
|
|
window.scrollTo({
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
behavior: 'smooth'
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<AnimatePresence>
|
|
|
|
|
|
{showScrollTop && (
|
|
|
|
|
|
<motion.button
|
|
|
|
|
|
initial={{ opacity: 0, scale: 0.8, y: 20 }}
|
|
|
|
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
|
|
|
|
exit={{ opacity: 0, scale: 0.8, y: 20 }}
|
|
|
|
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
|
|
|
|
|
onClick={scrollToTop}
|
2026-01-13 20:32:13 +08:00
|
|
|
|
className="fixed bottom-6 right-6 w-12 h-12 bg-gradient-to-br from-orange-400/70 to-amber-300/70 hover:from-orange-600/70 hover:to-orange-700/70 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-200 flex items-center justify-center z-40 group"
|
2025-12-04 20:05:13 +08:00
|
|
|
|
whileHover={{ scale: 1.1, y: -2 }}
|
|
|
|
|
|
whileTap={{ scale: 0.9 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg
|
|
|
|
|
|
className="w-5 h-5 transition-transform duration-200 group-hover:-translate-y-0.5"
|
|
|
|
|
|
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>
|
|
|
|
|
|
</motion.button>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|