|
| 1 | +'use client'; |
| 2 | + |
| 3 | +import { useRef } from 'react'; |
| 4 | +import { gsap } from 'gsap'; |
| 5 | +import { useGSAP } from '@gsap/react'; |
| 6 | +import { Users } from 'lucide-react'; |
| 7 | + |
| 8 | +// Register the hook to prevent React strict-mode double-firing issues |
| 9 | +gsap.registerPlugin(useGSAP); |
| 10 | + |
| 11 | +export default function AnimatedUsersButton() { |
| 12 | + const usersIconRef = useRef<SVGSVGElement>(null); |
| 13 | + |
| 14 | + // 1. Mount Animation: Slides the background user in when the page loads |
| 15 | + useGSAP( |
| 16 | + () => { |
| 17 | + if (!usersIconRef.current) return; |
| 18 | + |
| 19 | + // Selects elements inside this specific SVG |
| 20 | + const q = gsap.utils.selector(usersIconRef); |
| 21 | + |
| 22 | + // Target the elements making up the background person |
| 23 | + // (Lucide draws the foreground person first, so the last path/circle belong to the background) |
| 24 | + gsap.from(q('path:last-of-type, circle:last-of-type'), { |
| 25 | + x: 10, |
| 26 | + opacity: 0, |
| 27 | + duration: 0.6, |
| 28 | + ease: 'back.out(1.5)', |
| 29 | + delay: 0.1, // Slight delay so it feels natural after the page loads |
| 30 | + }); |
| 31 | + }, |
| 32 | + { scope: usersIconRef } |
| 33 | + ); |
| 34 | + |
| 35 | + // 2. Hover Animation: Makes both users "bounce" sequentially |
| 36 | + const handleMouseEnter = () => { |
| 37 | + if (!usersIconRef.current) return; |
| 38 | + |
| 39 | + // Grab all the individual paths and circles inside the SVG |
| 40 | + const svgParts = usersIconRef.current.children; |
| 41 | + |
| 42 | + gsap.to(svgParts, { |
| 43 | + y: -3, |
| 44 | + duration: 0.2, |
| 45 | + stagger: 0.05, |
| 46 | + yoyo: true, |
| 47 | + repeat: 1, |
| 48 | + ease: 'power2.out', |
| 49 | + transformOrigin: 'bottom center', |
| 50 | + }); |
| 51 | + }; |
| 52 | + |
| 53 | + return ( |
| 54 | + <button |
| 55 | + onMouseEnter={handleMouseEnter} |
| 56 | + className='group flex w-fit items-center gap-3 rounded-xl border border-neutral-800 bg-neutral-900 px-5 py-3 text-neutral-300 transition-colors hover:border-blue-500/50 hover:bg-neutral-800 hover:text-white' |
| 57 | + > |
| 58 | + <Users ref={usersIconRef} className='size-5 text-blue-400' /> |
| 59 | + <span className='text-sm font-medium'>View Participants</span> |
| 60 | + </button> |
| 61 | + ); |
| 62 | +} |
0 commit comments