Framer Motion ist eine deklarative Animations-Bibliothek für React, die physikbasierte Spring-Animationen, einfache Gesten-Unterstützung und komponentenübergreifende Übergänge in das React-Ökosystem integriert.
Was ist Framer Motion?
Framer Motion wurde von Framer – dem Prototyping-Tool-Unternehmen – als eigenständige Open-Source-Bibliothek veröffentlicht. Es ist heute die meistgenutzte Animations-Bibliothek im React-Ökosystem.
Das Besondere: Framer Motion folgt dem deklarativen Paradigma von React. Statt gsap.to(element, {x: 100}) schreibt man <motion.div animate={{ x: 100 }}>. Die Bibliothek übernimmt intern die Optimierung via WAAPI und CSS Custom Properties.
Version 12 (2024) führte neue Features wie animate() als eigenständige Funktion, verbesserte Layout-Animationen und bessere TypeScript-Typen ein.
Erklärung
Installation und Grundkonzept
``bash npm install framer-motion ``
```jsx import { motion } from 'framer-motion';
// Jedes HTML-Element hat ein motion-Äquivalent <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.4, ease: 'easeOut' }} > Animierter Inhalt </motion.div> ```
motion.div, motion.span, motion.button usw. sind direkte Drop-in-Ersetzungen für normale HTML-Elemente.
Variants: Zustände zentralisieren
Variants erlauben die Zentralisierung von Animationszuständen und Weitergabe an Kinder:
```jsx const containerVariants = { versteckt: { opacity: 0 }, sichtbar: { opacity: 1, transition: { staggerChildren: 0.08, // Kinder gestaffelt delayChildren: 0.2 } } };
const elementVariants = { versteckt: { opacity: 0, y: 30 }, sichtbar: { opacity: 1, y: 0 } };
function AnimierteList({ items }) { return ( <motion.ul variants={containerVariants} initial="versteckt" animate="sichtbar" > {items.map(item => ( <motion.li key={item.id} variants={elementVariants}> {item.text} </motion.li> ))} </motion.ul> ); } ```
Kinder-Elemente erben die initial und animate-Prop vom Parent – man muss sie nicht wiederholen.
Spring-Animationen
Framer Motion's größte Stärke sind physikbasierte Spring-Animationen:
```jsx // Type "spring" für federnde Bewegungen <motion.div animate={{ x: 200 }} transition={{ type: 'spring', stiffness: 300, // Federsteifigkeit (höher = schneller) damping: 25, // Dämpfung (niedriger = mehr Federn) mass: 1 // Masse des Objekts }} />
// Vordefinierte Spring-Presets transition={{ type: 'spring', bounce: 0.4 }} // 0 = kein Bounce, 1 = viel
// Snap: Abrupt, kein Federschwingen transition={{ type: 'spring', damping: 100 }} ```
AnimatePresence: Exit-Animationen
Das Herausragende an Framer Motion ist AnimatePresence – es ermöglicht Exit-Animationen, bevor ein Komponente aus dem DOM entfernt wird:
```jsx import { AnimatePresence, motion } from 'framer-motion';
function Modal({ istOffen, zuSchliessen }) { return ( <AnimatePresence> {istOffen && ( <motion.div key="modal" className="modal-overlay" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={zuSchliessen} > <motion.div className="modal-inhalt" initial={{ scale: 0.9, y: 40 }} animate={{ scale: 1, y: 0 }} exit={{ scale: 0.9, y: -20, opacity: 0 }} transition={{ type: 'spring', damping: 25 }} onClick={e => e.stopPropagation()} > {/ Modal-Inhalt /} </motion.div> </motion.div> )} </AnimatePresence> ); } ```
useAnimation Hook
```jsx import { useAnimation } from 'framer-motion';
function AnimiertesElement() { const controls = useAnimation();
async function starteSequenz() { await controls.start({ x: 100, opacity: 0.5 }); await controls.start({ x: 200, opacity: 1 }); controls.start({ x: 0 }); }
return ( <motion.div animate={controls} onClick={starteSequenz}> Klick mich </motion.div> ); } ```
Scroll-Animationen (useInView, useScroll)
```jsx import { motion, useInView, useScroll, useTransform } from 'framer-motion'; import { useRef } from 'react';
// Element bei Scroll einblenden function ScrollElement() { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: '-100px' });
return ( <motion.div ref={ref} initial={{ opacity: 0, y: 60 }} animate={isInView ? { opacity: 1, y: 0 } : {}} transition={{ duration: 0.6, ease: 'easeOut' }} > Erscheint beim Scrollen </motion.div> ); }
// Parallax mit useScroll/useTransform function ParallaxHero() { const { scrollY } = useScroll(); const y = useTransform(scrollY, [0, 500], [0, -150]);
return ( <div className="hero"> <motion.img src="/hintergrund.jpg" style={{ y }} /> </div> ); } ```
Layout-Animationen
```jsx // Automatische Animation bei Layout-Änderungen import { motion, LayoutGroup } from 'framer-motion';
function DynamischeList({ items, sortierung }) { return ( <LayoutGroup> {items.map(item => ( <motion.div key={item.id} layout // Animiert automatisch bei Position-Änderung transition={{ type: 'spring', damping: 20 }} > {item.text} </motion.div> ))} </LayoutGroup> ); } ```
Beispiele
Tab-Navigation mit shared layout
```jsx function Tabs({ tabs }) { const [aktiv, setAktiv] = useState(tabs[0].id);
return ( <div className="tabs"> {tabs.map(tab => ( <button key={tab.id} onClick={() => setAktiv(tab.id)} style={{ position: 'relative' }} > {tab.label} {aktiv === tab.id && ( <motion.div className="tab-indikator" layoutId="tab-indikator" // Gleiches layoutId = shared layout style={{ position: 'absolute', bottom: 0, left: 0, right: 0 }} /> )} </button> ))} </div> ); } ```
Gestensteuerung: Drag und Swipe
``jsx <motion.div drag="x" dragConstraints={{ left: -100, right: 100 }} dragElastic={0.1} whileDrag={{ scale: 1.05 }} onDragEnd={(event, { velocity, offset }) => { if (Math.abs(offset.x) > 100) { // Swipe-Aktion ausführen naechsteKarte(); } }} > Wischbare Karte </motion.div> ``
In der Praxis
prefers-reduced-motion in Framer Motion:
```jsx import { useReducedMotion } from 'framer-motion';
function AnimiertesElement() { const reduzierteBewegung = useReducedMotion();
return ( <motion.div initial={{ opacity: 0, y: reduzierteBewegung ? 0 : 40 }} animate={{ opacity: 1, y: 0 }} > Inhalt </motion.div> ); } ```
Vergleich & Abgrenzung
| Framer Motion | GSAP (GreenSock) – Grundlagen | Web Animations API (WAAPI) | |
|---|---|---|---|
| React-Integration | Native | Extern | Extern |
| Exit-Animationen | AnimatePresence | Manuell | Manuell |
| Spring-Physik | Exzellent | Mit Elastic-Ease | Nein |
| Layout-Animationen | Automatisch | Manuell | Manuell |
| Nicht-React | Nein | Ja | Nativ |
Häufige Fragen (FAQ)
Kann ich Framer Motion ohne React nutzen? Die Library ist ausschließlich für React. Für andere Frameworks: Vue mit vue3-motion oder Motion One (JS-Library von Framer Motion Autor Matt Perry).
Wie groß ist Framer Motion? ~50 KB gzip. Die Tree-shaking-Unterstützung ist gut – nur genutzte Features werden eingebunden.
Was ist Motion One? Motion One (von Matt Perry) ist eine ~3 KB kleine Alternative für Vanilla-JS-Projekte, die die WAAPI mit einer Framer-Motion-ähnlichen API abstrahiert.
Verwandte Einträge
- Web Animations API (WAAPI) – Die native API, auf der Framer Motion intern aufbaut
- Page Transitions im Web – Framer Motion für Page Transitions in React
- GSAP (GreenSock) – Grundlagen – Alternative für React und andere Frameworks
- Scrollytelling – Animation beim Scrollen – Scroll-Animationen mit Framer Motion
Weiterführend
- Framer Motion: Dokumentation (2024) – framer.com/motion
- Matt Perry: Framer Motion 5 Release (2021) – framer.com/blog
- Josh W. Comeau: A Friendly Introduction to Spring Physics (2021) – joshwcomeau.com
- Motion One: motion.dev (Matt Perry, 2021) – kleineres WAAPI-Wrapper
- Framer Motion Animations Course: ui.dev/framer-motion
