<
>
ETHIOCODE
SOFTSELECT
ሶፍትዌር ምርጫ ለኢትዮጵያ
P3 · L05 · FRAMER MOTION · ANIMATION · LOADING...
PHASE 3 · LESSON 05

Framer Motion —
አኒሜሽን በቀላሉ! ✨

CSS animation መፃፍ ሳያስፈልግ፣ ከ React ውጭ ሳንወጣ — motion.div ብቻ!
Spring physics፣ Variants፣ AnimatePresence፣ Gestures — ሁሉንም በአንድ ላይ! 🎬
The most popular production-ready motion library for React.

ያዘጋጀው: @AppMinds_ET
01Framer Motion ምንድን ነው?

Framer Motion — React ውስጥ animation ለመስራት የሚያገለግል production-ready library ነው! CSS keyframes መፃፍ ሳያስፈልግ፣ በ Spring physics እጅግ ማራኪ አኒሜሽኖችን መስራት ይቻላል። motion.divን በመጠቀም ብቻ አኒሜሽኑ ይጀምራል!
Declarative animations — describe what you want, Framer figures out how.

📦

npm install framer-motion
import { motion, AnimatePresence } from 'framer-motion'

❌ ያለ Framer — CSS animation
/* CSS */
@keyframes slideIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.card { animation: slideIn 0.5s ease; }

// ❌ Exit animation? የለም!
// ❌ Spring physics? በራስ መስራት ያሻል!
// ❌ Gesture? addEventListener ያስፈልጋል!
// ❌ Stagger? ሌላ logic ይጠይቃል!
✅ Framer Motion — ቀላሉ መንገድ!
import { motion } from 'framer-motion';

// ✅ motion. + ማንኛውም HTML element
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  exit={{ opacity: 0, y: -20 }}
  transition={{ type: 'spring', stiffness: 300 }}
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
/>
// ✅ Enter · Exit · Spring · Gesture — ሁሉም ተካተዋል!
02motion — ዋና Building Blocks
motion-components.tsx
MOTION PROPS
initial · animate
exit
transition
keyframes

initial — Component ሲጫን (mount ሲሆን) የሚጀምርበት ሁኔታ! animate — ደርሶ ማለቅ ያለበት ሁኔታ! React state ሲቀየር animate አብሮ ይቀየራል!

initial-animate.tsx
import { motion } from 'framer-motion';

// 🔵 Fade + Slide In
<motion.div
  initial={{ opacity: 0, y: 30 }}
  animate={{ opacity: 1, y: 0 }}
/>

// 🔵 Scale In (Pop effect)
<motion.div
  initial={{ scale: 0, opacity: 0 }}
  animate={{ scale: 1, opacity: 1 }}
/>

// 🔵 Rotate In
<motion.div
  initial={{ rotate: -180, opacity: 0 }}
  animate={{ rotate: 0, opacity: 1 }}
/>

// 🔵 State ሲቀየር animate አብሮ ይቀየራል!
<motion.div
  animate={{ x: isOpen ? 0 : -200 }}
/>

exit — Component ከ DOM ላይ ሲወገድ (Unmount ሲሆን) የሚታይ animation! ከ AnimatePresence ጋር ብቻ ነው የሚሰራው — ያለ CSS!

exit-animation.tsx
import { motion, AnimatePresence } from 'framer-motion';

// ⚠️ ከ AnimatePresence ጋር ብቻ ነው exit የሚሰራው!
function Modal({ isOpen }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          key="modal"
          initial={{ opacity: 0, scale: 0.85 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 0.85 }}  ← exit!
          transition={{ duration: 0.25 }}
        >
          Modal Content
        </motion.div>
      )}
    </AnimatePresence>
  );
}
// exit animation → ከ DOM ላይ ሲወገድ ሳይሆን አኒሜሽኑ ሲያልቅ ነው የሚወገደው!

transition — አኒሜሽኑ እንዴት እንደሚጓዝ ይወስናል! spring (ፊዚክስን የተከተለ) ወይም tween (የተለመደው easing) — ሁለቱ ዋና አይነቶች ናቸው!

transition.tsx
// 🌿 Spring — ተፈጥሯዊ Physics ይኖረዋል
<motion.div
  transition={{
    type:      'spring',
    stiffness: 300,  // ጠንካራነቱ? (100–1000)
    damping:   30,   // መቆሚያው? (0–100)
    mass:      1,    // ክብደቱ
  }}
/>

// 📐 Tween — linear/ease/easeIn...
<motion.div
  transition={{
    type:     'tween',
    duration: 0.5,     // ሰኮንድ
    ease:     'easeOut',// easeIn · easeOut · backOut
    delay:    0.2,     // ዘግይቶ ይጀምር
  }}
/>

// ⚡ Stagger — children አንድ በአንድ
<motion.ul
  variants={containerVariants}
  initial="hidden" animate="show"
/>
// containerVariants.show.transition.staggerChildren: 0.1

Keyframes — Animation ውስጥ ብዙ values በ array መልክ መስጠት! CSS @keyframes ያለ ቀላሉ መንገድ!

keyframes.tsx
// ✨ Values array = keyframes!
<motion.div
  animate={{
    x: [0, 100, 50, 200],
    // 0 → 100 → 50 → 200
  }}
/>

// 🔵 Bounce effect
<motion.div
  animate={{
    y: [0, -30, 0],
    scale: [1, 1.1, 1],
  }}
  transition={{
    duration: 0.6,
    ease: 'easeInOut',
    times: [0, 0.5, 1], // % of animation
    repeat: Infinity,
    repeatDelay: 1,
  }}
/>

// 🎨 Color keyframes
<motion.div
  animate={{ backgroundColor: ['#e879f9', '#a3e635', '#e879f9'] }}
  transition={{ duration: 2, repeat: Infinity }}
/>
03🎭 Variants — Animation State Machine
Variants Visualizer — State ጠቅ → ማብራሪያ ↓
variants={{ hidden, show, exit }}
👻
HIDDEN
SHOW
📋
STAGGER
🖱️
HOVER
🚪
EXIT
04🎮 Animation Playground — ራሳችሁ ሞክሩ!
🎛️ Motion Builder — Parameters ቀይሩ → ▶ Play ጠቅ አድርጉ
✅ READY
▷ PREVIEW STAGE
🎬 Animation Type
Fade+Slide
Scale
Rotate
Bounce
Flip
🌿 Transition Type
Spring
Tween
Inertia
⏱ Duration / Stiffness — 0.5s
0.5
🕐 Delay — 0s
0
📋 GENERATED CODE
05🚪 AnimatePresence — Enter/Exit Demo
AnimatePresence.tsx
DEMO
🎮 Live Demo
📋 Code
📦 List Animation

AnimatePresence — Component ከ DOM ከመወገዱ በፊት exit animation ሰርቶ እንዲጨርስ ያደርጋል! ያለ AnimatePresence የ exit prop አይሰራም!

🎬 Notification Stack
▷ Notification ጨምሩ →
🔄 Modal Toggle
▷ Modal Toggle ጠቅ ያድርጉ →
AnimatePresence-example.tsx
import { motion, AnimatePresence } from 'framer-motion';

// 🔔 Notification Stack
function NotifStack() {
  const [notifs, setNotifs] = useState([]);

  const add = (type) =>
    setNotifs(n => [...n, { id: Date.now(), type }]);

  const remove = (id) =>
    setNotifs(n => n.filter(x => x.id !== id));

  return (
    <AnimatePresence>
      {notifs.map(n => (
        <motion.div
          key={n.id}
          initial={{ opacity: 0, x: 50, scale: 0.9 }}
          animate={{ opacity: 1, x: 0,  scale: 1   }}
          exit={{ opacity: 0, x: 50, scale: 0.9 }}
          onClick={() => remove(n.id)}
        >
          {n.type === 'success' ? '✅' : '❌'}
        </motion.div>
      ))}
    </AnimatePresence>
  );
}
list-stagger.tsx
// 🎭 Stagger — List items አንድ በአንድ
const containerV = {
  hidden: {},
  show: {
    transition: {
      staggerChildren: 0.1,  // 100ms ልዩነት
      delayChildren:   0.2,  // ሁሉም ሲጀምሩ delay
    }
  }
};

const itemV = {
  hidden: { opacity: 0, y: 20 },
  show:   { opacity: 1, y: 0,
            transition: { type: 'spring', stiffness: 300 } }
};

<motion.ul
  variants={containerV}
  initial="hidden"
  animate="show"
>
  {items.map(item => (
    <motion.li
      key={item.id}
      variants={itemV}  // ← parent trigger ያደርገዋል
      exit={{ opacity: 0, x: -30 }}
    >
      {item.name}
    </motion.li>
  ))}
</motion.ul>
06👆 Gestures — whileHover · whileTap · drag

Framer Motion ውስጥ Gesture animations — whileHover (ማውስ ሲጠጋ)፣ whileTap (ሲጫን)፣ drag (ሲጎትት) — CSS ሳይጻፍ! ↓ ሞክሩ!

🖱️
whileHover
ማውስ ሲጠጋ animation!
whileHover={{ scale: 1.15 }}
👆
whileTap
ሲጫን/ሲታፕ animation!
whileTap={{ scale: 0.88 }}
🎯
drag
ሲጎትቱ ይንቀሳቀሳል!
drag dragConstraints={...}
🎪
whileFocus
Focus/Hover ሲሆን!
whileFocus={{ scale: 1.1 }}
whileInView
Viewport ሲታይ!
whileInView={{ opacity: 1 }}
const x = useMotionValue(0);
const bg = useTransform(x,
  [-100,100],
  ['#c026d3','#a3e635'])
useMotionValue
Value → auto animate!
useTransform(x, in, out)
07⚡ Production Ready — ሙሉ ምሳሌ
AnimatedCard.tsx — ሙሉ ምሳሌ
import { motion, AnimatePresence } from 'framer-motion';

// 1️⃣ Variants ፍጠር (Reusable!)
const cardV = {
  hidden: { opacity: 0, y: 30, scale: 0.95 },
  show:   { opacity: 1, y: 0,  scale: 1,
            transition: { type: 'spring', stiffness: 260, damping: 20 } },
  exit:   { opacity: 0, y: -20, scale: 0.95 }
};

const listV = {
  hidden: {},
  show:   { transition: { staggerChildren: 0.1 } }
};

// 2️⃣ Component
export function ProductList({ products, isOpen }) {
  return (
    <AnimatePresence mode="popLayout">
      {isOpen && (
        <motion.ul
          variants={listV}
          initial="hidden"
          animate="show"
          exit="exit"
        >
          {products.map(p => (
            <motion.li
              key={p.id}
              variants={cardV}
              layout               // ✅ List ሲቀየር smooth!
              whileHover={{ x: 6, backgroundColor: 'rgba(232,121,249,.08)' }}
              whileTap={{ scale: 0.98 }}
            >
              {p.name} — {p.price}
            </motion.li>
          ))}
        </motion.ul>
      )}
    </AnimatePresence>
  );
}

// 3️⃣ Page transition (Next.js App Router)
export default function Page() {
  return (
    <motion.main
      initial={{ opacity: 0, x: -20 }}
      animate={{ opacity: 1, x: 0 }}
      exit={{ opacity: 0, x: 20 }}
      transition={{ duration: 0.4, ease: 'easeOut' }}
    >
      {/* Page content */}
    </motion.main>
  );
}
📌ጠቅለል ያለ ማስታወሻ
1
🎬 motion.div — ዋና Building Block

ማንኛውም HTML element motion. ጨምሮ መፃፍ! initial, animate, exit props በመስጠት አኒሜሽኑን ሙሉ በሙሉ ትቆጣጠራላችሁ!

motion.div · motion.p · motion.img · motion.li — all supported!
2
🎭 Variants — Reusable Animation States

Animation config object ውጭ ፍጠር → variants prop ሰጥ! Parent-child stagger ለ list animations ምርጡ!

variants={{hidden, show, exit}} — trigger by name string.
3
🚪 AnimatePresence — Exit ለ UN-Mount

Component ከ DOM ሲወጣ exit animation እንዲሰራ ያደርጋል! key prop ያስፈልገዋል! mode="wait" ወይም "popLayout" መጠቀም ይቻላል!

Without AnimatePresence, exit prop is ignored entirely!
4
🌿 Spring vs Tween — Physics vs Math

type:'spring' + stiffness + damping = ተፈጥሯዊ እንቅስቃሴ ይኖረዋል! type:'tween' + duration + ease = ጊዜውን በትክክል ለመቆጣጠር! Default = spring ነው!

For UI: spring feels natural. For precise timing: tween.
5
👆 Gestures — CSS ሳይጻፍ

whileHover · whileTap · whileDrag · whileInView · whileFocus — props ብቻ በመስጠት! ምንም አይነት Event listener አያስፈልግም!

Framer handles pointer events, spring physics, and cleanup automatically.
🎯ለዕውቀት መፈተኛ Quiz!
❓ Component ከ DOM ሲወጣ exit animation እንዲሰራ ምን ያስፈልጋል?
What is required to run exit animations when a component unmounts in Framer Motion?
A
CSS @keyframes ጻፈ እና animation-fill-mode: both ጨምር
B
useEffect ውስጥ setTimeout ጋር className ቀይር
C
motion componentን <AnimatePresence> ውስጥ መጠቅለል + exit prop መስጠት ✅
D
whileHover={{ opacity:0 }} ብቻ ያስፈልጋል