reCAPTCHA WAF Session Token
Programming Languages

How to Create Animated GIFs from GSAP Animations — SitePoint


In this article, I’m going to explain how you can convert animations created using GSAP into animated GIFs using modern-gif.

Here’s a sneak peek of one I made earlier. 👇

On the links below, you’ll find a live preview and all the code I’ll be referencing throughout this article:

  • 🚀 Preview:
    • Index:
    • Simple: /simple
  • ⚙️ Repo:

There are two “pages” in the repo. index contains all the code for the GIF seen above, and simple is a starting point for the steps covered in this post.

Table of Contents

How to convert GSAP Animations into GIFs

The method I’m using to convert a GSAP animation into a GIF involves capturing SVG data on each “update” of the Tween and writing it to an HTML canvas. After the Tween completes I’m then able to convert SVG data into Rasterized image data which can be used by modern-gif to create each frame of an animated GIF.

Getting Started

Here’s the code I’ve used in the simple example, and it’s what I’ll be using to explain each of the steps required to create an animated GIF from a GSAP animation.

<code class="markup language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>en<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>utf-8<span class="token punctuation">'</span></span> <span class="token punctuation">/></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Simple<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
    <span class="token keyword">const</span> canvas <span class="token operator">=</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">getElementById</span><span class="token punctuation">(</span><span class="token string">'canvas'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> ctx <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token method function property-access">getContext</span><span class="token punctuation">(</span><span class="token string">'2d'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">let</span> animationFrames <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">let</span> canvasFrames <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token punctuation">.</span><span class="token method function property-access">timeline</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      <span class="token function-variable function">onUpdate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token function-variable function">onComplete</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">fromTo</span><span class="token punctuation">(</span><span class="token string">'#rect'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token operator">-</span><span class="token number">50</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> duration<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> x<span class="token operator">:</span> <span class="token number">350</span><span class="token punctuation">,</span> ease<span class="token operator">:</span> <span class="token string">'power.ease2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span>
        <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>svg<span class="token punctuation">'</span></span>
        <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>http://www.w3.org/2000/svg<span class="token punctuation">'</span></span>
        <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>0 0 400 200<span class="token punctuation">'</span></span>
        <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{400}</span>
        <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{200}</span>
        <span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{{</span> <span class="token attr-name"><span class="token namespace">border:</span></span> <span class="token attr-name">'1px</span> <span class="token attr-name">solid</span> <span class="token attr-name">red'</span> <span class="token attr-name">}}</span>
      <span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>rect</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>rect<span class="token punctuation">'</span></span> <span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>0<span class="token punctuation">'</span></span> <span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>75<span class="token punctuation">'</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>50<span class="token punctuation">'</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>50<span class="token punctuation">'</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>red<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>rect</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>canvas</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>canvas<span class="token punctuation">'</span></span> <span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{{</span> <span class="token attr-name"><span class="token namespace">border:</span></span> <span class="token attr-name">'1px</span> <span class="token attr-name">solid</span> <span class="token attr-name">blue'</span> <span class="token attr-name">}}</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{400}</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{200}</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>canvas</span><span class="token punctuation">></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>image<span class="token punctuation">'</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{400}</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{200}</span> <span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>{{</span> <span class="token attr-name"><span class="token namespace">border:</span></span> <span class="token attr-name">'1px</span> <span class="token attr-name">solid</span> <span class="token attr-name">green'</span> <span class="token attr-name">}}</span> <span class="token punctuation">/></span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>link<span class="token punctuation">'</span></span> <span class="token attr-name"> <a href="https://yourselfhood.com/from-pixels-to-the-cloud-the-advantages-of-storing-and-accessing-images-online/"  class="lar_link" data-linkid="4967" data-postid="7474"  title="tag"   target="_blank" >download</a></span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>simple.gif<span class="token punctuation">'</span></span><span class="token punctuation">></span></span>Download<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>https://unpkg.com/modern-gif<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">'</span>https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js<span class="token punctuation">'</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span>
</code>

There are a couple of things I’d like to explain about the above code.

Inline Script

At the top of the file I create a reference to the canvas element in the HTML (below) and define a new reference to the canvas context called ctx. This will allow me to reference the canvas element and write data to it.

There are two arrays defined to hold the captured data (I’ll explain where each is used in a later step):

  • animationFrames
  • canvasFrames

And last, but not least, an instance of a GSAP Timeline and Tween that animates an SVG rect element in the HTML (below).

HTML

  • The HTML contains an svg element with an ID of svg with a red rect element with an ID of rect. The rect is the element I’ll be animating.
  • Below the svg element is a canvas element. This is where I’ll write the captured SVG data for use later on.
  • Below the canvas element is an img element. This is where the final animated GIF will be displayed.
  • Lastly, there’s an a element which can be used to “ download” the GIF.

Script elements

The two script elements at the bottom are for the modern-gif library and GSAP library. Both need to be included in the page so you can use them.

Capturing SVG Data

Locate the GSAP Timeline and make the following changes:

<code class="javascript language-javascript"><span class="token punctuation">.</span><span class="token method function property-access">timeline</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">onUpdate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>    <span class="token keyword">const</span> xml <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">serializeToString</span><span class="token punctuation">(</span>svg<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">data:image/svg+xml;base64,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">btoa</span><span class="token punctuation">(</span>xml<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token operator">+</span>    animationFrames<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token function-variable function">onComplete</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>animationFrames<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token method function property-access">fromTo</span><span class="token punctuation">(</span><span class="token string">'#rect'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token operator">-</span><span class="token number">50</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> duration<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> x<span class="token operator">:</span> <span class="token number">350</span><span class="token punctuation">,</span> ease<span class="token operator">:</span> <span class="token string">'power.ease2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

The above code serializes the HTML svg element and converts the data to an svg+xml;base64 string. At this point, the “image data” isn’t quite what I need, but by converting it to a string I can store it in the animationFrame array for use later on.

If you’ve added the console.log in the onComplete function, you should see something similar to the image below in the console of your browser.

Adding console.log

Convert SVG Data to Rasterized Data

<code class="javascript language-javascript"><span class="token punctuation">.</span><span class="token method function property-access">timeline</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">onUpdate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> xml <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">serializeToString</span><span class="token punctuation">(</span>svg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">data:image/svg+xml;base64,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">btoa</span><span class="token punctuation">(</span>xml<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
    animationFrames<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token function-variable function">onComplete</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">-</span>    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>animationFrames<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token keyword">let</span> inc <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token keyword">const</span> <span class="token function-variable function">renderSvgDataToCanvas</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> virtualImage <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Image</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>      virtualImage<span class="token punctuation">.</span><span class="token property-access">src</span> <span class="token operator">=</span> animationFrames<span class="token punctuation">[</span>inc<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token operator">+</span>      virtualImage<span class="token punctuation">.</span><span class="token method-variable function-variable method function property-access">onload</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>        ctx<span class="token punctuation">.</span><span class="token method function property-access">clearRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        ctx<span class="token punctuation">.</span><span class="token method function property-access">drawImage</span><span class="token punctuation">(</span>virtualImage<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        canvasFrames<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span>canvas<span class="token punctuation">.</span><span class="token method function property-access">toDataURL</span><span class="token punctuation">(</span><span class="token string">'image/jpeg'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        inc<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>inc <span class="token operator"><</span> animationFrames<span class="token punctuation">.</span><span class="token property-access">length</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token operator">+</span>          <span class="token function">renderSvgDataToCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
<span class="token operator">+</span>          <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>canvasFrames<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        <span class="token punctuation">}</span>
<span class="token operator">+</span>      <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token function">renderSvgDataToCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token method function property-access">fromTo</span><span class="token punctuation">(</span><span class="token string">'#rect'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token operator">-</span><span class="token number">50</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> duration<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> x<span class="token operator">:</span> <span class="token number">350</span><span class="token punctuation">,</span> ease<span class="token operator">:</span> <span class="token string">'power.ease2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

This step is slightly more involved and requires that I perform an action for each index of the animationFrames array.

By using a recursive function, renderSvgDataToCanvas, I can use the image data from the animationFrames array, write it to the canvas. Then, by using canvas.toDataURL('image/jpeg') I can store rasterized data of each frame of the animation in the canvasFrames array.

If you’ve added the console.log in the onComplete function, you should see something similar to the below in the console of your browser. This time, however, note the MIME type of the data: instead of svg+xml, it’s image/jpeg. This is important for what I need to do next.

rasterized data

Convert Rasterized Data to GIF

This is the last step and involves passing each index of the canvasFrames array onto modern-gif.

<code class="javascript language-javascript"><span class="token punctuation">.</span><span class="token method function property-access">timeline</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token function-variable function">onUpdate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> xml <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLSerializer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">serializeToString</span><span class="token punctuation">(</span>svg<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">data:image/svg+xml;base64,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">btoa</span><span class="token punctuation">(</span>xml<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  animationFrames<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">onComplete</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> inc <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token function-variable function">renderSvgDataToCanvas</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> virtualImage <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Image</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    virtualImage<span class="token punctuation">.</span><span class="token property-access">src</span> <span class="token operator">=</span> animationFrames<span class="token punctuation">[</span>inc<span class="token punctuation">]</span><span class="token punctuation">;</span>
    virtualImage<span class="token punctuation">.</span><span class="token method-variable function-variable method function property-access">onload</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
      ctx<span class="token punctuation">.</span><span class="token method function property-access">clearRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      ctx<span class="token punctuation">.</span><span class="token method function property-access">drawImage</span><span class="token punctuation">(</span>virtualImage<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      canvasFrames<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span>canvas<span class="token punctuation">.</span><span class="token method function property-access">toDataURL</span><span class="token punctuation">(</span><span class="token string">'image/jpeg'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      inc<span class="token operator">++</span><span class="token punctuation">;</span>
      <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>inc <span class="token operator"><</span> animationFrames<span class="token punctuation">.</span><span class="token property-access">length</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">renderSvgDataToCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
<span class="token operator">-</span>          <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>canvasFrames<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>          <span class="token function">generateGif</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token keyword">const</span> <span class="token function-variable function">generateGif</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> gif <span class="token operator">=</span> <span class="token keyword control-flow">await</span> modernGif<span class="token punctuation">.</span><span class="token method function property-access">encode</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token operator">+</span>        width<span class="token operator">:</span> <span class="token number">400</span><span class="token punctuation">,</span>
<span class="token operator">+</span>        height<span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span>
<span class="token operator">+</span>        frames<span class="token operator">:</span> canvasFrames<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">frame</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
<span class="token operator">+</span>          <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> imageData<span class="token operator">:</span> frame<span class="token punctuation">,</span> delay<span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token operator">+</span>        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">+</span>      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> frames <span class="token operator">=</span> <span class="token keyword control-flow">await</span> gif<span class="token punctuation">;</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> blob <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Blob</span><span class="token punctuation">(</span><span class="token punctuation">[</span>frames<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">'image/gif'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token constant">URL</span><span class="token punctuation">.</span><span class="token method function property-access">createObjectURL</span><span class="token punctuation">(</span>blob<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token operator">+</span>      <span class="token keyword">const</span> image <span class="token operator">=</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">getElementById</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">+</span>      <span class="token keyword">const</span> link <span class="token operator">=</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">getElementById</span><span class="token punctuation">(</span><span class="token string">'link'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token operator">+</span>      image<span class="token punctuation">.</span><span class="token property-access">src</span> <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token operator">+</span>      link<span class="token punctuation">.</span><span class="token property-access">href</span> <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token operator">+</span>    <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token function">renderSvgDataToCanvas</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token method function property-access">fromTo</span><span class="token punctuation">(</span><span class="token string">'#rect'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> x<span class="token operator">:</span> <span class="token operator">-</span><span class="token number">50</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> duration<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> x<span class="token operator">:</span> <span class="token number">350</span><span class="token punctuation">,</span> ease<span class="token operator">:</span> <span class="token string">'power.ease2'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code>

Using modernGif.encode you can pass an array of data onto frames and define a delay for each frame, I’ve chosen to add a delay of 0 seconds.

The next part of the code deals with converting the modernGif.ecode data and converting it to “yet another” MIME type, this time image/gif.

Once I have a final “blob” of data that represents my animated GIF I convert it to a URL and then set the src and href of the image and link elements so I can see and download the GIF in the browser.

Converting to GIF

Frame Rate

You might notice the final GIF runs quite slowly, this is because animations that run in the browser will typically play back at 60 frames per second (fps), whereas GIFs typically run at a much slower frame rate, 12 or 24fps.

To “drop” some frames of the animation I use an array filter and JavaScript remainder operator to determine if the index is divisible by a certain number, in my case, I chose 6. Indexes that aren’t divisible by 6 are filtered out of the array. The resulting animated GIF, while a little clunky, will playback much faster.

<code class="javascript language-javascript"><span class="token keyword">const</span> <span class="token function-variable function">generateGif</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> gif <span class="token operator">=</span> <span class="token keyword control-flow">await</span> modernGif<span class="token punctuation">.</span><span class="token method function property-access">encode</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    width<span class="token operator">:</span> <span class="token number">400</span><span class="token punctuation">,</span>
    height<span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span>
    frames<span class="token operator">:</span> canvasFrames
<span class="token operator">+</span>       <span class="token punctuation">.</span><span class="token method function property-access">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> index <span class="token operator">%</span> <span class="token number">6</span> <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">frame</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
        <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> imageData<span class="token operator">:</span> frame<span class="token punctuation">,</span> delay<span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> frames <span class="token operator">=</span> <span class="token keyword control-flow">await</span> gif<span class="token punctuation">;</span>
  <span class="token keyword">const</span> blob <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Blob</span><span class="token punctuation">(</span><span class="token punctuation">[</span>frames<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> <span class="token string">'image/gif'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token constant">URL</span><span class="token punctuation">.</span><span class="token method function property-access">createObjectURL</span><span class="token punctuation">(</span>blob<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> image <span class="token operator">=</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">getElementById</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> link <span class="token operator">=</span> <span class="token dom variable">document</span><span class="token punctuation">.</span><span class="token method function property-access">getElementById</span><span class="token punctuation">(</span><span class="token string">'link'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  image<span class="token punctuation">.</span><span class="token property-access">src</span> <span class="token operator">=</span> src<span class="token punctuation">;</span>
  link<span class="token punctuation">.</span><span class="token property-access">href</span> <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code>

And that’s how you can go from GSAP SVG Animation to Animated GIF via the HTML Canvas!

If you have any questions about anything I’ve described in this post feel free to find me on Twitter/X: @PaulieScanlon.



Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button
WP Twitter Auto Publish Powered By : XYZScripts.com
SiteLock