Install component
"use client"; import { useRef, useState, SVGProps } from "react"; import { Cursor } from "@/components/ui/cursor"; import { AnimatePresence, motion } from "framer-motion"; import { PlusIcon, } from "lucide-react"; const MouseIcon = (props: SVGProps<SVGSVGElement>) => { return ( <svg xmlns="http://www.w3.org/2000/svg" width={26} height={31} fill="none" {...props} > <g clipPath="url(#a)"> <path fill={"#22c55e"} fillRule="evenodd" stroke={"#fff"} strokeLinecap="square" strokeWidth={2} d="M21.993 14.425 2.549 2.935l4.444 23.108 4.653-10.002z" clipRule="evenodd" /> </g> <defs> <clipPath id="a"> <path fill={"#22c55e"} d="M0 0h26v31H0z" /> </clipPath> </defs> </svg> ); }; export function CursorWithComponent() { return ( <div className="py-12"> <div className="overflow-hidden rounded-[12px] bg-white p-2 shadow-md dark:bg-black"> <Cursor attachToParent variants={{ initial: { scale: 0.3, opacity: 0 }, animate: { scale: 1, opacity: 1 }, exit: { scale: 0.3, opacity: 0 }, }} transition={{ ease: "easeInOut", duration: 0.15, }} className="left-12 top-4" > <div> <MouseIcon className="h-6 w-6" /> <div className="ml-4 mt-1 rounded-[4px] bg-green-500 px-2 py-0.5 text-neutral-50"> The city below </div> </div> </Cursor> <img src="https://i.pinimg.com/564x/a0/6a/5f/a06a5f814569fcf4a67f3ad89ae1babf.jpg" alt="Green herbs" className="h-40 w-full max-w-32 rounded-[8px] object-cover" /> </div> </div> ); } export function CursorWithSpring() { return ( <div> <div className="p-4"> <Cursor attachToParent variants={{ initial: { height: 0, opacity: 0, scale: 0.3 }, animate: { height: "auto", opacity: 1, scale: 1 }, exit: { height: 0, opacity: 0, scale: 0.3 }, }} transition={{ type: "spring", duration: 0.3, bounce: 0.1, }} className="overflow-hidden" springConfig={{ bounce: 0.01, }} > <img src="https://i.pinimg.com/564x/4c/95/69/4c9569ab2928e5ae400a6a34e7c537a0.jpg" alt="Christian Church, Eastern Europe" className="h-40 w-40" /> </Cursor> Christian Church, Eastern Europe </div> </div> ); } export function CursorWithImage() { const [isHovering, setIsHovering] = useState(false); const targetRef = useRef < HTMLDivElement > null; const handlePositionChange = (x: number, y: number) => { if (targetRef.current) { const rect = targetRef.current.getBoundingClientRect(); const isInside = x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; setIsHovering(isInside); } }; return ( <div className="flex h-[400px] w-full items-center justify-center"> <Cursor attachToParent variants={{ initial: { scale: 0.3, opacity: 0 }, animate: { scale: 1, opacity: 1 }, exit: { scale: 0.3, opacity: 0 }, }} springConfig={{ bounce: 0.001, }} transition={{ ease: "easeInOut", duration: 0.15, }} onPositionChange={handlePositionChange} > <motion.div animate={{ width: isHovering ? 80 : 16, height: isHovering ? 32 : 16, }} className="flex items-center justify-center rounded-[24px] bg-gray-500/40 backdrop-blur-md dark:bg-gray-300/40" > <AnimatePresence> {isHovering ? ( <motion.div initial={{ opacity: 0, scale: 0.6 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.6 }} className="inline-flex w-full items-center justify-center" > <div className="inline-flex items-center text-sm text-white dark:text-black"> More <PlusIcon className="ml-1 h-4 w-4" /> </div> </motion.div> ) : null} </AnimatePresence> </motion.div> </Cursor> <div ref={targetRef}> <img src="https://i.pinimg.com/564x/75/3c/3f/753c3f1a9f85871ffa7a7a78bcf49f66.jpg" alt="Olympic logo Paris 2024" className="h-52 w-full max-w-48 rounded-[8px] border border-zinc-100 object-cover" /> </div> </div> ); } export { CursorWithComponent, CursorWithSpring, CursorWithImage };