这篇文章我们要学习的是一个详细的案例,也是我在实际业务开拓中所打仗过的一个案例。详细的效果便是对付微信小游戏和小程序来说,不能直接地利用动态 Gif 图片,一张 Gif 图片在小游戏或小程序中是不会动的。以是在我们公司的游戏开拓中,须要一张将全体 Gif 动图的每一帧拆出来的图片拼成一张精灵图交给前端,由他们来利用 JS+CSS 的能力动态地循环我们拆帧后的图片,从而形成动图的效果。
业务需求便是这么个情形,当然,末了的办理方案也正是利用了 ImageMagick 来实现的。话不多说,我们直接先看代码。
原始的图片是这样的一张动图:

$imgPath = '../img/4.gif';$imagick = new \Imagick($imgPath);$imagick = $imagick->coalesceImages();$imageCount = $imagick->count();echo 'image count:', $imageCount, PHP_EOL; // image count:51$imgAttrs = [ 'width' => $imagick->getImageWidth(), 'height' => $imagick->getImageHeight(), 'frame_count' => $imageCount,];$column = 5;if ($imageCount < $column) { $column = $imageCount;}$row = ceil($imageCount / $column);$spImgWidth = $imgAttrs['width'] $column;$spImgHeight = $imgAttrs['height'] $row; // 创建图片$spImg = new \Imagick();$spImg->setSize($spImgWidth, $spImgHeight);$spImg->newImage($spImgWidth, $spImgHeight, new \ImagickPixel('#ffffff00'));$spImg->setImageFormat('png');$i = 0;$h = 0;$cursor = 0;do { if ($i == $column) { $i = 0; $h++; } if($cursor == 0){ // 保存第一帧图片 $imagick->writeImage($imgPath . '.first.png'); } // 保存全部的图片帧到一张 png 图片中 $spImg->compositeImage($imagick, \Imagick::COMPOSITE_DEFAULT, $i $imgAttrs['width'], $h $imgAttrs['height']); $i++; $cursor++;} while ($imagick->nextImage());$spImg->writeImage($imgPath . '.png');
实例化 Imagick 工具就不用多说了,我们首先调用的是 coalesceImages() 这个方法。它的浸染是返回合成后的 Imagick 工具。通过这个方法,我们就得到了全体 GIF 图里面的全部每一帧图片的信息。这时,利用 count() 方法,就可以得到图片中的所有图片帧的个数。比如我们测试的这张图片就有 51 帧。
然后打算精灵图的行和列以及相应须要的宽高,比如我们以 5 列为基准,也便是一行放五张拆帧出来的图片,这样一共须要 11 行才放得下末了天生的精灵图。同理,宽高也因此拆出来的图片宽高乘以相应的列和行数。
接着,根据打算出来的宽高天生一张新的图片,作为精灵图的背景图,利用 newImage() 函数设置图片宽高及背景透明。利用 setImageFormat() 方法设置图片的格式为 PNG 格式,利用 PNG 紧张是为了透明,实在按我们这样紧密排列的图片来说,不用透明也可以,但某些运用中比如网站前端须要的精灵图可能不同的图片之前是须要一定间隔的,以是一样平常会利用透明的底图。
然后便是一个循环,也便是循环那 51 张拆帧出来的图片,利用 nextImage() 不断地获取原始 GIF 图中的下一帧图片,并将他们组合保存在上面新建的背景图片中,每一帧的图片位置也是通过单帧图片的宽高与行列情形打算出来的。在这段代码中,我们还保存了第一帧的图片,当然,这也是业务须要,你可以随时保存任何一张每帧的图片。
末了,利用 writeImage() 保存图片。输出的图片便是下面的这个样子:
组合成动态 GIF 图
以上的业务功能是我在开拓中实际利用过的功能,当然,除了可以对 GIF 图进行拆帧之外,我们也可以将多张图片组合成一个动态的 GIF 图。
$gifImagek = new Imagick();$gifImagek->setFormat('GIF');for($i=1;$i<=5;$i++){ $img = new Imagick('../img/3'.$i.'.jpeg'); $img->setImageDelay(100); $gifImagek->addImage($img);}$gifImagek->writeImages("../img/5.gif", true);$gifImagek->writeImages("../img/52.gif", false);
这段代码就比较大略了,依然还是创建一个图片,并且指定格式为 GIF 图片。然后循环添加图片,这里我们利用的是上篇文章中 GraphicsMagick 中操作过的那些图片。setImageDelay() 用于设置图片显示间隔,这里我们设置的是 100 毫秒,然后再利用 addImage() 将图片添加到我们新创建的 GIF 图画布中。
末了保存图片的时候,须要利用 writeImages() 进行保存,它的浸染是保存这种连续的多张图片。它的第二个参数是指定是否将图片保存到一张图片中,如果是 false 的话,就类似于拆帧的效果,不过会将图片一张一张的分开保存,比如 52-1.gif 、 52-2.gif 这样。
末了天生的动图便是这样的:
总结
本日的内容故意思吧,不是那些烂大街的缩放、加水印、验证码之类的功能,而是比较好玩的对付 GIF 图的操作。说实话,在业务开拓中类似的业务场景还是很多的,就像自动天生精灵图这种功能就完备可以利用 ImageMagick 来实现,而且都是 ImageMagick 扩展中自带的函数就可以搞定了,非常方便。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/5.利用ImageMagick操作gif图.php
参考文档:
https://www.php.net/manual/zh/book.imagick.php