Canvas Drawing with GIF Animation Issues
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.