Animation de la hauteur - la bonne manière

Soyons honnêtes. L'animation de la hauteur peut être très pénible. Pour moi, cela a été une bataille constante entre vouloir avoir de belles animations et ne pas être prêt à payer l'énorme coût de performance associé à l'animation en hauteur. Maintenant - tout est fait.

Tout a commencé lorsque je travaillais sur mon projet parallèle - un générateur de CV où vous pouvez partager des liens vers votre CV qui ne sont actifs que pendant un certain temps.

Je voulais avoir une belle animation pour toutes les sections du CV, et j'ai construit un composant de réaction qui a exécuté l'animation. Cependant, j'ai vite découvert que ce composant détruisait absolument les performances sur les appareils bas de gamme et dans certains navigateurs. Bon sang, même mon Macbook pro haut de gamme avait du mal à garder des images par seconde fluides sur l'animation.

La raison en est bien sûr que l'animation de la propriété height dans CSS oblige le navigateur à effectuer des opérations de mise en page et de peinture coûteuses sur chaque image. Il y a une section fantastique sur les performances de rendu sur Google Web Fundamentals, je vous suggère de la consulter.

En bref, chaque fois que vous modifiez une propriété géométrique en css, le navigateur doit ajuster et effectuer des calculs sur l'impact de ce changement sur la mise en page de la page, puis il devra rendre la page à nouveau dans une étape appelée peinture.

Pourquoi devrions-nous même nous soucier de la performance?

Il peut être tentant d'ignorer les performances. Ce n'est pas sage, mais cela peut être tentant. D'un point de vue commercial, vous gagnez beaucoup de temps qui pourrait autrement être consacré à la création de nouvelles fonctionnalités.

Cependant, les performances peuvent influencer directement vos résultats. À quoi cela sert-il de créer un grand nombre de fonctionnalités, si personne ne les utilise? Plusieurs études réalisées par Amazon et Google montrent que c'est vrai. Les performances sont directement liées à l'utilisation des applications et aux revenus nets.

L'autre aspect de la performance est tout aussi important. En tant que développeurs, nous avons la responsabilité de nous assurer que le Web reste accessible à tous - nous le faisons parce que c'est juste. Parce qu'Internet n'est pas seulement pour vous et moi, c'est pour tout le monde.

Comme le montre l'excellent article d'Addy Osmani, les appareils bas de gamme prennent beaucoup plus de temps à analyser et exécuter javascript par rapport à leurs homologues haut de gamme.

Pour éviter de créer un fossé de classe sur Internet, nous devons être implacables dans notre quête de performance. Pour moi, cela signifiait être créatif et trouver un autre moyen de réaliser mes animations sans sacrifier les performances.

Animer la hauteur de la bonne manière

Si vous ne vous souciez pas du comment et que vous voulez juste voir un exemple en direct, veuillez consulter les liens ci-dessous pour le site de démonstration, des exemples et un package npm pour réagir:

  • Site de démonstration utilisant la technique
  • Exemple en direct dans vanilla JS
  • Exemple simple dans react
  • Package NPM et documentation pour react

La question que je me suis posée était de savoir comment éviter le coût de performance engendré par l'animation de la hauteur. Réponse simple - vous ne pouvez pas.

Au lieu de cela, j'avais besoin de faire preuve de créativité avec d'autres propriétés CSS qui n'entraînaient pas ces coûts. À savoir transforme.

Puisque les transformations n'ont aucun moyen d'influencer la hauteur. On ne peut pas simplement appliquer une simple propriété à un élément et être fait. Nous devons être plus intelligents que cela.

La façon dont nous allons utiliser pour réaliser une animation performante de la hauteur est en fait de la simuler avec transform: scaleY. L'animation se fait en plusieurs étapes:

Voici un exemple:

 ${this.markup} `
  • Nous devons d'abord obtenir la hauteur initiale du conteneur d'élément. Ensuite, nous définissons la hauteur du conteneur externe pour qu'elle soit explicite à cette hauteur. Cela entraînera le débordement du contenu changeant du conteneur au lieu de l'expansion de son parent.
  • À l'intérieur du conteneur extérieur, nous avons un autre div qui est absolument positionné pour couvrir toute la largeur et la hauteur du div. Ceci est notre arrière-plan, et sera mis à l'échelle une fois que nous basculerons la transformation.
  • Nous avons également un conteneur intérieur. Le conteneur interne contient le balisage et changera sa hauteur en fonction du contenu qu'il contient.
  • Voici l'astuce: une  fois que nous basculons un événement qui change le balisage, nous prenons la nouvelle hauteur du conteneur interne et calculons la quantité dont l'arrière-plan doit être mis à l'échelle afin de s'adapter à la nouvelle hauteur. Ensuite, nous définissons l'arrière-plan sur scaleY par le nouveau montant.
  • En javascript vanille, cela signifie une supercherie avec des rendus doubles. Une fois pour obtenir la hauteur du conteneur intérieur pour calculer l'échelle. Puis à nouveau pour appliquer l'échelle à la division d'arrière-plan afin qu'il effectue la transformation.

Vous pouvez voir un exemple en direct ici dans vanilla JS.

À ce stade, notre arrière-plan a été correctement mis à l'échelle pour créer l'illusion de la hauteur. Mais qu'en est-il du contenu environnant? Puisque nous n'ajustons plus la mise en page, le contenu environnant n'est pas affecté par les changements.

Pour faire bouger le contenu environnant. Nous devons ajuster le contenu à l'aide de transformY. L'astuce consiste à prendre la quantité d'expansion du contenu et à l'utiliser pour déplacer le contenu environnant avec des transformations. Voir l'exemple ci-dessus.

Animation de la hauteur dans React

Comme je l'ai mentionné précédemment, j'ai développé cette méthode en travaillant sur un projet personnel dans React. Ce site de démonstration utilise cette méthode exclusivement dans toute son «animation de hauteur». Consultez le site de démonstration ici.

Après avoir implémenté cela avec succès, j'ai pris le temps d'ajouter ce composant et quelques composants de support à une petite bibliothèque d'animation que j'ai créée dans React. Si vous êtes intéressé, vous pouvez trouver les informations pertinentes ici:

  • voir la bibliothèque sur NPM ici
  • La documentation peut être trouvée ici.

Les composants les plus importants de cette bibliothèque sont AnimateHeight et AnimateHeightContainer. Examinons-les:

// Inside a React component // handleAnimateHeight is called inside AnimateHeight and is passed the // transitionAmount and optionally selectedId if you pass that as a prop to // AnimateHeight. This means that you can use the transitionAmount to // transition your surrounding content.const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; // Takes a style prop, a shouldchange prop and a callback. shouldChange // determines when the AnimateHeight component should trigger, which is // whenever the prop changes. The same prop is used to control which // content to show.  {this.state.open && } {!this.state.open && } 
  • Exemple avec déplacement du contenu environnant

Les exemples ci-dessus vous montrent comment utiliser AnimateHeight et déclencher manuellement l'ajustement du contenu environnant. Mais que faire si vous avez beaucoup de contenu et que vous ne souhaitez pas effectuer ce processus manuellement? Dans ce cas, vous pouvez utiliser AnimateHeight avec AnimateHeightContainer.

Pour utiliser AnimateHeightContainer, vous devez fournir à tous les enfants de niveau supérieur un accessoire appelé animateHeightId, qui doit également être transmis à vos composants AnimateHeight:

// Inside React Component const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; // AnimateHeight receives the transitionAmount and the active id of the AnimateHeight that fired. {this.state.open &&  {!this.state.open && }  // When AnimateHeight is triggered by state change // this content will move because the animateHeightId // is greater than the id of the AnimateHeight component above I will move I will also move 

Comme vous pouvez le voir dans cet exemple, AnimateHeight reçoit les informations dont il a besoin pour ajuster le contenu lorsque le composant AnimateHeight est déclenché par un changement d'état.

Une fois que cela se produit, le composant AnimateHeight déclenchera le rappel et définira les propriétés dans l'état. À l'intérieur d'AnimateHeight, cela ressemble à ceci (simplifié):

// Inside AnimateHeight componentDidUpdate() { if (update) { doUpdate() callback(transitionAmount, this.props.animateHeightId) } } // Equivalent to calling this function: const handleAnimateHeight = (transitionAmount, selectedId) => { this.setState({ transitionAmount, selectedId }); }; handleAnimateHeight(transitionAmount, this.props.animateHeight)

Vous avez maintenant la quantité de transition du contenu en pixels et l'ID du composant AnimateHeight qui s'est déclenché. Une fois que vous avez transmis ces valeurs à AnimateHeightContainer, il les prendra et effectuera la transition des autres composants en lui-même, à condition que vous configuriez l'incrémentation d'animateHeightIds sur les enfants de niveau supérieur.

Exemples plus avancés:

  • Déplacer le contenu environnant avec AnimateHeightContainer
  • Exemple d'accordéon

REMARQUE: si vous utilisez cette méthode pour animer la hauteur et déplacer le contenu environnant, vous devez ajouter la valeur de transition à la hauteur de votre page.

Conclusion

Vous avez peut-être remarqué dans cet article que nous n'animons pas réellement la hauteur - et l'appelons est faux. Vous avez bien sûr tout à fait raison. Cependant, je crois fermement que ce que nous appelons cela n'a pas d'importance. Ce qui compte, c'est que nous obtenions l'effet souhaité avec le coût de performance le plus bas possible.

Bien que je pense avoir trouvé un moyen qui est meilleur que d'animer directement la propriété height, je ne prétends pas avoir inventé ou pensé à quelque chose qui n'a pas été découvert auparavant. Je ne juge pas non plus. Peut-être que l'animation de la hauteur fonctionne pour vous dans votre scénario - Pas de problème.

All I want is to enable and simplify effects that we all need to do, but sometimes incur costs that are difficult to bear. At the very least, I want to spark a discussion that is worth having. How can we improve the internet for everyone?