Search code examples
javascripthtml5-canvasfabricjsgifsprite-sheet

how to convert Gif to spritesheet using Javascript


i am getting frames from gif using Libgif. and then i am appending those frames in the div with Id = frames. then i am taking those frames and trying to add each frames one after the other in canvas to make a spritesheet.

in the end i am getting an image in canvas but instead of getting different frames i am getting same image in the spritesheet. Please help me find the issue.

I had taken canvas width 10000 assuming a gif wont have frames more than 100.

c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.beginPath();
var imageGiF = "";
var total = 0;
let canvasWidth = 0;
let canvasHeight = 0;
$('div.gifimage img').each(function(idx, img_tag) {
  var total = 0;
  if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
    var rub = new SuperGif({
      gif: img_tag,
      progressbar_height: 0
    });
    rub.load(function() {
      for (let i = 0; i < rub.get_length(); i++) {
        total += 1;
        rub.move_to(i);
        // var canvas = cloneCanvas(rub.get_canvas());
        var canvas = rub.get_canvas().toDataURL("image/png");
        img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class= frameimages>');

        $("#frames").append(img);

      }
      var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
      var totalimages = frameimages.length;

      x = 0;
      y = 0;
      for (let i = 0; i < frameimages.length; i++) {
        img = document.getElementById("gifframe" + i + "");
        img.onload = function() {
          ctx.drawImage(img, i * 100, 0, 100, 100);
          total++;
          console.log(total);
        }
      }
      totalwidth = (total) * 100;
      c.width = totalwidth;
      c.height = 100;
      setTimeout(() => {
        imageGiF = c.toDataURL("image/png");
        console.log(imageGiF);
        // addBgimg(imageGiF)
      }, 10);
    });
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div class="gifimage" id="placehere">
  <img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">

</div>
<div id="frames" class="classGIF"></div>
<canvas id='myCanvas' width="10000" height="300"></canvas>


Solution

  • You were looping through the images, using img in your event handler. However, this variable img in the outer scope was overridden by every loop, until it was finished looping through everything, then img was stuck on the last frame added.
    Then when the event handler triggered, it added the last frame in every instance, because that was the value of img at that point. The loop was done before the images could load.

    By adding it to it's own scope by wrapping it in a function, the variable is preserved.

    I also modified your code to store the DOM img elements in an array, so you don't need expensive DOM lookups which makes your code a tad bit faster.

    I added comments in the code to explain my changes.

    c = document.getElementById("myCanvas");
    ctx = c.getContext("2d");
    ctx.clearRect(0, 0, ctx.width, ctx.height);
    ctx.beginPath();
    var imageGiF = "";
    var total = 0;
    let canvasWidth = 0;
    let canvasHeight = 0;
    $('div.gifimage img').each(function(idx, img_tag) {
      var total = 0;
      if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
        var rub = new SuperGif({
          gif: img_tag,
          progressbar_height: 0
        });
        rub.load(function() {
          // An array for the image references
          let images = [];
          // Keep the reference to save on expensive DOM lookups every iteration.
          let frames = $("#frames");
          for (let i = 0; i < rub.get_length(); i++) {
            total += 1;
            rub.move_to(i);
            // var canvas = cloneCanvas(rub.get_canvas());
            var canvas = rub.get_canvas().toDataURL("image/png");
            img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class="frameimages">');
            
            // Use the reference to append the image.
            frames.append(img);
            
            // Add image to images array with the current index as the array index.
            // Use the jQuery get method to get the actual DOM element.
            images[i] = img.get(0);
          }
          var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
          var totalimages = frameimages.length;
    
          x = 0;
          y = 0;
          
          // Loop through all the images in the image array
          // Using a scope so the reference to img won't be overridden.
          images.forEach((img, index) => {
            img.onload = () => {
              ctx.drawImage(img, index * 100, 0, 100, 100);
              total++;
              console.log(total);
            } 
          })
          
          
          totalwidth = (total) * 100;
          c.width = totalwidth;
          c.height = 100;
          setTimeout(() => {
            imageGiF = c.toDataURL("image/png");
            console.log(imageGiF);
            // addBgimg(imageGiF)
          }, 10);
        });
      }
    });
    #frames { display:none;}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
    <div class="gifimage" id="placehere">
      <img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">
    
    </div>
    <div id="frames" class="classGIF"></div>
    <canvas id='myCanvas' width="10000" height="300"></canvas>