import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
export default function App() {
const [selectedId, setSelectedId] = useState<number | null>(null);
const [items, setItems] = useState([1, 2, 3, 4]);
const [isVisible, setIsVisible] = useState(true);
const containerStyle: React.CSSProperties = {
minHeight: '100vh',
background: 'linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)',
padding: '40px 20px',
fontFamily: 'system-ui, sans-serif',
};
const sectionStyle: React.CSSProperties = {
maxWidth: '800px',
margin: '0 auto 40px',
};
const titleStyle: React.CSSProperties = {
color: '#fff',
fontSize: '24px',
marginBottom: '20px',
textAlign: 'center',
};
const gridStyle: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
gap: '20px',
marginBottom: '30px',
};
const cardStyle: React.CSSProperties = {
background: 'linear-gradient(145deg, #e94560, #ff6b6b)',
borderRadius: '16px',
padding: '30px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '32px',
fontWeight: 'bold',
color: '#fff',
boxShadow: '0 10px 30px rgba(233, 69, 96, 0.3)',
};
const buttonStyle: React.CSSProperties = {
background: 'linear-gradient(145deg, #0f3460, #16213e)',
border: '2px solid #e94560',
color: '#fff',
padding: '12px 24px',
borderRadius: '8px',
cursor: 'pointer',
fontSize: '16px',
marginRight: '10px',
marginBottom: '10px',
};
const boxStyle: React.CSSProperties = {
width: '100px',
height: '100px',
background: 'linear-gradient(145deg, #00d9ff, #0f3460)',
borderRadius: '16px',
margin: '20px auto',
};
const overlayStyle: React.CSSProperties = {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0,0,0,0.8)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 100,
};
const expandedCardStyle: React.CSSProperties = {
...cardStyle,
width: '300px',
height: '200px',
fontSize: '48px',
};
const addItem = () => {
const newId = Math.max(...items, 0) + 1;
setItems([...items, newId]);
};
const removeItem = (id: number) => {
setItems(items.filter(item => item !== id));
};
return (
<div style={containerStyle}>
<h1 style={{ ...titleStyle, fontSize: '36px', marginBottom: '40px' }}>
๐ฌ Framer Motion Demo
</h1>
{}
<section style={sectionStyle}>
<h2 style={titleStyle}>Hover & Tap Animations</h2>
<div style={{ display: 'flex', justifyContent: 'center', gap: '20px', flexWrap: 'wrap' }}>
<motion.div
style={boxStyle}
whileHover={{ scale: 1.2, rotate: 10 }}
whileTap={{ scale: 0.9 }}
transition={{ type: 'spring', stiffness: 300 }}
/>
<motion.div
style={{ ...boxStyle, background: 'linear-gradient(145deg, #e94560, #ff6b6b)' }}
whileHover={{ scale: 1.1, borderRadius: '50%' }}
whileTap={{ scale: 0.95 }}
transition={{ duration: 0.3 }}
/>
<motion.div
style={{ ...boxStyle, background: 'linear-gradient(145deg, #ffd700, #ff8c00)' }}
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Infinity, ease: 'linear' }}
/>
</div>
</section>
{}
<section style={sectionStyle}>
<h2 style={titleStyle}>AnimatePresence - Enter/Exit</h2>
<div style={{ textAlign: 'center' }}>
<motion.button
style={buttonStyle}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => setIsVisible(!isVisible)}
>
{isVisible ? 'Hide' : 'Show'} Box
</motion.button>
<AnimatePresence>
{isVisible && (
<motion.div
style={{ ...boxStyle, width: '150px' }}
initial={{ opacity: 0, y: -50, scale: 0.5 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 50, scale: 0.5, rotate: 180 }}
transition={{ type: 'spring', stiffness: 200, damping: 20 }}
/>
)}
</AnimatePresence>
</div>
</section>
{}
<section style={sectionStyle}>
<h2 style={titleStyle}>Layout Animations - Dynamic List</h2>
<div style={{ textAlign: 'center', marginBottom: '20px' }}>
<motion.button
style={buttonStyle}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={addItem}
>
โ Add Item
</motion.button>
</div>
<div style={gridStyle}>
<AnimatePresence>
{items.map((item) => (
<motion.div
key={item}
style={cardStyle}
layout
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.5, rotate: -10 }}
whileHover={{ scale: 1.05, y: -5 }}
whileTap={{ scale: 0.95 }}
onClick={() => setSelectedId(item)}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
>
{item}
</motion.div>
))}
</AnimatePresence>
</div>
</section>
{}
<section style={sectionStyle}>
<h2 style={titleStyle}>Drag Me! ๐๏ธ</h2>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<motion.div
style={{
...boxStyle,
width: '120px',
height: '120px',
cursor: 'grab',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '40px',
}}
drag
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.2}
whileDrag={{ scale: 1.1, cursor: 'grabbing' }}
>
๐ฏ
</motion.div>
</div>
</section>
{}
<AnimatePresence>
{selectedId && (
<motion.div
style={overlayStyle}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedId(null)}
>
<motion.div
style={expandedCardStyle}
layoutId={`card-${selectedId}`}
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.8, opacity: 0 }}
onClick={(e) => e.stopPropagation()}
>
<div style={{ textAlign: 'center' }}>
<div>{selectedId}</div>
<motion.button
style={{ ...buttonStyle, marginTop: '20px', fontSize: '14px' }}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => {
removeItem(selectedId);
setSelectedId(null);
}}
>
๐๏ธ Delete
</motion.button>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}