上篇文章咱们曾经学习了 GraphicsMagick 中的许多函数,也说过 GraphicsMagick 是 ImageMagick 的一个分支,所以他们很多的函数都是一样的应用形式和成果,类似的内容咱们也就不再多说了,感兴趣的敌人能够间接查阅官网文档。
这篇文章咱们要学习的是一个具体的案例,也是我在理论业务开发中所接触过的一个案例。具体的成果就是对于微信小游戏和小程序来说,不能间接地应用动静 Gif 图片,一张 Gif 图片在小游戏或小程序中是不会动的。所以在咱们公司的游戏开发中,须要一张将整个 Gif 动图的每一帧拆出来的图片拼成一张精灵图交给前端,由他们来应用 JS+CSS 的能力动静地循环咱们拆帧后的图片,从而造成动图的成果。
业务需要就是这么个状况,当然,最初的解决方案也正是应用了 ImageMagick 来实现的。话不多说,咱们间接先看代码。
GIF 图拆帧
原始的图片是这样的一张动图:
<code class="php">$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 图。
<code class="php">$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
各自媒体平台均可搜寻【硬核项目经理】