我非常感谢 Sean Ja 的回答。(他应该得到更多的支持。)然后我想让代码更具可读性和可配置性(并支持透明 gif 上的文本并自动居中文本):
use Carbon\Carbon;
class CountdownGifHelper {
const DELAY = 100; /* Why was this labeled as 'milliseconds' when it seems like a value of 100 here causes 1 frame to be shown per second? */
const MAX_FRAMES = 120;
/**
*
* @param string $bgImg
* @param \DateInterval $interval
* @param array $fontArr
* @param array $frames
* @param array $delays
* @param string $format
*/
public function addFrame($bgImg, $interval, $fontArr, &$frames, &$delays, $format) {
$image = imagecreatefrompng($bgImg); //Each frame needs to start by creating a new image because otherwise the new numbers would draw on top of old ones. Here, it doesn't really matter what the PNG is (other than for size) because it's about to get filled with a new color.
$text = $interval->format($format);
ob_start();
imageSaveAlpha($image, true);
$backgroundColor = $fontArr['backgroundColor'];
imagefill($image, 0, 0, $backgroundColor); //https://stackoverflow.com/a/17016252/470749 was a helpful hint
imagecolortransparent($image, $backgroundColor);
$this->insertCenteredText($image, $fontArr, $text);
//imagettftext($image, $font['size'], $font['angle'], $font['x-offset'], $font['y-offset'], $font['color'], $font['file'], $text);//this was the old way
imagegif($image); //The image format will be GIF87a unless the image has been made transparent with imagecolortransparent(), in which case the image format will be GIF89a.
$frames[] = ob_get_contents();
ob_end_clean();
$delays[] = self::DELAY;
}
/**
*
* @param resource $image
* @param array $fontArray
* @param string $text
*/
public function insertCenteredText(&$image, $fontArray, $text) {
$image_width = imagesx($image);
$image_height = imagesy($image);
$text_box = imagettfbbox($fontArray['size'], $fontArray['angle'], $fontArray['file'], $text); // Get Bounding Box Size
$text_width = $text_box[2] - $text_box[0];
$text_height = $text_box[7] - $text_box[1];
// Calculate coordinates of the text https://stackoverflow.com/a/14517450/470749
$x = ($image_width / 2) - ($text_width / 2);
$y = ($image_height / 2) - ($text_height / 2);
imagettftext($image, $fontArray['size'], $fontArray['angle'], $x, $y, $fontArray['color'], $fontArray['file'], $text);
}
/**
*
* @param int $timestamp
* @param string $bgImg
* @param array $fontArray
* @return string [can be used by Laravel response()->make($gifString, 200, $headers)]
*/
public function getAnimatedGif($timestamp, $bgImg, $fontArray) {
$future_date = Carbon::createFromTimestamp($timestamp);
$time_now = time();
$moment = new \DateTime(date('r', $time_now));
$frames = [];
$delays = [];
for ($i = 0; $i <= self::MAX_FRAMES; $i++) {
$interval = date_diff($future_date, $moment);
if ($future_date < $moment) {
$this->addFrame($bgImg, $interval, $fontArray, $frames, $delays, '00 : 00 : 00');
$loops = 1; //stay stuck on this frame
break;
} else {
$this->addFrame($bgImg, $interval, $fontArray, $frames, $delays, '%H : %I : %S');
$loops = 0; //infinite loop
}
$moment->modify('+1 second');
}
$animatedGif = new \App\Helpers\AnimatedGif($frames, $delays, $loops, 0, 0, 0);
return $animatedGif->getAnimation();
}
/**
* ONEDAY allow config via params
* @param resource $image
* @return array
*/
public function getFontArray($image) {
$fontArr = [
'file' => resource_path('assets/fonts/Kanit-Regular.ttf'),
'size' => 30,
//'x-offset' => 5,
//'y-offset' => 30,
'color' => imagecolorallocate($image, 90, 90, 90), //gray
'backgroundColor' => imagecolorallocate($image, 0, 0, 0), //white. Must match the arguments provided to AnimatedGif (such as 0,0,0).
'angle' => 0,
];
return $fontArr;
}
}