Animations de pages

SmartCommon utilise Framer Motion pour animer les transitions entre pages. Les animations sont configurables par route.

Configuration de base

Dans la configuration du Provider :

const config = {
  pages: {
    "*": "fade" // Animation par défaut pour toutes les pages
  }
};

Animations disponibles

Animation Description Effet
fade Fondu enchaîné Opacité 0 -> 1
slideLeft Glissement gauche Entrée par la droite
slideRight Glissement droite Entrée par la gauche
zoom Effet de zoom Scale 0.9 -> 1

Configuration par route

Vous pouvez définir des animations différentes selon la route d'origine et de destination :

const config = {
  pages: {
    // Depuis la page d'accueil
    "/": {
      "/dashboard": "slideLeft",  // Vers dashboard: glisse à gauche
      "/settings": "slideLeft",   // Vers settings: glisse à gauche
      "*": "fade"                 // Vers autres: fondu
    },

    // Depuis le dashboard
    "/dashboard": {
      "/": "slideRight",          // Retour accueil: glisse à droite
      "/details": "slideLeft",    // Vers détails: glisse à gauche
      "*": "fade"
    },

    // Depuis les paramètres
    "/settings": {
      "/": "slideRight",
      "*": "fade"
    },

    // Pour toutes les autres pages
    "*": "fade"
  }
};

Logique de navigation

L'animation est choisie selon ce schéma :

  1. Cherche une config pour la page actuelle
  2. Si trouvée, cherche une animation pour la page précédente
  3. Si pas trouvée, utilise * de la page actuelle
  4. Si pas de config pour la page actuelle, utilise * global

Exemple

Navigation de /dashboard vers / :

pages: {
  "/": {
    "/dashboard": "slideRight", // <- Cette animation sera utilisée
    "*": "fade"
  },
  "/dashboard": {
    "/": "slideRight",
    "*": "fade"
  }
}

La page / s'affiche avec slideRight car on vient de /dashboard.

Désactiver les animations sur desktop

Par défaut, les animations de glissement sont remplacées par fade sur desktop pour une meilleure UX :

// Dans le composant Page (comportement interne)
if (device?.type === "desktop") {
  return "fade";
}

Utiliser le composant Page

Le composant Page gère automatiquement les animations :

import { Page, Block } from '@cap-rel/smartcommon';
import { useLocation } from 'react-router-dom';

const Dashboard = () => {
  const location = useLocation();

  return (
    <Page location={location}>
      <Block>
        Contenu du dashboard
      </Block>
    </Page>
  );
};

Important

Le prop location est requis pour que les animations fonctionnent correctement.

Personnaliser les animations

Vous pouvez créer des animations personnalisées en modifiant les variants Framer Motion :

// Animations par défaut dans SmartCommon
const animations = {
  slideRight: {
    initial: { x: "50%", opacity: 0 },
    animate: { x: 0, opacity: 1, transition: { duration: 0.15, ease: "easeInOut" } },
    exit: { x: "50%", opacity: 0, transition: { duration: 0.15, ease: "easeInOut" } }
  },
  slideLeft: {
    initial: { x: "-50%", opacity: 0 },
    animate: { x: 0, opacity: 1, transition: { duration: 0.15, ease: "easeInOut" } },
    exit: { x: "-50%", opacity: 0, transition: { duration: 0.15, ease: "easeInOut" } }
  },
  fade: {
    initial: { opacity: 0 },
    animate: { opacity: 1, transition: { duration: 0.15, ease: "easeOut" } },
    exit: { opacity: 0, transition: { duration: 0.15, ease: "easeOut" } }
  },
  zoom: {
    initial: { scale: 0.9, opacity: 0 },
    animate: { scale: 1, opacity: 1 },
    exit: { scale: 0.9, opacity: 0 },
    transition: { duration: 0.2, ease: "easeOut" }
  }
};

Hook useAnimation

Pour des animations personnalisées dans vos composants :

import { useAnimation } from '@cap-rel/smartcommon';

const MyComponent = () => {
  const { start, animations, setAnimations } = useAnimation({
    fadeIn: { value: false, state: null },
    slideIn: { value: false, state: null }
  });

  useEffect(() => {
    if (start) {
      // Déclencher l'animation après le premier rendu
      setAnimations(prev => ({
        ...prev,
        fadeIn: { ...prev.fadeIn, state: 'visible' }
      }));
    }
  }, [start]);

  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={animations.fadeIn.value ? { opacity: 1 } : {}}
    >
      Contenu animé
    </motion.div>
  );
};

Animations avec Framer Motion

Pour des animations plus complexes, utilisez directement Framer Motion :

import { motion, AnimatePresence } from 'framer-motion';

const MyList = ({ items }) => (
  <AnimatePresence>
    {items.map(item => (
      <motion.div
        key={item.id}
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: -20 }}
        transition={{ duration: 0.2 }}
      >
        {item.name}
      </motion.div>
    ))}
  </AnimatePresence>
);

Exemples de configurations

Application mobile classique

Navigation hiérarchique (liste -> détail -> sous-détail) :

pages: {
  "/": {
    "*": "slideLeft"
  },
  "/items": {
    "/": "slideRight",
    "/items/*": "slideLeft",
    "*": "fade"
  },
  "/items/*": {
    "/items": "slideRight",
    "*": "fade"
  },
  "*": "fade"
}

Application avec onglets

Navigation entre onglets sans animation de glissement :

pages: {
  "/home": { "*": "fade" },
  "/search": { "*": "fade" },
  "/profile": { "*": "fade" },
  "/settings": {
    "*": "slideLeft" // Seul settings a une animation différente
  },
  "*": "fade"
}

Désactiver toutes les animations

pages: {
  "*": "fade" // Utiliser uniquement fade (le plus discret)
}

// Ou modifier la durée à 0
// (nécessite de modifier les animations dans le code)

Voir aussi