Spaces:
Running
Running
| import React, { useState, useEffect } from 'react'; | |
| import { motion, AnimatePresence } from 'framer-motion'; | |
| import { ArrowUpCircle } from 'lucide-react'; | |
| import { Button } from '@/components/ui/button'; | |
| const ScrollToTopButton = () => { | |
| const [isVisible, setIsVisible] = useState(false); | |
| const toggleVisibility = () => { | |
| if (window.pageYOffset > 300) { | |
| setIsVisible(true); | |
| } else { | |
| setIsVisible(false); | |
| } | |
| }; | |
| const scrollToTop = () => { | |
| // Get the current scroll position | |
| const startPosition = window.pageYOffset; | |
| // Duration of the scroll animation in milliseconds | |
| const duration = 800; | |
| // Improved easing function for smoother animation | |
| const easeOutCubic = t => 1 - Math.pow(1 - t, 3); | |
| let startTime = null; | |
| const animation = currentTime => { | |
| if (startTime === null) startTime = currentTime; | |
| const timeElapsed = currentTime - startTime; | |
| const progress = Math.min(timeElapsed / duration, 1); | |
| const easedProgress = easeOutCubic(progress); | |
| window.scrollTo(0, startPosition * (1 - easedProgress)); | |
| if (timeElapsed < duration) { | |
| requestAnimationFrame(animation); | |
| } | |
| }; | |
| requestAnimationFrame(animation); | |
| }; | |
| useEffect(() => { | |
| window.addEventListener('scroll', toggleVisibility); | |
| return () => { | |
| window.removeEventListener('scroll', toggleVisibility); | |
| }; | |
| }, []); | |
| return ( | |
| <AnimatePresence> | |
| {isVisible && ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: 20 }} | |
| transition={{ duration: 0.5, ease: "easeInOut" }} | |
| className="fixed bottom-20 left-6 z-50" | |
| > | |
| <motion.div | |
| whileHover={{ scale: 1.1 }} | |
| whileTap={{ scale: 0.9 }} | |
| transition={{ type: "spring", stiffness: 400, damping: 17 }} | |
| > | |
| <Button | |
| onClick={scrollToTop} | |
| variant="outline" | |
| size="icon" | |
| className="bg-amber-600 hover:bg-amber-700 text-white border-amber-700 rounded-full shadow-lg w-12 h-12" | |
| > | |
| <ArrowUpCircle size={28} /> | |
| <span className="sr-only">Scroll to top</span> | |
| </Button> | |
| </motion.div> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| ); | |
| }; | |
| export default ScrollToTopButton; |