Files
2nd/10_Wiki/Topics/Coding/Frontend_Animation_Motion.md
T
2026-05-09 21:08:02 +09:00

6.9 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
frontend-animation-motion Animation — Motion / GSAP / View Transitions Coding draft B conceptual 2026-05-09 2026-05-09
frontend
animation
motion
gsap
vibe-coding
language applicable_to
TS / React
Frontend
Framer Motion
Motion
GSAP
View Transitions API
CSS animation
Web Animations API

Frontend Animation

Modern stack: Motion (Framer Motion 후속) / GSAP / View Transitions API. CSS / Web Animations API / RequestAnimationFrame. 60fps + a11y prefers-reduced-motion.

📖 핵심 개념

  • Declarative (Motion / Framer): React 친화.
  • Imperative (GSAP / WAAPI): 강력 / 정밀.
  • View Transitions: 페이지 전환 자동 (browser native).
  • Hardware-accelerated: transform / opacity 만.

💻 코드 패턴

Motion (Framer Motion 후속)

import { motion } from 'motion/react';

<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  exit={{ opacity: 0 }}
  transition={{ duration: 0.3, ease: 'easeOut' }}
>
  Content
</motion.div>

// Variants
const variants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { staggerChildren: 0.1 } },
};

<motion.ul variants={variants} initial="hidden" animate="visible">
  {items.map(i => (
    <motion.li key={i.id} variants={{ hidden: { opacity: 0, x: -20 }, visible: { opacity: 1, x: 0 } }}>
      {i.text}
    </motion.li>
  ))}
</motion.ul>

Layout animation (자동)

<motion.div layout>
  {/* size / position 변경 자동 animate */}
</motion.div>

<AnimatePresence mode="popLayout">
  {items.map(i => (
    <motion.div key={i.id} layout exit={{ opacity: 0 }}>
      {i.text}
    </motion.div>
  ))}
</AnimatePresence>

Gesture

<motion.div
  drag dragConstraints={{ left: 0, right: 200 }}
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
  whileDrag={{ scale: 1.1 }}
>
  Drag me
</motion.div>

Spring physics

<motion.div
  animate={{ x: 100 }}
  transition={{ type: 'spring', stiffness: 200, damping: 20 }}
/>

GSAP (강력 / 복잡)

import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

// Timeline
const tl = gsap.timeline();
tl.to('.box', { x: 100, duration: 1 })
  .to('.box', { y: 100, duration: 0.5 })
  .to('.box', { opacity: 0, duration: 0.3 });

// Scroll trigger
gsap.to('.parallax', {
  y: -200,
  scrollTrigger: {
    trigger: '.section',
    start: 'top top',
    end: 'bottom top',
    scrub: 1,
  },
});

// React useGSAP
import { useGSAP } from '@gsap/react';

function Component() {
  const ref = useRef(null);
  useGSAP(() => {
    gsap.from('.item', { y: 50, opacity: 0, stagger: 0.1 });
  }, { scope: ref });
  
  return <div ref={ref}>...</div>;
}

View Transitions API (browser native)

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
}

::view-transition-group(card) {
  animation: cardMorph 0.5s;
}
<img style="view-transition-name: hero" src="...">

→ 페이지 / view 변경이 부드럽게.

// SPA 사용
async function navigate(url: string) {
  if (!document.startViewTransition) return goto(url);
  
  document.startViewTransition(async () => {
    await goto(url);
  });
}

→ Chrome / Edge / Safari 17.4+. Polyfill X.

CSS animations (가벼운)

.fade-in {
  animation: fadeIn 0.3s ease-out forwards;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .fade-in { animation: none; }
}
<div className="fade-in">...</div>

→ Build 작은 + browser 빠름.

Web Animations API (modern, vanilla)

const el = document.querySelector('.box')!;
const anim = el.animate(
  [
    { opacity: 0, transform: 'translateY(20px)' },
    { opacity: 1, transform: 'translateY(0)' },
  ],
  { duration: 300, easing: 'ease-out', fill: 'forwards' }
);

await anim.finished;

a11y — prefers-reduced-motion

import { useReducedMotion } from 'motion/react';

function Component() {
  const reduced = useReducedMotion();
  return (
    <motion.div
      animate={{ x: reduced ? 0 : 100 }}
    />
  );
}
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

→ 사용자가 setting 으로 끄면 응답.

Performance — transform / opacity 만

/* ✅ 60fps (GPU) */
.move { transform: translateX(100px); }
.fade { opacity: 0.5; }

/* ❌ Reflow (CPU) */
.bad { left: 100px; }
.bad { width: 200px; }
.bad { margin-top: 50px; }

will-change: transform (cautious — 메모리).

FLIP technique (자체 구현 layout animation)

// First — 시작 위치
const start = el.getBoundingClientRect();
// Last — 변경 후 위치
performLayoutChange();
const end = el.getBoundingClientRect();
// Invert — 차이 적용
const dx = start.left - end.left;
el.style.transform = `translateX(${dx}px)`;
// Play — 0 으로
requestAnimationFrame(() => {
  el.style.transition = 'transform 0.3s';
  el.style.transform = '';
});

→ Motion 의 layout 이 FLIP 자동.

Lottie (designer 친화)

npm install lottie-react
import Lottie from 'lottie-react';
import animationData from './animation.json';

<Lottie animationData={animationData} loop play />

→ After Effects 에서 export. 풍부 애니메이션.

큰 list animation

// AnimatePresence + 큰 list = 느림
// 작게 또는 virtualize + minimal animation

Staggered (timeline-like)

<motion.div
  variants={{
    visible: { transition: { staggerChildren: 0.05, delayChildren: 0.2 } },
  }}
  initial="hidden"
  animate="visible"
>
  {items.map(i => (
    <motion.div key={i.id} variants={itemVariants}>
      {i.text}
    </motion.div>
  ))}
</motion.div>

🤔 의사결정 기준

상황 추천
React 일반 Motion (Framer 후속)
매우 복잡 / 강력 GSAP
간단 (전환 / fade) CSS animation
Vanilla / 가벼움 WAAPI
페이지 전환 View Transitions API
Designer animation Lottie
Skia / canvas Reanimated (RN) / Pixi (web)

안티패턴

  • width / left animation: reflow — jank.
  • prefers-reduced-motion 무시: a11y / 멀미.
  • 너무 길음 (1초+ 일반): 사용자 지루.
  • 모든 component animate: noise.
  • Lottie 거대 JSON (500KB+): bundle 큼.
  • GSAP transition 매번 cleanup 안 함: leak.
  • CSS animation + JS animation 같은 element: 충돌.

🤖 LLM 활용 힌트

  • Motion = React 표준 (variants, layout, AnimatePresence).
  • GSAP = 강력 timeline / scroll.
  • View Transitions = page transition.
  • prefers-reduced-motion 항상.

🔗 관련 문서