Trading Card

A card that moves in 3D when you hover over it. Perfect for showcasing profiles, characters, or products with engaging effects.

Install dependencies

npm i framer-motion tailwindcss

Component

Create a file trading-card.tsx in your components folder and paste this code

import { motion, useAnimationControls } from 'framer-motion'; import Image from 'next/image'; import React, { useRef } from 'react'; interface TradingCardProps { imageUrl: string; rank: number; name: string; description: string; } const TradingCard: React.FC<TradingCardProps> = ({ imageUrl, rank, name, description }) => { const cardRef = useRef<HTMLDivElement>(null); const backgroundControls = useAnimationControls(); const contentControls = useAnimationControls(); const cardControls = useAnimationControls(); const onMouseEnter: React.MouseEventHandler<HTMLDivElement> = () => { contentControls.start({ x: -300 }); backgroundControls.start({ scale: 1.05, opacity: 1 }); }; const onMouseLeave = () => { contentControls.start({ x: 0, transition: { delay: 0.5 } }); backgroundControls.start({ scale: 0.95, opacity: 0.4 }); cardControls.start({ transform: `rotateY(0deg) rotateX(0deg)` }); }; const onMouseMove: React.MouseEventHandler<HTMLDivElement> = (e) => { if (!cardRef.current) return; const rect = cardRef.current?.getBoundingClientRect(); if (!rect) return; const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const width = rect.right - rect.left; const height = rect.bottom - rect.top; const xd = (mx - width / 2) / 10; const yd = (height / 2 - my) / 10; cardRef.current.style.transform = `perspective(1000px) rotateY(${xd}deg) rotateX(${yd}deg)`; }; return ( <motion.div ref={cardRef} onMouseMove={onMouseMove} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} initial={{ transform: 'perspective(1000px) rotateX(0deg) rotateY(0deg)' }} style={{ transformStyle: 'preserve-3d' }} animate={cardControls} className="flex flex-col hover:scale-105 transition-all duration-200 ease-linear items-start justify-end rounded-lg relative shadow-xl overflow-hidden h-[400px] w-[300px] cursor-pointer border-[1px] border-neutral-800" > <motion.div className="h-full w-full absolute z-0" initial={{ opacity: 0.4, scale: 0.95 }} animate={backgroundControls} transition={{ duration: 0.7, ease: 'backOut' }} > <div className="h-full w-full inset-0 bg-cover bg-center"> <Image src={imageUrl} alt={name} fill className="object-cover" /> </div> </motion.div> <div className="font-semibold absolute top-5 right-5 z-10 text-white/70">#{rank}</div> <motion.div animate={contentControls} transition={{ duration: 0.5, ease: 'backOut' }} className="p-5 h-full w-full flex flex-col justify-end bg-transparent z-10 opacity-90" > <div className="font-medium"> {name.split(' ').map((word) => { return ( <div key={word} className="flex flex-col items-start justify-start text-4xl font-bold" > {word} <br /> </div> ); })} </div> <p className="text-sm text-white/90">{description}</p> </motion.div> </motion.div> ); }; export default TradingCard;

Usage

import TradingCard from './ui/trading-card'; const TradingCardDemo = () => { const cards = [ { imageUrl: 'https://ky008ymy6s.ufs.sh/f/NFGlOqM3XnMdo7u44DpEAkCcBT6MsuPK2yhGzx8aIU5obVlp', rank: 7, name: 'Neymar Jr', description: "Genius on the field, Neymar's journey is a testament to talent, resilience, and the pursuit of excellence. His electrifying skills and unwavering determination make him a force to be reckoned with." }, { imageUrl: 'https://ky008ymy6s.ufs.sh/f/NFGlOqM3XnMdZrkXcU6sM1fhWrqco2HGsiP6knYBUV47mRvA', rank: 10, name: 'Lionel Messi', description: "Unstoppable force, unrivaled skill - Messi's legacy is built on precision, perseverance, and a relentless drive to redefine greatness on the field." }, { imageUrl: 'https://ky008ymy6s.ufs.sh/f/NFGlOqM3XnMddKZzHaIWPBmp7t6OxSiRdMrTJ4l3g1KX0fqj', rank: 7, name: 'Cristiano Ronaldo', description: "Legendary striker, Ronaldo's career is a testament to relentless ambition, unmatched talent, and a relentless pursuit of excellence. His journey is a story of resilience, determination, and a relentless drive to redefine greatness on the field." } ]; return ( <div className="h-[700px] flex flex-col items-start justify-start lg:items-center lg:justify-center w-full gap-6"> <p className="md:text-2xl font-bold w-full text-center lg:mt-0 mt-10 "> The greatest to have ever played </p> <div className="grid grid-cols-1 sm:grid-cols-2 lg:flex lg:flex-row w-full items-center justify-center gap-6"> {cards.map((card, index) => ( <div className="w-full lg:w-fit flex items-center justify-center"> <TradingCard key={index} {...card} /> </div> ))} </div> </div> ); }; export default TradingCardDemo;
Loading...