Back to list
Smarter-Poker

animation-engine

by Smarter-Poker

Smarter-Poker-World-Hub

0🍴 0📅 Jan 26, 2026

SKILL.md


name: Animation Engine description: Implement fluid animations for poker gameplay and UI interactions

Animation Engine Skill

Overview

Create smooth, performant animations that bring the poker experience to life using Framer Motion, CSS animations, and GSAP.

Animation Libraries

Primary: Framer Motion

npm install framer-motion

Heavy Animations: GSAP

npm install gsap

3D: React Three Fiber

npm install @react-three/fiber @react-three/drei three

Card Animations

Card Deal

import { motion } from 'framer-motion';

function DealingCard({ card, seatIndex, delay }) {
  return (
    <motion.div
      className="card"
      initial={{ 
        x: 0, 
        y: 0, 
        scale: 0.5, 
        rotateY: 180,
        opacity: 0 
      }}
      animate={{ 
        x: SEAT_POSITIONS[seatIndex].x, 
        y: SEAT_POSITIONS[seatIndex].y, 
        scale: 1, 
        rotateY: 0,
        opacity: 1 
      }}
      transition={{ 
        duration: 0.4, 
        delay: delay * 0.1,
        ease: [0.25, 0.8, 0.25, 1]  // Custom bezier
      }}
    >
      <CardFace card={card} />
    </motion.div>
  );
}

Card Flip

function FlippingCard({ card, isRevealed }) {
  return (
    <motion.div
      className="card-container"
      style={{ perspective: 1000 }}
    >
      <motion.div
        className="card-inner"
        animate={{ rotateY: isRevealed ? 0 : 180 }}
        transition={{ duration: 0.6, ease: "easeInOut" }}
        style={{ transformStyle: 'preserve-3d' }}
      >
        <div className="card-front">
          <CardFace card={card} />
        </div>
        <div className="card-back">
          <CardBack />
        </div>
      </motion.div>
    </motion.div>
  );
}

Chip Animations

Chip to Pot

function ChipsToPot({ amount, fromSeat, onComplete }) {
  const controls = useAnimation();
  
  useEffect(() => {
    controls.start({
      x: POT_POSITION.x - SEAT_POSITIONS[fromSeat].x,
      y: POT_POSITION.y - SEAT_POSITIONS[fromSeat].y,
      scale: 0.5,
      transition: { duration: 0.4, ease: "easeOut" }
    }).then(onComplete);
  }, [amount]);
  
  return (
    <motion.div 
      className="chip-stack"
      animate={controls}
      initial={{ x: 0, y: 0, scale: 1 }}
    >
      <ChipStack amount={amount} />
    </motion.div>
  );
}

Pot to Winner

function PotToWinner({ amount, winnerSeat }) {
  return (
    <motion.div
      className="winning-pot"
      initial={{ 
        x: POT_POSITION.x, 
        y: POT_POSITION.y,
        scale: 1 
      }}
      animate={{
        x: SEAT_POSITIONS[winnerSeat].x,
        y: SEAT_POSITIONS[winnerSeat].y,
        scale: 1.2
      }}
      transition={{
        duration: 0.6,
        ease: [0.34, 1.56, 0.64, 1]  // Bouncy
      }}
    >
      <ChipStack amount={amount} />
      <motion.div
        className="win-glow"
        animate={{ 
          scale: [1, 1.5, 1],
          opacity: [0.5, 1, 0]
        }}
        transition={{ duration: 1, repeat: 2 }}
      />
    </motion.div>
  );
}

UI Animations

Button Interactions

const ActionButton = motion(Button);

function BetButton({ onClick, children }) {
  return (
    <ActionButton
      onClick={onClick}
      whileHover={{ scale: 1.05, boxShadow: "0 0 20px rgba(0,245,255,0.5)" }}
      whileTap={{ scale: 0.95 }}
      transition={{ duration: 0.15 }}
    >
      {children}
    </ActionButton>
  );
}

Page Transitions

import { AnimatePresence, motion } from 'framer-motion';
import { useRouter } from 'next/router';

function PageTransition({ children }) {
  const router = useRouter();
  
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={router.pathname}
        initial={{ opacity: 0, x: 20 }}
        animate={{ opacity: 1, x: 0 }}
        exit={{ opacity: 0, x: -20 }}
        transition={{ duration: 0.3 }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  );
}

Staggered Lists

function PlayerList({ players }) {
  return (
    <motion.ul
      initial="hidden"
      animate="visible"
      variants={{
        visible: {
          transition: { staggerChildren: 0.1 }
        }
      }}
    >
      {players.map((player) => (
        <motion.li
          key={player.id}
          variants={{
            hidden: { opacity: 0, y: 20 },
            visible: { opacity: 1, y: 0 }
          }}
        >
          <PlayerCard player={player} />
        </motion.li>
      ))}
    </motion.ul>
  );
}

Timer Animation

Action Timer with Color Shift

function ActionTimer({ duration, onExpire }) {
  const [progress, setProgress] = useState(1);
  
  useEffect(() => {
    const start = Date.now();
    const interval = setInterval(() => {
      const elapsed = (Date.now() - start) / 1000;
      const remaining = 1 - (elapsed / duration);
      
      if (remaining <= 0) {
        clearInterval(interval);
        onExpire();
      } else {
        setProgress(remaining);
      }
    }, 50);
    
    return () => clearInterval(interval);
  }, [duration]);
  
  const color = progress > 0.5 ? '#00ff88' : progress > 0.2 ? '#ffd700' : '#ff4757';
  
  return (
    <motion.div 
      className="timer-ring"
      style={{
        background: `conic-gradient(${color} ${progress * 360}deg, transparent 0)`
      }}
      animate={{ 
        boxShadow: progress < 0.2 
          ? ['0 0 10px #ff4757', '0 0 30px #ff4757', '0 0 10px #ff4757']
          : 'none'
      }}
      transition={{ duration: 0.5, repeat: Infinity }}
    />
  );
}

GSAP for Complex Sequences

import gsap from 'gsap';

function useWinAnimation(winnerRef, prizeAmount) {
  useEffect(() => {
    if (!winnerRef.current) return;
    
    const tl = gsap.timeline();
    
    tl.to(winnerRef.current, {
      scale: 1.1,
      duration: 0.3,
      ease: "power2.out"
    })
    .to(winnerRef.current, {
      boxShadow: "0 0 50px #ffd700",
      duration: 0.5
    })
    .to(".chips", {
      y: -50,
      opacity: 0,
      stagger: 0.05,
      duration: 0.4
    }, "-=0.3")
    .to(winnerRef.current, {
      scale: 1,
      boxShadow: "0 0 10px #ffd700",
      duration: 0.5,
      delay: 1
    });
    
    return () => tl.kill();
  }, [prizeAmount]);
}

Performance Best Practices

1. Use transform and opacity only

/* GOOD - GPU accelerated */
.animate { transform: translateX(100px); opacity: 0.5; }

/* BAD - Triggers layout */
.animate { left: 100px; width: 200px; }

2. Use will-change sparingly

.card-animating { will-change: transform; }

3. Use layoutId for shared element transitions

<motion.div layoutId={`card-${card.id}`}>
  <Card card={card} />
</motion.div>

4. Reduce motion for accessibility

const prefersReducedMotion = usePrefersReducedMotion();

<motion.div
  animate={{ x: 100 }}
  transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
/>

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon