如何计算GIF动画中方块实现加减速效果的每一帧x坐标值
问题原因
你调整后的代码出现往返运动,是因为位移计算逻辑完全错误:你将匀速位移值直接与当前速度做乘法,当速度随时间下降至0时,x值会从峰值回落至0,自然出现往返效果,不符合位移是随时间累积的基本逻辑。
解决方案
这里提供两种常用实现方案,你可以按需选择:
方案1:动画缓动插值(推荐,易调整)
不需要计算物理参数,直接对运动进度做缓动变换,是动画领域最常用的加减速实现方案:
- 先计算当前运动进度
$progress,取值范围 0(运动开始)~1(运动结束) - 对进度做缓动函数变换,得到缓动后的进度
- 用缓动进度乘以总距离得到当前x坐标
常用缓动效果对应代码:
- 减速效果(初速度快,逐渐变慢到停止):
$easedProgress = 1 - pow(1 - $progress, 2) - 加速效果(初速度慢,逐渐变快):
$easedProgress = pow($progress, 2) - 先加速后减速:
$easedProgress = $progress < 0.5 ? 2 * pow($progress, 2) : 1 - pow(-2 * $progress + 2, 2) / 2
修改后核心代码示例(以减速效果为例):
$fps = 25; $duration = 2000; $distance = 100; $nbFrames = $duration / 1000 * $fps; $imFrames = new Imagick(); for ($i = 0; $i < $nbFrames; $i++) { $currentTime = $duration / ($nbFrames - 1) * $i; // 计算当前进度 0~1 $progress = $currentTime / $duration; // 应用减速缓动 $easedProgress = 1 - pow(1 - $progress, 2); // 计算最终x坐标,限制最大不超过distance避免超出范围 $x = min($distance, $easedProgress * $distance); $draw = new ImagickDraw(); $draw->setFillColor('#ff0000'); $draw->rectangle( $x, 10, $x + 10, 20 ); $imFrame = new Imagick(); $imFrame->newImage(400, 40, '#5b5b5b'); $imFrame->drawImage($draw); $imFrame->setImageDelay(100 / $fps); $imFrame->setImageFormat('gif'); $imFrame->setImageDispose(3); $imFrames->addImage($imFrame); } header('Content-type: image/gif'); $imFrames->setFormat('gif'); echo $imFrames->getImagesBlob(); exit();
方案2:物理匀变速位移公式
如果需要严格符合匀变速物理运动规律,使用正确的位移公式:x = 初速度 * 时间 + 0.5 * 加速度 * 时间²
注意需要先保证你的参数匹配总位移要求:匀变速运动总位移满足公式 $distance = ($initialVelocity + $finalVelocity) * $duration / 2,参数不匹配的话要么调整参数,要么最后对x做范围限制。
示例代码:
$fps = 25; $initialVelocity = 100; // 单位px/s,按总位移要求计算得到:2*$distance/$duration = 2*100/2 =100 $finalVelocity = 0; $duration = 2000; // 单位ms $distance = 100; // 加速度单位 px/s² $acceleration = ($finalVelocity - $initialVelocity) / ($duration / 1000); $nbFrames = $duration / 1000 * $fps; $imFrames = new Imagick(); for ($i = 0; $i < $nbFrames; $i++) { $currentTime = $duration / ($nbFrames - 1) * $i; $currentTimeSec = $currentTime / 1000; // 匀变速位移公式 $x = $initialVelocity * $currentTimeSec + 0.5 * $acceleration * pow($currentTimeSec, 2); // 限制范围避免参数误差导致超出 $x = max(0, min($distance, $x)); // 后续绘制逻辑不变 $draw = new ImagickDraw(); $draw->setFillColor('#ff0000'); $draw->rectangle( $x, 10, $x + 10, 20 ); $imFrame = new Imagick(); $imFrame->newImage(400, 40, '#5b5b5b'); $imFrame->drawImage($draw); $imFrame->setImageDelay(100 / $fps); $imFrame->setImageFormat('gif'); $imFrame->setImageDispose(3); $imFrames->addImage($imFrame); } header('Content-type: image/gif'); $imFrames->setFormat('gif'); echo $imFrames->getImagesBlob(); exit();
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

