import { motion, useMotionValue, useSpring } from 'motion/react' import { useRef, useState } from 'react' const springValues = { damping: 30, stiffness: 100, mass: 2 } export interface HoverTiltedCardProps { imageSrc: string altText?: string captionText?: string containerHeight?: string containerWidth?: string imageHeight?: string imageWidth?: string scaleOnHover?: number rotateAmplitude?: number showTooltip?: boolean overlayContent?: React.ReactNode displayOverlayContent?: boolean } export default function HoverTiltedCard({ imageSrc, altText = 'NapCat', captionText = 'NapCat', containerHeight = '200px', containerWidth = '100%', imageHeight = '200px', imageWidth = '200px', scaleOnHover = 1.1, rotateAmplitude = 14, showTooltip = false, overlayContent = (
NapCat
), displayOverlayContent = true }: HoverTiltedCardProps) { const ref = useRef(null) const x = useMotionValue(0) const y = useMotionValue(0) const rotateX = useSpring(useMotionValue(0), springValues) const rotateY = useSpring(useMotionValue(0), springValues) const scale = useSpring(1, springValues) const opacity = useSpring(0) const rotateFigcaption = useSpring(0, { stiffness: 350, damping: 30, mass: 1 }) const [lastY, setLastY] = useState(0) function handleMouse(e: React.MouseEvent) { if (!ref.current) return const rect = ref.current.getBoundingClientRect() const offsetX = e.clientX - rect.left - rect.width / 2 const offsetY = e.clientY - rect.top - rect.height / 2 const rotationX = (offsetY / (rect.height / 2)) * -rotateAmplitude const rotationY = (offsetX / (rect.width / 2)) * rotateAmplitude rotateX.set(rotationX) rotateY.set(rotationY) x.set(e.clientX - rect.left) y.set(e.clientY - rect.top) const velocityY = offsetY - lastY rotateFigcaption.set(-velocityY * 0.6) setLastY(offsetY) } function handleMouseEnter() { scale.set(scaleOnHover) opacity.set(1) } function handleMouseLeave() { opacity.set(0) scale.set(1) rotateX.set(0) rotateY.set(0) rotateFigcaption.set(0) } return (
{displayOverlayContent && overlayContent && ( {overlayContent} )} {showTooltip && ( {captionText} )}
) }