React Game UI
Production-ready UI patterns for game interfaces using shadcn/ui, Tailwind, and Framer Motion.
Core Components Resource Bar import { cn } from '@/lib/utils'; import { motion, AnimatePresence } from 'framer-motion';
interface ResourceBarProps { icon: React.ReactNode; value: number; maxValue?: number; label: string; color?: 'gold' | 'green' | 'blue' | 'red'; }
const colorMap = { gold: 'from-amber-400 to-yellow-500', green: 'from-emerald-400 to-green-500', blue: 'from-sky-400 to-blue-500', red: 'from-rose-400 to-red-500', };
export function ResourceBar({ icon, value, maxValue, label, color = 'gold' }: ResourceBarProps) { return (
Animated Counter import { useEffect, useRef } from 'react'; import { motion, useSpring, useTransform } from 'framer-motion';
interface AnimatedCounterProps { value: number; duration?: number; className?: string; }
export function AnimatedCounter({ value, duration = 0.5, className }: AnimatedCounterProps) { const spring = useSpring(0, { duration: duration * 1000 }); const display = useTransform(spring, (v) => Math.floor(v).toLocaleString());
useEffect(() => { spring.set(value); }, [spring, value]);
return
Meter/Progress Component interface MeterProps { value: number; // 0-100 label: string; tier?: 'mini' | 'major' | 'grand'; showValue?: boolean; }
const tierStyles = { mini: 'h-2', major: 'h-3', grand: 'h-4', };
export function Meter({ value, label, tier = 'mini', showValue = true }: MeterProps) { const clampedValue = Math.max(0, Math.min(100, value));
return (
${clampedValue}% }}
transition={{ type: 'spring', stiffness: 100, damping: 15 }}
/>
Game Card Component import { motion } from 'framer-motion';
interface GameCardProps { suit: 'hearts' | 'diamonds' | 'clubs' | 'spades'; rank: string; faceDown?: boolean; onClick?: () => void; }
export function GameCard({ suit, rank, faceDown = false, onClick }: GameCardProps) { const isRed = suit === 'hearts' || suit === 'diamonds';
return (
{/* Back */}
<div
className="absolute inset-0 rounded-lg shadow-lg backface-hidden bg-gradient-to-br from-indigo-600 to-purple-700"
style={{ transform: 'rotateY(180deg)' }}
>
<div className="w-full h-full rounded-lg border-4 border-white/20 flex items-center justify-center">
<div className="w-12 h-12 rounded-full bg-white/10" />
</div>
</div>
</motion.div>
</motion.div>
); }
function suitSymbol(suit: string) { const symbols = { hearts: '♥', diamonds: '♦', clubs: '♣', spades: '♠' }; return symbols[suit] || ''; }
Micro-Interactions Button with Feedback import { Button } from '@/components/ui/button'; import { motion } from 'framer-motion';
export function GameButton({
children,
onClick,
variant = 'default',
...props
}: React.ComponentProps
Coin Pop Animation import { motion, AnimatePresence } from 'framer-motion';
interface CoinPopProps { amount: number; position: { x: number; y: number }; onComplete: () => void; }
export function CoinPop({ amount, position, onComplete }: CoinPopProps) {
return (
Shake on Error const shakeAnimation = { x: [0, -10, 10, -10, 10, 0], transition: { duration: 0.4 } };
export function ShakeOnError({ error, children }) {
return (
Layout Patterns Game HUD Layout export function GameLayout({ children }: { children: React.ReactNode }) { return (
{/* Main Content */}
<main className="absolute inset-0 flex items-center justify-center pt-20 pb-20">
{children}
</main>
{/* Bottom Bar */}
<footer className="absolute bottom-0 left-0 right-0 z-20 p-4 flex justify-center">
<PhaseControls />
</footer>
{/* Side Panel */}
<aside className="absolute top-20 right-0 bottom-20 z-10 w-80 p-4">
<MetaPotPanel />
</aside>
</div>
); }
Responsive Game Board export function GameBoard({ children }: { children: React.ReactNode }) { return (
Modal & Dialog Patterns Game Modal with shadcn import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { motion, AnimatePresence } from 'framer-motion';
export function GameModal({ open, onOpenChange, title, children }) { return ( ); }
Tooltip for Game Elements import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip';
export function GameTooltip({ children, content }) {
return (
Accessibility for Games Skip Animation Preference import { useReducedMotion } from 'framer-motion';
export function AnimatedElement({ children }) { const shouldReduceMotion = useReducedMotion();
return (
Screen Reader Announcements import { useEffect, useRef } from 'react';
export function useAnnounce() {
const ref = useRef
const announce = (message: string) => { if (ref.current) { ref.current.textContent = message; } };
return { announce, AnnouncerRegion: () => (
)}; }// Usage const { announce, AnnouncerRegion } = useAnnounce(); announce('You won 500 coins!');
Performance Tips Memoize heavy components: React.memo() for hex cells, cards Use CSS transforms: GPU-accelerated vs layout-triggering properties Virtualize large lists: Only render visible items Debounce rapid updates: Score counters, resource bars Lazy load modals: Don't mount until needed
← 返回排行榜