Ajouter des effets d'entrée et de sortie aux éléments lorsqu'ils entrent dans la zone de défilement ou la quittent

Les effets d'entrée et de sortie sont des animations qui se déclenchent lorsqu'un élément entre dans la fenêtre d'affichage ou en sort. Cela peut être utilisé pour créer des expériences utilisateur attrayantes et dynamiques. Par exemple, vous pouvez utiliser un effet d'entrée pour faire apparaître un élément en fondu lorsqu'il défile dans la vue, ou un effet de sortie pour réduire sa taille lorsqu'il défile hors de la vue.

Mise en œuvre

Pour ajouter des effets d'entrée et de sortie à un élément, vous devez combiner plusieurs propriétés CSS. Voici la procédure à suivre, étape par étape :

  1. Créez des @keyframes distincts pour les animations d'entrée et de sortie. L'animation d'entrée sera appliquée lorsque l'élément entrera dans la fenêtre d'affichage, et l'animation de sortie sera appliquée lorsqu'il en sortira.

    @keyframes slide-in {
      from { transform: translateX(-100%); }
    }
    @keyframes slide-out {
      to { transform: translateX(100%); }
    }
    
  2. Associez les images clés d'entrée et de sortie à l'élément. Pour ce faire, définissez plusieurs animations dans la propriété animation.

    • Attribuez à l'animation d'entrée un animation-fill-mode de backwards afin qu'elle applique son état initial avant le début de l'animation.
    • Attribuez à l'animation de sortie un animation-fill-mode de forwards afin qu'elle conserve son état final une fois l'animation terminée.
    .animated-element {
      animation:
        slide-in 1s linear backwards,
        slide-out 1s linear forwards;
    }
    
  3. Créez une timeline d'affichage et associez-la aux animations. Une timeline d'affichage est un type de timeline associé à la visibilité d'un élément dans la fenêtre d'affichage. Vous pouvez en créer une à l'aide de la fonction view(), puis l'appliquer à vos animations à l'aide de la propriété animation-timeline.

    .animated-element {
      animation-timeline: view();
    }
    

    Par défaut, view() suit l'élément sur l'axe block. Si vous devez le suivre sur l'axe inline, vous pouvez utiliser view(inline).

  4. Limitez les animations aux plages entry et exit. La propriété animation-range vous permet de spécifier la partie de la chronologie sur laquelle une animation doit s'exécuter.

    • La plage entry couvre la période entre l'entrée de l'élément dans la fenêtre d'affichage et sa visibilité complète.
    • La plage exit couvre la période entre le moment où l'élément commence à quitter la fenêtre d'affichage et celui où il est complètement masqué.
    .animated-element {
      animation-range: entry, exit;
    }
    

Exemple de code

Ce code anime les enfants directs du défileur lors du défilement à l'aide d'une view-timeline anonyme :

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    @keyframes grow {
      from {
        scale: 0.5;
      }
    }
    @keyframes shrink {
      to {
        scale: 0.5;
      }
    }

    .scroller > * {
      animation:
        grow auto linear backwards,
        shrink auto linear forwards;
      animation-timeline: view(inline);
      animation-range: entry, exit;
    }
  }
}

Lorsque les éléments entrent dans la zone de défilement, l'animation grow est lue. Lorsqu'ils quittent la zone de défilement, l'animation shrink est lue.

Le code suivant produit le même résultat visuel, mais anime les enfants directs du défileur lors du défilement à l'aide d'une chronologie de vue nommée :

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    @keyframes grow {
      from {
        scale: 0.5;
      }
    }
    @keyframes shrink {
      to {
        scale: 0.5;
      }
    }

    .scroller > * {
      view-timeline: --tl inline;
      animation:
        grow auto linear backwards,
        shrink auto linear forwards;
      animation-timeline: --tl;
      animation-range: entry, exit;
    }
  }
}

Bonnes pratiques

Lorsque vous utilisez des animations basées sur le défilement, il est important de suivre quelques bonnes pratiques pour garantir une expérience fluide et accessible :

  • À FAIRE : incluez la détection de fonctionnalités. Tous les navigateurs ne sont pas compatibles avec les animations déclenchées par le défilement. Utilisez @supports ((animation-timeline: view()) and (animation-range: entry)) pour vérifier la compatibilité et fournir une solution de remplacement pour les navigateurs qui ne sont pas compatibles.
    • La vérification (animation-range: entry) DOIT être incluse ici pour filtrer les navigateurs qui ne sont que partiellement compatibles.
    • N'UTILISEZ PAS le package scroll-timeline-polyfill pour la stratégie de remplacement, car il n'est pas complet et présente de nombreux problèmes connus.
    • Si l'animation n'est considérée que comme décorative, optez pour l'amélioration progressive et NE FOURNISSEZ PAS de solution de remplacement.
  • À FAIRE : Respectez les préférences des utilisateurs. Certains préfèrent avoir moins de mouvements sur le Web. Utilisez la requête média prefers-reduced-motion pour désactiver ou réduire vos animations pour ces utilisateurs.
  • À FAIRE : Essayez de n'animer que les propriétés CSS performantes. Pour des animations plus fluides, n'animez que les propriétés pouvant être gérées par le thread du compositeur du navigateur, telles que transform et opacity. L'animation d'autres propriétés telles que width ou height peut entraîner des problèmes de performances.
  • À FAIRE : Utilisez l'ordre de déclaration correct. Lorsque vous utilisez la propriété abrégée animation, déclarez animation-timeline après celle-ci pour éviter que la propriété abrégée ne réinitialise la chronologie.

Lorsque vous utilisez la fonction view() pour créer une animation basée sur le défilement :

  • FACULTATIF : indiquez explicitement l'axe à suivre. Lorsque vous ne ciblez pas l'axe block par défaut (par exemple, dans un défilement horizontal), indiquez explicitement l'axe à suivre avec view(block) ou view(inline).
  • Lorsque l'animation n'est pas appliquée au sujet suivi lui-même, utilisez une chronologie de vue nommée.

Lorsque vous utilisez la propriété view-timeline pour créer une animation basée sur le défilement :

  • À FAIRE : utilisez une <dashed-ident> CSS pour le nom.
  • FACULTATIF : Soyez explicite sur l'axe à suivre. Lorsque vous ne ciblez pas l'axe block par défaut (par exemple, dans un défilement horizontal), indiquez explicitement l'axe à suivre avec view-timeline-axis.
  • À FAIRE : Assurez-vous que le champ d'application de la recherche fonctionne. Lorsque l'élément qui déclare view-timeline n'est pas un ancêtre de l'arborescence plate de l'élément animé, augmentez la visibilité du nom de view-timeline en utilisant timeline-scope sur un ancêtre partagé.

Privilégiez un view-timeline nommé lorsque plusieurs éléments ou enfants du sujet suivi doivent être animés.

Compatibilité des navigateurs et stratégies de remplacement

Par conséquent, une stratégie de remplacement est généralement requise.

Pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement, vous pouvez utiliser une solution de remplacement pour recréer les effets visuels. Les solutions de remplacement sont généralement créées avec un écouteur de défilement (pour les effets ScrollTimeline) ou l'API IntersectionObserver (pour les effets ViewTimeline).

Dans les navigateurs compatibles de manière native avec les animations liées au défilement, utilisez TOUJOURS l'implémentation CSS native, car elle est plus performante.

Notez que tous les effets ne peuvent pas être recréés à l'aide de l'approche de remplacement.

Pour ce cas d'utilisation spécifique, le script suivant applique la solution de remplacement pour les navigateurs qui ne sont pas compatibles avec les animations déclenchées par le défilement. Il utilise un IntersectionObserver pour suivre la visibilité de l'élément .wrapper et met à jour la propriété transform des calques en fonction de la position de défilement.

<script>
  if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          // This matches the effect as defined in the CSS example above.
          // Customize this further if needed.
          entry.target.style.scale = 0.5 + entry.intersectionRatio * 0.5;
        }
      },
      {
        threshold: Array.from({ length: 101 }, (_, i) => i / 100),
      }
    );

    document.querySelectorAll('.scroller > *').forEach((el) => {
      observer.observe(el);
    });
  }
</script>