Trading Card
A 3D interactive trading card with perspective transforms that respond to mouse movement. Features smooth reveal animations and a premium holographic feel.
Install using CLI
npx shadcn@latest add "https://obsidianui.dev/r/trading-card.json"Install Manually
1
Install dependencies
2
Copy the source code
Copy into components/ui/trading-card.tsx
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 onMouseEnter = () => {
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 });
};
const onMouseMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
const mx = e.clientX - rect.left;
const my = e.clientY - rect.top;
const xd = (mx - rect.width / 2) / 10;
const yd = (rect.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}
className="flex flex-col hover:scale-105 transition-all items-start justify-end rounded-lg relative shadow-xl overflow-hidden h-[400px] w-[300px] cursor-pointer border 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' }}
>
<Image src={imageUrl} alt={name} fill className="object-cover" />
</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 flex flex-col justify-end z-10"
>
<div className="text-4xl font-bold">{name}</div>
<p className="text-sm text-white/90">{description}</p>
</motion.div>
</motion.div>
);
};
export default TradingCard;Props
| Prop Name | Type | Default | Description |
|---|---|---|---|
| imageUrl | string | - | URL of the card background image |
| rank | number | - | Rank number displayed in corner |
| name | string | - | Name/title of the card |
| description | string | - | Description text |
