Canvas绘图包含GIF动画问题

最近做的小程序有个海报分享feat,大致做法是将一些图片+文本都绘制在一个海报Canvas上,Canvas同时支持输出为dataURL,因此下载也就很简单。但图片资源中有一种是GIF,它是动态资源,包含多帧,测试发现GIF资源在绘制海报时没有问题,但疑问是GIF动画生成静态图片时使用的哪一帧,同时如果想改怎么做呢?借着假期,了解下。

canvas.getContext(‘2d’).drawImage

绘制海报使用的即该函数,因为GIF的每帧可能是不同的,因此查看几个典型的GIF,对比发现,绘制时,使用的会是GIF的第一帧。

在Mac下可以使用preview 来预览GIF,显示的数字即帧数。

指定绘制的GIF帧

本身Canvas或者HTMLImageElement没有提供这种能力,但是第三方类库libgif可以解决该问题。

主要是使用函数superGifEl.move_to(50); superGifEl.pause();移动到指定帧后,再停止播放,获得该上下文下的canvas。

以下为完整例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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');
}

一个小坑

之所以需要const parentEl = document.createElement('div');,是因为libgif的实现中,牵扯到使用GIF资源的父元素,因为这里使用的Image构造函数,而不是document.createElement,因此没有父元素,为了避免控制台报错,所以增加了隐藏的父元素。

微信小程序组件-Painter

微信小程序中支持Canvas绘图,但Canvas原生API操作起来还是繁琐,为了方便,这里我使用Painter组件来操作。

Painer组件中对于图片资源支持URL形式,而SuperGif=>Canvas=>toDataURL,因此小程序中这问题就有解了。

写在最后

如上方案,在海报分享时,遇到GIF资源,就可以灵活控制选择哪一帧了。