
Créer une animation de liste UL avec Framer Motion sur React et Next.js
Dans ce tutoriel, je vous montre comment créer une liste d'éléments animés en utilisant Framer Motion, une bibliothèque que j'utilise régulièrement dans mes projets pour ajouter des interactions et animations fluides. Ce code est conçu pour Next.js, mais je vous explique les quelques modifications nécessaires pour le rendre compatible avec React.
Étape 1 : Importer les dépendances
Framer Motion vous permet d'animer vos composants en quelques lignes. Commencez par installer Framer Motion si ce n’est pas déjà fait :
npm install framer-motion
Ensuite, importez les composants nécessaires au début de votre fichier.
"use client"; // Utilisé pour Next.js
import React, { useState } from "react";
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
import Image from "next/image"; // Utilisé pour Next.js.
Étape 2 : Configurer la liste d’éléments
Créez une liste d’objets pour représenter les éléments de votre liste. Ici, chaque élément a un id, un name, une description, et une descriptionLarge.
const elementsList = [
{
id: 1,
name: "Élément 1",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 1...",
},
{
id: 2,
name: "Élément 2",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 2...",
},
{
id: 3,
name: "Élément 3",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 3...",
},
];
Étape 3 : Gérer l'état de l'élément sélectionné
Pour gérer l'affichage de la description détaillée d’un élément, créez un useState pour stocker l'élément sélectionné :
const [selectedId, setSelectedId] = useState(null);
Étape 4 : Afficher et Animer la Liste
Créez une liste <ul> qui contiendra chaque élément <li>. On utilise motion.li pour animer chaque élément lors du survol.
<motion.ul className="flex items-center gap-4">
<LayoutGroup>
{elementsList.map((element, index) => (
<motion.li
key={index}
onClick={() => setSelectedId(element.id)}
layoutId={element.id}
whileHover={{ scale: 1.06 }}
className="flex cursor-pointer flex-col gap-2 rounded border bg-gray-200 p-4 shadow-md"
>
<div className="flex h-20 w-full items-center justify-center rounded bg-gray-400">
<Image src={"/image.svg"} width={30} height={30} alt="Image" />
</div>
<h2 className="text-base font-bold">{element.name}</h2>
<p>{element.description}</p>
</motion.li>
))}
</LayoutGroup>
</motion.ul>
Explications
- layoutId={element.id} : assure que chaque élément ait une transition fluide lors du changement d’état.
- whileHover={{ scale: 1.06 }} : agrandit légèrement l’élément au survol.
- <LayoutGroup> : garde la mise en page synchronisée et permet des animations cohérentes.
Étape 5 : Afficher les détails de l'élément sélectionné
Lorsqu'un élément est cliqué, on utilise AnimatePresence pour gérer l’affichage de la carte détaillée en superposition.
<AnimatePresence>
{selectedId && (
<div
className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-black bg-opacity-30"
onClick={() => setSelectedId(null)}
>
<motion.div
layoutId={selectedId}
className="flex w-11/12 max-w-lg flex-col gap-4 rounded-lg bg-gray-200 p-6 shadow-lg"
animate={{ opacity: 1 }}
transition={{ duration: 0.6, type: "spring" }}
>
<div className="flex h-36 items-center justify-center bg-gray-400">
<Image src={"/image.svg"} width={30} height={30} alt="Image" />
</div>
<h2 className="text-xl font-bold">
{elementsList[selectedId - 1].name}
</h2>
<p className="text-gray-700">
{elementsList[selectedId - 1].descriptionLarge}
</p>
</motion.div>
</div>
)}
</AnimatePresence>
Explications
- AnimatePresence : permet de gérer l’entrée et la sortie animée des composants.
- layoutId={selectedId} : synchronise l’animation entre la vue réduite de l’élément et la vue détaillée.
- onClick={() => setSelectedId(null)} : ferme la carte en cliquant sur l’arrière-plan.
Code Complet
Voici le code complet de l’animation de liste avec Framer Motion :
"use client";
import React, { useState } from "react";
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
import Image from "next/image";
const FramerList = () => {
const elementsList = [
{
id: 1,
name: "Élément 1",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 1...",
},
{
id: 2,
name: "Élément 2",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 2...",
},
{
id: 3,
name: "Élément 3",
description: "Cliquez pour voir plus.",
descriptionLarge: "Description détaillée de l'élément 3...",
},
];
const [selectedId, setSelectedId] = useState(null);
return (
<div className="flex flex-col gap-6">
<motion.ul className="flex items-center gap-4">
<LayoutGroup>
{elementsList.map((element, index) => (
<motion.li
key={index}
onClick={() => setSelectedId(element.id)}
layoutId={element.id}
whileHover={{ scale: 1.06 }}
className="flex cursor-pointer flex-col gap-2 rounded border bg-gray-200 p-4 shadow-md"
>
<div className="flex h-20 w-full items-center justify-center rounded bg-gray-400">
<Image src={"/image.svg"} width={30} height={30} alt="Image" />
</div>
<h2 className="text-base font-bold">{element.name}</h2>
<p>{element.description}</p>
</motion.li>
))}
<AnimatePresence>
{selectedId && (
<div
className="fixed left-0 top-0 z-50 flex h-full w-full items-center justify-center bg-black bg-opacity-30"
onClick={() => setSelectedId(null)}
>
<motion.div
layoutId={selectedId}
className="flex w-11/12 max-w-lg flex-col gap-4 rounded-lg bg-gray-200 p-6 shadow-lg"
animate={{ opacity: 1 }}
transition={{ duration: 0.6, type: "spring" }}
>
<div className="flex h-36 items-center justify-center bg-gray-400">
<Image src={"/image.svg"} width={30} height={30} alt="Image" />
</div>
<h2 className="text-xl font-bold">
{elementsList[selectedId - 1].name}
</h2>
<p className="text-gray-700">
{elementsList[selectedId - 1].descriptionLarge}
</p>
</motion.div>
</div>
)}
</AnimatePresence>
</LayoutGroup>
</motion.ul>
</div>
);
};
export default FramerList;
Conclusion
En quelques étapes, vous avez une liste interactive et animée avec Framer Motion et Next.js ! Ce tutoriel vous montre comment intégrer des animations facilement. Pour ceux utilisant React, remplacez simplement le composant Image de Next.js par une balise <img>. Amusez-vous à adapter le code à vos projets, et explorez la documentation de Framer Motion pour découvrir plus d’options d’animation.

Alexis Pennel
Publié le 18 décembre 2024
Autres posts qui pourraient vous plaire
Composant de cartes avec skeleton loader en React.js et Tailwind CSS
Barre de recherche avec React et Framer Motion
Créer un Header Responsive avec Menu Burger en Framer Motion
Créez des Animations Fluides avec Framer Motion en React