Canvas Drawing with GIF Animation Issues

· 2 min read

Recently, I’ve been working on a mini-program with a poster sharing feature. The basic approach is to draw images and text onto a poster Canvas, which also supports output as dataURL, making downloads simple. However, one type of image resource is GIF, which is a dynamic resource containing multiple frames. Testing shows that GIF resources work fine when drawing posters, but the question is: which frame does a GIF animation use when generating a static image, and how can we change this? Let me explore this during the holidays.

canvas.getContext(‘2d’).drawImage

This is the function used for drawing posters. Since each frame of a GIF might be different, I examined several typical GIFs and found through comparison that when drawing, the first frame of the GIF is used.

On Mac, you can use Preview to preview GIFs, and the displayed number represents the frame count.

Specifying Which GIF Frame to Draw

Canvas or HTMLImageElement don’t provide this capability natively, but the third-party library libgif can solve this problem.

The main approach is to use the function superGifEl.move_to(50); superGifEl.pause(); to move to a specified frame, then stop playback to get the canvas in that context.

Here’s a complete example:

 const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const parentEl = document.createElement('div');
      parentEl.style.display = 'none';
      const imgEl = new Image(60, 45);
      imgEl.src = './5-160914192R4.gif';
      parentEl.appendChild(imgEl);
      const superGifEl = new SuperGif({ gif: imgEl });
      superGifEl.load(function () {
        superGifEl.move_to(50);
        superGifEl.pause();
        drawImageForGif();
        console.log('The number of frames in the gif:' + superGifEl.get_length());
      });

      function drawImageForGif() {
        canvas.width = imgEl.naturalWidth;
        canvas.height = imgEl.naturalHeight;
        console.log(superGifEl.get_canvas().toDataURL('image/png'));
        ctx.drawImage(
          superGifEl.get_canvas(),
          0,
          0,
          imgEl.naturalWidth,
          imgEl.naturalHeight
        );
      }

      function downloadURI(uri, name) {
        var link = document.createElement('a');
        link.download = name;
        link.href = uri;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        delete link;
      }
      function download() {
        downloadURI(canvas.toDataURL('image/png'), 'wp.png');
      }

A Small Pitfall

The reason we need const parentEl = document.createElement('div'); is because libgif’s implementation involves using the parent element of the GIF resource. Since we’re using the Image constructor here rather than document.createElement, there’s no parent element. To avoid console errors, we add a hidden parent element.

WeChat Mini Program Component - Painter

WeChat mini programs support Canvas drawing, but the native Canvas APIs are still cumbersome to operate. For convenience, I use the Painter component.

The Painter component supports image resources in URL format, and with SuperGif => Canvas => toDataURL, this problem is solved in mini programs.

Final Thoughts

With the above solution, when sharing posters with GIF resources, you can flexibly control which frame to select.

Authors
Developer, digital product enthusiast, tinkerer, sharer, open source lover