Site icon Le blog Tech d'Indy

Lottie Animation

Qu’est-ce que Lottie ? 🧠

Lottie est un format d’animation vectorielle se basant sur des donnĂ©es au format JSON. Il a Ă©té crĂ©Ă© en 2015 par la sociĂ©tĂ© amĂ©ricaine Airbnb.

Airbnb utilise beaucoup d’animations dans son parcours utilisateur et il Ă©tait souvent problĂ©matique pour les dĂ©veloppeurs d’intĂ©grer correctement les animations faites par les designers. Les GIFs fournis Ă©taient difficilement adaptables Ă  la rĂ©solution d’écran de l’utilisateur et mettait parfois du temps Ă  se charger.

Partant de ce constat, Airbnb a dĂ©veloppĂ© un plugin sur le logiciel Adobe After Effects baptisĂ© Bodymovin. Ce plugin permet d’exporter des animations dans un fichier au format JSON.

Le fichier au format JSON peut ensuite ĂȘtre interprĂ©tĂ© par les diffĂ©rentes librairies proposĂ©es par Lottie : sur un navigateur web, sur une application tĂ©lĂ©phone native ou encore sur une application Windows.

Les avantages de Lottie sont nombreux :

“If a PNG is a T-Rex, and a GIF is an elephant, then a Lottie is a puppy.”

Notre besoin chez Indy đŸ’»

Chez Indy, nous souhaitons proposer à nos utilisateurs une expérience moderne et intuitive afin de faciliter leur compréhension de la comptabilité, une notion parfois difficile à appréhender.

Le but Ă©tait d’annoncer une nouvelle fonctionnalitĂ© dans l’application via une animation Lottie.

RĂ©alisation technique de l’intĂ©gration de l’animation đŸ› ïž

CrĂ©ation d’un composant Vue gĂ©nĂ©rique đŸ–Œïž

Nous avons crĂ©Ă© un composant gĂ©nĂ©rique responsable du chargement de l’animation via la mĂ©thode loadAnimation de la bibliothĂšque lottie-web. L’utilisation d’animation Lottie dans l’application passe obligatoirement par ce composant.

Ce composant prend en compte 3 props :

La mĂ©thode loadAnimation peut Ă©galement prendre en compte d’autres paramĂštres (cf. la documentation), mais nous jugions que ceux-ci Ă©taient inutiles dans notre utilisation. En effet chez Indy, nous respectons le principe YAGNI (« You ain’t gonna need it », qui peut se traduire par « vous n’en aurez pas besoin »). Nous Ă©vitons au maximum l’importation de librairies inutiles et l’implĂ©mentation de code non utilisĂ©s dans l’application.

Voici ce Ă  quoi ressemble le composant LottieAnimation :

<template>
  <div ref="animationContainer" />
</template>

<script>
export default {
  name: 'LottieAnimation',
  props: {
    lottieJsonPath: {
      // the JSON path linked to the animation
      type: String,
      required: true,
    },
    loopAnimation: {
      // Do we want the animation to loop?
      type: Boolean,
      required: false,
      default: true,
    },
    autoPlayAnimation: {
      // Do we want to play the animation on initialization?
      type: Boolean,
      required: false,
      default: true,
    },
  },
  data: () => ({
    rendererSettings: {
      scaleMode: 'centerCrop',
      clearCanvas: true,
      progressiveLoad: false,
      hideOnTransparent: true,
    },
    lottieAnimation: undefined,
  }),
  async mounted() {
    await this.init();
  },
  beforeDestroy() {
    this.lottieAnimation?.destroy();
  },
  methods: {
    [...]
  },
};
</script>

⚠ Il est important d’appeler la mĂ©thode destroy sur l’objet contenant l’animation dans le hook beforeDestroy de Vue pour Ă©viter les fuites de mĂ©moire.

Lazy loading de la bibliothĂšque lottie-web ⚙

La bibliothÚque lottie-web est une bibliothÚque plutÎt lourde (taille gzipped à 67.3 Ko). Ceci est particuliÚrement impactant si la bibliothÚque est située dans le chunk principal lors du build.

Le chargement de la page principale pour un utilisateur n’ayant pas une bonne connexion prendrait un temps beaucoup plus long dans ce cas. La bibliothĂšque serait quand mĂȘme chargĂ©e, mĂȘme si des pages n’affichent pas d’animation Lottie.

Dans notre cas d’utilisation chez Indy, nous utilisons Lottie pour l’instant qu’à un seul endroit. Nous avons souhaitĂ© lazy loader la bibliothĂšque pour qu’elle ne soit chargĂ©e que quand l’utilisateur ouvre la page contenant l’animation. Dans sa navigation sur les autres pages, la bibliothĂšque n’est pas chargĂ©e.

Pour lazy loader lottie, nous avons crĂ©Ă© un chunk contenant seulement la bibliothĂšque. La bibliothĂšque est en mode prefetch, c’est Ă  dire qu’elle ne se charge que quand le navigateur est disponible pour effectuer le chargement.

Voici Ă  quoi ressemble la mĂ©thode init() de notre composant LottieAnimation. Elle s’occupe de lazy loader la bibliothĂšque Lottie puis de configurer l’animation Lottie.

methods: {
  async init() {
    const lottie = await import(
      /* webpackChunkName: "lottie" */
      /* webpackMode: "lazy" */
      /* webpackPrefetch: true */
      'lottie-web'
    );
    this.lottieAnimation = lottie.loadAnimation({
      container: this.$refs.animationContainer,
      renderer: 'svg',
      loop: this.loopAnimation,
      autoplay: this.autoPlayAnimation,
      path: this.lottieJsonPath,
      rendererSettings: this.rendererSettings,
    });
  },
},

⚠ La rĂ©fĂ©rence au container dans la mĂ©thode loadAnimation ne doit pas pointer sur un Ă©lĂ©ment HTML contenant des directives Vue telles que v-if. Autrement, le mapping ne peut pas se faire.

Code complet du composant LottieAnimation đŸ‘šđŸœâ€đŸ’»

<template>
  <div ref="animationContainer" />
</template>

<script>
export default {
  name: 'LottieAnimation',
  props: {
    lottieJsonPath: {
      // the JSON path linked to the animation
      type: String,
      required: true,
    },
    loopAnimation: {
      // Do we want the animation to loop ?
      type: Boolean,
      required: false,
      default: true,
    },
    autoPlayAnimation: {
      // Do we want to play the animation on initialization ?
      type: Boolean,
      required: false,
      default: true,
    },
  },
  data: () => ({
    rendererSettings: {
      scaleMode: 'centerCrop',
      clearCanvas: true,
      progressiveLoad: false,
      hideOnTransparent: true,
    },
    lottieAnimation: undefined,
  }),
  async mounted() {
    await this.init();
  },
  beforeDestroy() {
    this.lottieAnimation?.destroy();
  },
  methods: {
    async init() {
      const lottie = await import(
        /* webpackChunkName: "lottie" */
        /* webpackMode: "lazy" */
        /* webpackPrefetch: true */
        'lottie-web'
      );
      this.lottieAnimation = lottie.loadAnimation({
        container: this.$refs.animationContainer,
        renderer: 'svg',
        loop: this.loopAnimation,
        autoplay: this.autoPlayAnimation,
        path: this.lottieJsonPath,
        rendererSettings: this.rendererSettings,
      });
    },
  },
};
</script>
Quitter la version mobile