Fermer

décembre 1, 2023

Comment créer des GIF animés à partir d’animations GSAP —

Comment créer des GIF animés à partir d’animations GSAP —


Dans cet article, je vais vous expliquer comment convertir des animations créées à l’aide de GSAP en GIF animés en utilisant gif-moderne.

Voici un aperçu de celui que j’ai réalisé plus tôt. 👇

Deux panneaux, lecture "allez vous faire foutre les gars, je fais des GIF"avec un personnage de South Park passant d'une case à l'autre

Sur les liens ci-dessous, vous trouverez un aperçu en direct et tout le code auquel je ferai référence tout au long de cet article :

  • 🚀 Aperçu :
    • Index : https://gsap-animation-to-gif.netlify.app
    • Simple : https://gsap-animation-to-gif.netlify.app/simple
  • ⚙️ Dépôt : https://github.com/PaulieScanlon/gsap-animation-to-gif

Il y a deux « pages » dans le dépôt. indice contient tout le code du GIF vu ci-dessus, et simple est un point de départ pour les étapes couvertes dans cet article.

Table des matières

Comment convertir des animations GSAP en GIF

La méthode que j’utilise pour convertir une animation GSAP en GIF consiste à capturer des données SVG à chaque « mise à jour » du Tween et à les écrire sur un canevas HTML. Une fois l’interpolation terminée, je suis alors en mesure de convertir les données SVG en données d’image rastérisées qui peuvent être utilisées par gif-moderne pour créer chaque image d’un GIF animé.

Commencer

Voici le code que j’ai utilisé dans l’exemple simple, et c’est ce que je vais utiliser pour expliquer chacune des étapes requises pour créer un GIF animé à partir d’une animation GSAP.

<html lang='en'>
<head>
  <meta charset='utf-8' />
  <title>Simple</title>
  <script>
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    let animationFrames = [];
    let canvasFrames = [];

    .timeline({
      onUpdate: () => {},
      onComplete: () => {},
    })
    .fromTo('#rect', { x: -50 }, { duration: 2, x: 350, ease: 'power.ease2' });
  </script>
</head>
<body>
  <main>
    <section>
      <svg
        id='svg'
        xmlns='http://www.w3.org/2000/svg'
        viewBox='0 0 400 200'
        width={400}
        height={200}
        style={{ border: '1px solid red' }}
      >
        <rect id='rect' x='0' y='75' width='50' height='50' fill='red'></rect>
      </svg>
      <canvas id='canvas' style={{ border: '1px solid blue' }} width={400} height={200}></canvas>
      <img id='image' width={400} height={200} style={{ border: '1px solid green' }} />
      <a id='link' download='simple.gif'>Download</a>
    </section>
  </main>
  <script src='https://unpkg.com/modern-gif'></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js'></script>
</body>
</html>

Il y a quelques choses que j’aimerais expliquer à propos du code ci-dessus.

Script en ligne

En haut du fichier, je crée une référence au canvas élément dans le code HTML (ci-dessous) et définissez une nouvelle référence au contexte du canevas appelée ctx. Cela me permettra de référencer l’élément canvas et d’y écrire des données.

Deux tableaux sont définis pour contenir les données capturées (j’expliquerai où chacun est utilisé dans une étape ultérieure) :

  • animationFrames
  • canvasFrames

Et enfin, mais non des moindres, une instance d’une chronologie GSAP et d’un Tween qui anime un fichier SVG. rect élément dans le HTML (ci-dessous).

HTML

  • Le HTML contient un svg élément avec un ID de svg avec un rouge rect élément avec un ID de rect. Le rect est l’élément que je vais animer.
  • Sous le svg l’élément est un canvas élément. C’est ici que j’écrirai les données SVG capturées pour une utilisation ultérieure.
  • Sous le canvas l’élément est un img élément. C’est ici que le GIF animé final sera affiché.
  • Enfin, il existe un élément a qui peut être utilisé pour « télécharger » le GIF.

Éléments de script

Les deux éléments de script en bas concernent la bibliothèque modern-gif et la bibliothèque GSAP. Les deux doivent être inclus dans la page pour que vous puissiez les utiliser.

Capturer des données SVG

Localisez la chronologie GSAP et apportez les modifications suivantes :

.timeline({
  onUpdate: () => {
+    const xml = new XMLSerializer().serializeToString(svg);
+    const src = `data:image/svg+xml;base64,${btoa(xml)}`;
+    animationFrames.push(src);
  },
  onComplete: () => {
+    console.log(animationFrames);
  },
})
.fromTo('#rect', { x: -50 }, { duration: 2, x: 350, ease: 'power.ease2' });

Le code ci-dessus sérialise le HTML svg élément et convertit les données en un svg+xml;base64 chaîne. À ce stade, les « données d’image » ne sont pas tout à fait ce dont j’ai besoin, mais en les convertissant en chaîne, je peux les stocker dans le animationFrame tableau pour une utilisation ultérieure.

Si vous avez ajouté le console.log dans le onComplete fonction, vous devriez voir quelque chose de similaire à l’image ci-dessous dans la console de votre navigateur.

Ajout de console.log

Convertir des données SVG en données rastérisées

.timeline({
  onUpdate: () => {
    const xml = new XMLSerializer().serializeToString(svg);
    const src = `data:image/svg+xml;base64,${btoa(xml)}`;
    animationFrames.push(src);
  },
  onComplete: () => {
-    console.log(animationFrames);
+    let inc = 0;
+    const renderSvgDataToCanvas = () => {
+      const virtualImage = new Image();
+      virtualImage.src = animationFrames[inc];
+      virtualImage.onload = () => {
+        ctx.clearRect(0, 0, 400, 200);
+        ctx.drawImage(virtualImage, 0, 0, 400, 200);
+        canvasFrames.push(canvas.toDataURL('image/jpeg'));
+        inc++;
+        if (inc < animationFrames.length - 1) {
+          renderSvgDataToCanvas();
+        } else {
+          console.log(canvasFrames);
+        }
+      };
+    };
+    renderSvgDataToCanvas();
  },
})
.fromTo('#rect', { x: -50 }, { duration: 2, x: 350, ease: 'power.ease2' });

Cette étape est légèrement plus complexe et nécessite que j’effectue une action pour chaque index du animationFrames tableau.

En utilisant une fonction récursive, renderSvgDataToCanvasje peux utiliser les données d’image du animationFrames tableau, écrivez-le sur le canevas. Ensuite, en utilisant canvas.toDataURL('image/jpeg') Je peux stocker les données pixellisées de chaque image de l’animation dans le canvasFrames tableau.

Si vous avez ajouté le console.log dans le onComplete fonction, vous devriez voir quelque chose de similaire à celui ci-dessous dans la console de votre navigateur. Cette fois, notez cependant le type MIME des données : au lieu de svg+xmlc’est image/jpeg. C’est important pour ce que je dois faire ensuite.

données rastérisées

Convertir des données rastérisées en GIF

Il s’agit de la dernière étape et consiste à transmettre chaque index du canvasFrames tableau sur modern-gif.

.timeline({
onUpdate: () => {
  const xml = new XMLSerializer().serializeToString(svg);
  const src = `data:image/svg+xml;base64,${btoa(xml)}`;
  animationFrames.push(src);
},
onComplete: () => {
  let inc = 0;
  const renderSvgDataToCanvas = () => {
    const virtualImage = new Image();
    virtualImage.src = animationFrames[inc];
    virtualImage.onload = () => {
      ctx.clearRect(0, 0, 400, 200);
      ctx.drawImage(virtualImage, 0, 0, 400, 200);
      canvasFrames.push(canvas.toDataURL('image/jpeg'));
      inc++;
      if (inc < animationFrames.length - 1) {
        renderSvgDataToCanvas();
      } else {
-          console.log(canvasFrames);
+          generateGif();
      }
    };
  };
+    const generateGif = async () => {
+      const gif = await modernGif.encode({
+        width: 400,
+        height: 200,
+        frames: canvasFrames.map((frame) => {
+          return { imageData: frame, delay: 0 };
+        }),
+      });
+      const frames = await gif;
+      const blob = new Blob([frames], { type: 'image/gif' });
+      const src = URL.createObjectURL(blob);

+      const image = document.getElementById('image');
+      const link = document.getElementById('link');

+      image.src = src;
+      link.href = src;
+    };
    renderSvgDataToCanvas();
  },
})
.fromTo('#rect', { x: -50 }, { duration: 2, x: 350, ease: 'power.ease2' });

En utilisant moderneGif.encode vous pouvez transmettre un tableau de données sur des images et définir un délai pour chaque image, j’ai choisi d’ajouter un délai de 0 seconde.

La partie suivante du code traite de la conversion du modernGif.ecode données et en les convertissant en « encore un autre » type MIME, cette fois image/gif.

Une fois que j’ai un « blob » final de données qui représente mon GIF animé, je le convertis en URL, puis je définis le src et href de l’image et des éléments de lien afin que je puisse voir et télécharger le GIF dans le navigateur.

Conversion en GIF

Fréquence d’images

Vous remarquerez peut-être que le GIF final s’exécute assez lentement, car les animations exécutées dans le navigateur seront généralement lues à 60 images par seconde (ips), alors que les GIF s’exécutent généralement à une fréquence d’images beaucoup plus lente, 12 ou 24 ips.

Pour « supprimer » certaines images de l’animation, j’utilise un filtre matriciel et Opérateur de reste JavaScript pour déterminer si l’index est divisible par un certain nombre, dans mon cas, j’ai choisi 6. Les index qui ne sont pas divisibles par 6 sont filtrés du tableau. Le GIF animé résultant, bien qu’un peu maladroit, sera lu beaucoup plus rapidement.

const generateGif = async () => {
  const gif = await modernGif.encode({
    width: 400,
    height: 200,
    frames: canvasFrames
+       .filter((_, index) => index % 6 === 0)
      .map((frame) => {
        return { imageData: frame, delay: 0 };
      }),
    });
  const frames = await gif;
  const blob = new Blob([frames], { type: 'image/gif' });
  const src = URL.createObjectURL(blob);

  const image = document.getElementById('image');
  const link = document.getElementById('link');

  image.src = src;
  link.href = src;
};

Et c’est ainsi que vous pouvez passer de l’animation GSAP SVG au GIF animé via le canevas HTML !

Si vous avez des questions sur tout ce que j’ai décrit dans cet article, n’hésitez pas à me trouver sur Twitter/X : @PaulieScanlon.






Source link

décembre 1, 2023