Créer des animations dignes d'Awwwards avec Framer Motion
Guide complet pour maîtriser Framer Motion et créer des animations premium qui impressionnent. De la théorie à la pratique avec des exemples concrets.
Introduction
Les animations web ne sont plus un luxe, elles sont devenues essentielles pour créer des expériences utilisateur mémorables. Framer Motion est la bibliothèque de référence pour React, et voici comment en tirer le maximum.
Les fondamentaux
Le composant motion
Tout commence par le composant motion. Il transforme n'importe quel élément HTML en élément animable :
import { motion } from 'framer-motion'
function FadeIn() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
Contenu animé
</motion.div>
)
}
Les propriétés clés
- initial : État de départ
- animate : État final
- exit : État de sortie (avec AnimatePresence)
- transition : Configuration de l'animation
- whileHover / whileTap : États interactifs
Animations avancées
Variants : orchestrer des animations complexes
Les variants permettent de créer des animations coordonnées entre parents et enfants :
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1, // Délai entre chaque enfant
},
},
}
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
}
function StaggeredList({ items }) {
return (
<motion.ul
variants={containerVariants}
initial="hidden"
animate="visible"
>
{items.map((item) => (
<motion.li key={item.id} variants={itemVariants}>
{item.name}
</motion.li>
))}
</motion.ul>
)
}
Animations au scroll avec useScroll
Créez des animations liées au défilement :
import { motion, useScroll, useTransform } from 'framer-motion'
function ParallaxHero() {
const { scrollYProgress } = useScroll()
const y = useTransform(scrollYProgress, [0, 1], [0, -200])
const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0])
const scale = useTransform(scrollYProgress, [0, 0.5], [1, 0.8])
return (
<motion.div style={{ y, opacity, scale }}>
<h1>Hero parallax</h1>
</motion.div>
)
}
Spring physics pour des animations naturelles
Les animations spring imitent la physique réelle :
<motion.div
animate={{ x: 100 }}
transition={{
type: "spring",
stiffness: 100, // Rigidité du ressort
damping: 10, // Amortissement
mass: 1, // Masse de l'élément
}}
/>
Patterns Awwwards
1. Text reveal character by character
const text = "Hello World"
function TextReveal() {
return (
<motion.div
initial="hidden"
animate="visible"
variants={{
visible: { transition: { staggerChildren: 0.05 } },
}}
>
{text.split("").map((char, i) => (
<motion.span
key={i}
variants={{
hidden: { opacity: 0, y: 50 },
visible: { opacity: 1, y: 0 },
}}
>
{char}
</motion.span>
))}
</motion.div>
)
}
2. Magnetic buttons
function MagneticButton({ children }) {
const ref = useRef(null)
const [position, setPosition] = useState({ x: 0, y: 0 })
const handleMouse = (e) => {
const { clientX, clientY } = e
const { left, top, width, height } = ref.current.getBoundingClientRect()
const x = (clientX - left - width / 2) * 0.3
const y = (clientY - top - height / 2) * 0.3
setPosition({ x, y })
}
const reset = () => setPosition({ x: 0, y: 0 })
return (
<motion.button
ref={ref}
onMouseMove={handleMouse}
onMouseLeave={reset}
animate={position}
transition={{ type: "spring", stiffness: 150, damping: 15 }}
>
{children}
</motion.button>
)
}
3. Page transitions
import { AnimatePresence } from 'framer-motion'
function Layout({ children }) {
return (
<AnimatePresence mode="wait">
<motion.main
key={pathname}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.main>
</AnimatePresence>
)
}
Performance
Optimisations essentielles
- Utilisez transform et opacity - Ces propriétés sont optimisées par le GPU
- Évitez les re-renders - Utilisez
useMotionValuepour les valeurs dynamiques - Layout animations - Utilisez
layoutIdpour des transitions fluides entre états
// ❌ Mauvais - cause des re-renders
const [x, setX] = useState(0)
<motion.div animate={{ x }} />
// ✅ Bon - pas de re-render
const x = useMotionValue(0)
<motion.div style={{ x }} />
Conclusion
Framer Motion offre des possibilités infinies pour créer des expériences web exceptionnelles. La clé est de commencer simple et d'itérer progressivement vers des animations plus complexes.
N'oubliez pas : une bonne animation améliore l'UX, une mauvaise la dégrade. Testez toujours sur différents appareils et respectez les préférences prefers-reduced-motion.