GifCreator: создаём динамические GIF-ки своими руками на PHP

Введение

Здравствуйте.

Встала задача – показывать на сайте GIF-ку с текстом, который со временем должен меняться. В случае статического рисунка проблем нет, смотрите хотя бы наш пост PHP-торт на день Рождения!, в котором рассказано как писать текст, в том числе Кириллицей, на картинке с возможностью поворота и центрирования его по нужным осям.

Но на GIF’ке с несколькими кадрами как мы текст напишем? Надо доставить каждый кадр, обновлять на нём текст и собирать кадры в GIF-ку снова.

Вот к примеру дана нам такая GIF-ка всего с 2-мя кадрами, закольцованными, меняющимися примерно каждые 2-3 секунды:

openprice.gif

Внизу вы видите текст:

До окончания акции: 3 дня
Осталось: 5 курсов

Сразу понятно, что тут надо ежедневно менять кол-во оставшихся дней. А кол-во оставшихся курсов может поменяться в любой момент времени, и тогда нам надо будет обновить картинку соответственно.

Что же делать?

Как это сделать, спросите вы? GifCreator.php нам в руку, отвечу я. Проект на GitHub’е здесь: https://github.com/Sybio/GifCreator

С помощью этой небольшой либы можно создавать GIF-ки на PHP своими руками.

Пройдемся по циклу создания GIF-ки, а потом напишем собственный скрипт, который будет автоматически обновлять нашу GIF-ку кол-вом оставшихся до окончания акции дней и кол-вом оставшихся курсов.

Основы создания новой GIF-ки

Итак, процесс создания GIF-ки с помощью GifCreator таков:

Определяем массив, содержащий в себе пути к файлам рисунков, GD Image-ресурсам (созданным с помощью таких функций как imagecreatefromjpeg и т.п.), а также сырыми (raw) данными содержимого рисунков.
К примеру:

$frames = array(
    imagecreatefrompng('images/pic1.png'), // ресурс GD Image 
    'images/pic2.png', // путь к рисунку на диске 
    file_get_contents('images/pic3.jpg'), // сырое содержимое рисунка  
    'https://mydom.ru/images/pic4.jpg', // URL-путь к рисунку в сети
);

Далее определяем массив с продолжительностью кадров в миллисекундах.

$durations = array(40, 80, 40, 20);

Далее создаём саму GIF-ку

$gc = new GifCreator\GifCreator();
$gc->create($frames, $durations, 5);

Третий параметр метода .create() определяет количество повторов кадров GIF-ки. В данном случае мы повторяем кадры GIF-ки 5 раз. Можно задать 0 для бесконечного повтора.

И наконец получаем результат:

$gif = $gc->getGif();

Теперь мы можем сохранить нашу GIF-ку на диске:

file_put_contents('images/pic.gif', $gif);

или вывести прямиком в браузер:

header('Content-type: image/gif');
die($gif);

Важные особенности

  • Прозрачность основывается на первом фрэйме и сохранится только в том случае, если мы задаём фрэймы с одинаковой прозрачностью.
  • Размеры генерируемой GIF-ки также базируются на первом фрэйме, то есть все остальные фреймы будут подогнаны по размеру к нему.

Практика

Итак, мы делаем естественный шаг от теории к практике. Возьмем нашу заданную вначале поста GIF-ку и напишем на ней актуальный на данный момент текст. Поехали по шагам.

Первое, разбираем GIF-ку на фреймы. Как это сделать, спросите вы. Без понятия, отвечу я. Гоголь или Яндекс вам в помощь.

Получаем наши заветные фрэймы:

Frame0.gif 
Frame0.gif  
Frame1.gif
Frame1.gif

В любом тектсовом, ой, графическом редакторе очищаем нужные области от текста, и сохраняем результат в файлики Frame0.png и Frame1.png.

Результат может выглядеть так:

Frame0.png   Frame1.png

Выкладываем файлики в заданное место на сервер и пишем следующий скрипт,
в комментариях к которому многое поясняется, а пару моментов я дам ниже:

// Подключаем класс создания GIF-ок
include('GifCreator.php');
 
// Определим сколько дней осталось до окончания акции
 
// сейчас
$datetime1 = new DateTime();
 
// до какого числа: выберем случайный промежуток 0 - 7 дней
$datetime2 = new DateTime(date('Y-m-d', time() + rand(80000, 604800)));
 
$interval = $datetime1->diff($datetime2);
$days_left = $interval->format('%d') + 1;
 
// определим окончание для слова ДЕНЬ (дня/дней/день)	
$ln = substr($days_left, -1, 1); // последняя цифра числа
if($days_left < 21 && $days_left > 4)
	$days = 'дней';
elseif($ln >= 2 && $ln < 5)
	$days = 'дня';
elseif($ln == 1)
	$days = 'день';
else
	$days = 'дней';
 
// сколько курсов осталось
mt_srand(time());
$num = toZero(15 - mt_rand(0, 15));
 
// готовим наши динамические тексты, функцию Suffix смотрите внизу
$text1 = "До окончания акции: $days_left $days";
$text2 = "Осталось: $num курс" . Suffix($num);
 
/*
 
	Для Кириллицы - используем TTF-шрифты,
	Times New Roman Bold для данного примера.
	Путь по умолчанию для шрифта - в текущем каталоге.
 
*/
 
putenv('GDFONTPATH=' . realpath('.')); 
$font = 'timesbd'; // название шрифта без расширения ttf
 
/*
 
 Размер символов ($size) и начальная координата по вертикали ($y),
	подбираются опытным путём
 
*/
 
$size = 11;
$y = 310;
 
// Открываем первый фрэйм и добавляем тексты по центру внизу друг под другом чёрным цветом
$frame0 = imagecreatefrompng('gif/Frame0.png');
$black = imagecolorallocate($frame0, 0, 0, 0);
 
$x1 = getX($size, 0, $text1, $font, $frame0);
$x2 = getX($size, 0, $text2, $font, $frame0);
imagettftext($frame0, $size, 0, $x1, $y, $black, $font, $text1);
imagettftext($frame0, $size, 0, $x2, $y + 20, $black, $font, $text2);
 
// Тоже самое проделываем для второго фрэйма, координата по X нам уже известна
// Помним, что фрэймы у нас должны быть всегда одного размера
$frame1 = imagecreatefrompng('gif/Frame1.png');
$black = imagecolorallocate($frame1, 0, 0, 0);
imagettftext($frame1, $size, 0, $x1, $y, $black, $font, $text1);
imagettftext($frame1, $size, 0, $x2, $y + 20, $black, $font, $text2);
 
 
/*
 
	Итак, добавляем созданные нами кадры в массив и определяем
	продолжительность задержки между кадрами в 2 секунды
 
*/
 
$frames = array($frame0, $frame1);
$durations = array(200, 200);
 
/*
	Создаём GIF-ку, анимация зациклена (3-ий параметр в create() - 0)
*/
 
$gc = new GifCreator\GifCreator();
$gc->create($frames, $durations, 0);
 
// Получаем GIF-ку и выводим её в браузер
$gif = $gc->getGif();
 
header('Content-type: image/gif');
die($gif);
 
/* ********************************
 
	Вспомогательные функции
 
******************************** */
 
/*
 
	getX($size, $angle, $text, $font, $im)
	Центруем текст по оси X (горизонтали) на рисунке
 
	Вход:
		@size - размер символов текста
		@angle - угол поворота текста
		@text - сам текст
		@font - название шрифта
		@im - ресурс GD Image
 
	Выход: нужная координата по оси X
 
*/
 
function getX($size, $angle, $text, $font, $im)
{
	$ts = imagettfbbox($size, $angle, $font, $text);
	$dx = abs($ts[2] - $ts[0]);
	return (imagesx($im) - $dx) / 2;
}
 
/*
 
	Возвращает 0, если аргумент меньше нуля, иначе - сам аргумент
 
*/
 
function toZero($val)
{
  return ($val > 0 ? $val : 0);
}
 
/*
 
	Вернуть правильное окончание слова `курс`
	в зависимости	от переданного числа
 
	Правильно для числа от 0 до 110 (> 100 надо дорабатывать)
 
	@returns {String} окончание
 
*/
 
function Suffix($num)
{
	if($num > 10 && $num < 20)
		return 'ов';
 
	// смотрим последнюю цифру
  $n = intval(substr(strval($num), -1, 1));
 
 	return $n == 1 ? '' : ($n < 5 && $n > 0 ? 'а' : 'ов');
}

Файл GifCreator.php с классом GifCreator можно скачать с GitHub’а – ссылку я давал выше, либо взять с архива с примерами – ссылку я даю ниже. 😉

Также при создании картинок с текстом необходимо подключать нужные шрифты, в данном примере мы подключили шрифт Times New Roman Bold из файла timesbd.ttf. Путь к файлу шрифта надо задавать либо напрямую в имени шрифта, либо через переменную окружения GDFONTPATH, как и показано в нашем примере. В Windows тут надо проявлять большую осмотрительность и осторожность.

В данном примере меняющиеся данные для текстов мы создаём рандомно, в реальных проектах они конечно же вычисляются на основе реальных вещей.

Результат

Вот результат работы нашего скрипта.

GifCreate

Заметьте, что при каждом обновлении странички надпись будет меняться.

Также при нажатии на картинку она обновится и покажет новые надписи, но не злоупотребляйте этим, всё таки хоть и малая, а нагрузка на сервак какая-никакая создаётся, так что стоит наверное рассмотреть возможность кэширования таких динамически создаваемых GIF-ок, особенно для страничек с большими нагрузками.

Скачивайте полный рабочий архив со всеми файликами ресурсов и скриптами отсюда:
https://beotiger.com/download/gifcreate, заваривайте крепкий чай с вишнёвым вареньем и наслаждайтесь жизнью и классной погодой за окном.

А мне остаётся только раскланяться с вами, поздравить с наступающими праздниками и началом чемпионата мира по футболу.

Вуаля, адью, гудбай, ауфидерзеен, до свиданья, чаю, пока.