图片动画效果如何实现及踩过的坑本文档描述在视频中叠加图片字幕图时可对图片应用的各类动画效果包括动画类型、参数说明及实现原理。一、动画类型总览类型名称支持方向支持循环支持次数marquee跑马灯left / right✓✓slideIn滑入left / right / up / down——rotate旋转left逆时针/ right顺时针✓✓bounce弹跳—✓✓blink闪烁—✓✓fadeIn渐入———fadeInOut渐入渐出———zoomIn缩放入场———none无动画———二、公共参数说明参数类型说明typeString动画类型取值见上表durationDouble动画时长秒。建议不超过图片显示窗口时长否则动画会被截断delayDouble动画延迟秒。相对于图片出现时刻的偏移量实际动画开始 出现时刻 delaydirectionString方向仅 marquee / slideIn / rotate 有效其他类型忽略loopBoolean是否在图片显示期间持续循环仅 marquee / rotate / bounce / blink 有效iterCountIntegerloopfalse 时的播放次数默认 1仅 marquee / rotate / bounce / blink 有效三、各动画类型详解1. marquee — 跑马灯图片在水平方向持续匀速滚动一个duration周期内图片从画面一侧横穿至另一侧。方向left默认从右侧画外向左运动最终从左侧消失right从左侧画外向右运动最终从右侧消失循环控制looptrue在图片显示期间持续滚动loopfalse滚动iterCount次后停止实现原理利用mod函数对时间取模将当前时刻映射到[0, 画面宽度 图片宽度)范围内的偏移值使图片坐标周期性地从一侧移动到另一侧形成无缝循环滚动效果。2. slideIn — 滑入图片从画面外某个方向滑入到目标坐标在duration时间内完成入场之后保持静止。方向left默认从左侧画外滑入right从右侧画外滑入up从顶部画外滑入down从底部画外滑入实现原理根据方向计算起始坐标画面外的位置和目标坐标配置的 x/y在duration时间内通过线性插值在起始坐标和目标坐标之间平滑过渡。入场完成后坐标固定在目标位置。3. rotate — 旋转图片绕自身中心持续旋转duration为旋转一整圈所需的时长。方向right默认顺时针旋转left逆时针旋转循环控制looptrue持续旋转不停止loopfalse旋转iterCount圈后停止实现原理角速度 ±2π / duration正负对应顺/逆时针旋转角度随时间线性增长。为避免旋转时图片四角被画布裁剪产生残影画布会先扩展为图片对角线长度向上取偶数overlay 坐标同步补偿确保旋转中心始终对准目标位置。背景填充透明色。4. bounce — 弹跳图片在垂直方向按正弦函数规律上下振动振幅固定为20pxduration为一个完整振动周期。循环控制looptrue持续弹跳loopfalse弹跳iterCount次后停止在原始位置实现原理Y 坐标 目标 Y -|sin((t - 动画起始) * π / duration)| * 振幅利用绝对值正弦函数模拟物体弹跳的轨迹仅向上偏移无负向偏移。5. blink — 闪烁图片以duration为周期交替出现和消失形成闪烁效果。循环控制looptrue在图片显示期间持续闪烁loopfalse闪烁iterCount次后保持常驻显示实现原理通过 overlay 的enable表达式控制图片的显示与隐藏以duration为单位对时间做整除取模奇数帧隐藏、偶数帧显示或反之实现周期性开关切换。6. fadeIn — 渐入图片从完全透明逐渐变为完全不透明duration为渐入时长完成后保持完全显示。实现原理通过geq滤镜逐帧修改图片的 Alpha 通道值动画开始前alpha 0透明动画进行中alpha (当前时刻 - 动画起始) / duration线性增长动画结束后alpha 1完全不透明每一帧原始像素的 alpha 值乘以上述系数实现平滑的透明度过渡。7. fadeInOut — 渐入渐出前半段duration / 2从透明渐变到完全不透明后半段duration / 2从完全不透明渐变到透明duration结束后图片隐藏。实现原理与 fadeIn 相同均通过geq滤镜逐帧计算 Alpha 系数但系数分为两个阶段前半段(t - 起始) / (duration/2)从 0 到 1后半段1 - (t - 半程时刻) / (duration/2)从 1 到 0适合需要图片短暂出现后自然消失的场景。8. zoomIn — 缩放入场图片从尺寸为 0 逐渐缩放到原始尺寸以图片的配置坐标为中心向外展开duration为缩放时长完成后保持原始尺寸。实现原理通过scale滤镜逐帧动态修改图片的宽高缩放比例 (当前时刻 - 动画起始) / duration从 0 到 1为防止 scale0 时 FFmpeg 回退为原始尺寸导致首帧闪烁最小尺寸限制为 2pxoverlay 坐标动态补偿(原始尺寸 - 当前缩放尺寸) / 2确保缩放过程中图片始终以目标坐标为中心9. none — 无动画图片在指定时间段内静态显示不附加任何动画效果。所有动画参数均被忽略。四、时序关系说明视频时间轴 ──────────────────────────────────────────────────────────► │←── delay ──►│←────── duration ────────►│ startTime animStart animEnd endTime 图片出现 动画开始 动画结束 图片消失图片在startTime时刻出现endTime时刻消失动画实际开始时刻 startTime delayduration建议小于endTime - startTime否则动画在图片消失前尚未完成会被截断looptrue的动画marquee/rotate/bounce/blink会持续到图片消失时刻五、特殊情况处理情况处理方式type为空或未知值降级为无动画静态叠加duration未设置默认使用 1 秒delay未设置默认为 0即图片出现时立即开始动画iterCount未设置默认为 1 次rotate / zoomIn 无法读取图片尺寸rotate 使用坐标最大值估算对角线zoomIn 退化为固定坐标叠加GIF 动图输入需标记animatedtrueFFmpeg 会启用-ignore_loop 0和-stream_loop -1驱动循环播放六、踩坑记录坑1PNG 单帧图片时间戳恒为 0导致所有依赖时间的动画失效现象fadeIn / fadeInOut / zoomIn / rotate 等动画完全没有效果图片从第一帧就以最终状态静止显示。原因PNG 是单帧静态图片FFmpeg 读入后其视频流的时间戳t始终为 0无论视频播放到哪一秒动画表达式中的t都不会递增导致所有基于时间的动画表达式失效。解决方案在动画滤镜链前插入looploop-1:size1,setptsN/25/TB让 FFmpeg 以 25fps 的帧率无限循环单帧图片并通过setpts为每一帧生成单调递增的时间戳使后续滤镜中的时间变量t/T正常工作。坑2colorchannelmixer不支持时间变量fadeIn / fadeInOut / blink 无效果现象使用colorchannelmixeraaif(lt(t,...),...)尝试实现逐帧 alpha 渐变结果图片 alpha 始终为固定值渐变不生效。原因colorchannelmixer滤镜不支持在参数表达式中使用时间变量t它只会在滤镜初始化时求值一次不会逐帧重新计算。解决方案fadeIn / fadeInOut改用geq滤镜其a参数支持逐像素、逐帧动态计算可使用时间变量T注意 geq 中时间变量为大写 T。blink改用 overlay 的enable表达式控制图片整体的显示与隐藏彻底绕开 alpha 通道操作。坑3geq滤镜时间变量是大写T写成小写t不生效现象将colorchannelmixer替换为geq后渐变仍然不生效alpha 值始终为 0 或 1。原因geq滤镜中的时间变量是大写T而大多数其他滤镜如 overlay 的 enable 表达式使用小写t。写成小写t时geq将其视为未定义变量求值结果为 0导致 alpha 计算错误。解决方案在geq的a表达式中所有时间引用改为大写T。坑4动画时间基准用了相对delay实际动画在视频绝对时间轴上偏移错误现象配置了delay2、字幕startTime10的动画动画在第 2 秒就开始播放而不是第 12 秒。原因动画表达式中的时间条件直接使用了delay作为基准但 FFmpeg 的时间轴是视频的绝对时间delay只是相对于字幕出现时刻的偏移量必须加上startTime才能换算为绝对时间。解决方案统一计算animStart startTime delay所有动画表达式包括 fadeIn、fadeInOut、zoomIn、slideIn、rotate、bounce、marquee均以animStart作为时间基准。坑5rotate 旋转时图片四角被裁剪出现残影现象图片旋转过程中四个角周期性地消失又出现产生明显的闪烁残影尤其是宽高比悬殊的图片更为严重。原因FFmpeg 的rotate滤镜默认输出画布尺寸等于原图的宽高owiw:ohih当图片旋转到对角线方向时四个角会超出这个画布范围并被裁剪掉下一帧旋转回来时又重新出现形成残影。解决方案将输出画布扩展为图片对角线长度√(w²h²)向上取整到偶数确保任意旋转角度下图片内容都不会被裁剪。同时对 overlay 坐标进行补偿使旋转中心始终保持在目标位置。坑6rotate 扩展画布后背景色使用cnone实际渲染出黑色背景现象旋转效果的扩展区域本应透明但叠加到视频上后显示为黑色方块。原因rotate滤镜的cnone参数在部分 FFmpeg 版本或像素格式下不能正确输出透明背景扩展区域实际为不透明黑色。解决方案改为cblack0黑色但 alpha0即透明并在 rotate 前加formatrgba确保像素格式支持 alpha 通道扩展区域才能正确透明。坑7zoomIn 使用scale0导致首帧闪烁为原始尺寸现象zoomIn 动画开始的第一帧图片突然以原始尺寸出现然后才开始从小到大缩放产生明显的闪烁。原因FFmpeg 的scale滤镜在目标尺寸为 0或经浮点截整后为 0时会自动回退为原始尺寸导致动画起始帧图片以全尺寸闪现一帧。最初尝试用0.001作为最小值但浮点截整后仍可能为 0。解决方案将最小值改为max(2, ...)保证像素数始终 ≥ 2FFmpeg 不会触发回退逻辑首帧仅显示 2×2 的极小图片视觉上等同于从零开始。坑8marquee 循环模式的 enable 范围不受字幕时间段约束导致图片在应消失后仍然滚动现象跑马灯设置了字幕显示时间段如第 515 秒但looptrue时图片超过第 15 秒后依然继续滚动显示。原因最初 enable 表达式写的是gte(t, delay)即从动画起始时间开始一直到视频结束没有设置上边界导致不受字幕endTime约束。解决方案循环模式改为between(t, animStart, endTime)将 overlay 的显示范围限定在字幕时间段内非循环模式则取animStart duration * iterCount与endTime的最小值作为上边界。坑9loop 模式下 overlay 挂起视频无法正常结束现象使用loop-1无限循环单帧图片时最终输出视频的overlay滤镜一直等待循环流的结束信号导致处理进程挂起或视频被无限延长。原因loop-1生成的是一个无限长的流overlay 默认以最长输入流为准因此视频流有限结束后overlay 仍在等待循环图片流无限结束。解决方案overlay 加上shortest1参数使滤镜以最短输入流即视频流为结束基准循环流在视频结束时自动截断。
图片动画效果如何实现及踩过的坑
图片动画效果如何实现及踩过的坑本文档描述在视频中叠加图片字幕图时可对图片应用的各类动画效果包括动画类型、参数说明及实现原理。一、动画类型总览类型名称支持方向支持循环支持次数marquee跑马灯left / right✓✓slideIn滑入left / right / up / down——rotate旋转left逆时针/ right顺时针✓✓bounce弹跳—✓✓blink闪烁—✓✓fadeIn渐入———fadeInOut渐入渐出———zoomIn缩放入场———none无动画———二、公共参数说明参数类型说明typeString动画类型取值见上表durationDouble动画时长秒。建议不超过图片显示窗口时长否则动画会被截断delayDouble动画延迟秒。相对于图片出现时刻的偏移量实际动画开始 出现时刻 delaydirectionString方向仅 marquee / slideIn / rotate 有效其他类型忽略loopBoolean是否在图片显示期间持续循环仅 marquee / rotate / bounce / blink 有效iterCountIntegerloopfalse 时的播放次数默认 1仅 marquee / rotate / bounce / blink 有效三、各动画类型详解1. marquee — 跑马灯图片在水平方向持续匀速滚动一个duration周期内图片从画面一侧横穿至另一侧。方向left默认从右侧画外向左运动最终从左侧消失right从左侧画外向右运动最终从右侧消失循环控制looptrue在图片显示期间持续滚动loopfalse滚动iterCount次后停止实现原理利用mod函数对时间取模将当前时刻映射到[0, 画面宽度 图片宽度)范围内的偏移值使图片坐标周期性地从一侧移动到另一侧形成无缝循环滚动效果。2. slideIn — 滑入图片从画面外某个方向滑入到目标坐标在duration时间内完成入场之后保持静止。方向left默认从左侧画外滑入right从右侧画外滑入up从顶部画外滑入down从底部画外滑入实现原理根据方向计算起始坐标画面外的位置和目标坐标配置的 x/y在duration时间内通过线性插值在起始坐标和目标坐标之间平滑过渡。入场完成后坐标固定在目标位置。3. rotate — 旋转图片绕自身中心持续旋转duration为旋转一整圈所需的时长。方向right默认顺时针旋转left逆时针旋转循环控制looptrue持续旋转不停止loopfalse旋转iterCount圈后停止实现原理角速度 ±2π / duration正负对应顺/逆时针旋转角度随时间线性增长。为避免旋转时图片四角被画布裁剪产生残影画布会先扩展为图片对角线长度向上取偶数overlay 坐标同步补偿确保旋转中心始终对准目标位置。背景填充透明色。4. bounce — 弹跳图片在垂直方向按正弦函数规律上下振动振幅固定为20pxduration为一个完整振动周期。循环控制looptrue持续弹跳loopfalse弹跳iterCount次后停止在原始位置实现原理Y 坐标 目标 Y -|sin((t - 动画起始) * π / duration)| * 振幅利用绝对值正弦函数模拟物体弹跳的轨迹仅向上偏移无负向偏移。5. blink — 闪烁图片以duration为周期交替出现和消失形成闪烁效果。循环控制looptrue在图片显示期间持续闪烁loopfalse闪烁iterCount次后保持常驻显示实现原理通过 overlay 的enable表达式控制图片的显示与隐藏以duration为单位对时间做整除取模奇数帧隐藏、偶数帧显示或反之实现周期性开关切换。6. fadeIn — 渐入图片从完全透明逐渐变为完全不透明duration为渐入时长完成后保持完全显示。实现原理通过geq滤镜逐帧修改图片的 Alpha 通道值动画开始前alpha 0透明动画进行中alpha (当前时刻 - 动画起始) / duration线性增长动画结束后alpha 1完全不透明每一帧原始像素的 alpha 值乘以上述系数实现平滑的透明度过渡。7. fadeInOut — 渐入渐出前半段duration / 2从透明渐变到完全不透明后半段duration / 2从完全不透明渐变到透明duration结束后图片隐藏。实现原理与 fadeIn 相同均通过geq滤镜逐帧计算 Alpha 系数但系数分为两个阶段前半段(t - 起始) / (duration/2)从 0 到 1后半段1 - (t - 半程时刻) / (duration/2)从 1 到 0适合需要图片短暂出现后自然消失的场景。8. zoomIn — 缩放入场图片从尺寸为 0 逐渐缩放到原始尺寸以图片的配置坐标为中心向外展开duration为缩放时长完成后保持原始尺寸。实现原理通过scale滤镜逐帧动态修改图片的宽高缩放比例 (当前时刻 - 动画起始) / duration从 0 到 1为防止 scale0 时 FFmpeg 回退为原始尺寸导致首帧闪烁最小尺寸限制为 2pxoverlay 坐标动态补偿(原始尺寸 - 当前缩放尺寸) / 2确保缩放过程中图片始终以目标坐标为中心9. none — 无动画图片在指定时间段内静态显示不附加任何动画效果。所有动画参数均被忽略。四、时序关系说明视频时间轴 ──────────────────────────────────────────────────────────► │←── delay ──►│←────── duration ────────►│ startTime animStart animEnd endTime 图片出现 动画开始 动画结束 图片消失图片在startTime时刻出现endTime时刻消失动画实际开始时刻 startTime delayduration建议小于endTime - startTime否则动画在图片消失前尚未完成会被截断looptrue的动画marquee/rotate/bounce/blink会持续到图片消失时刻五、特殊情况处理情况处理方式type为空或未知值降级为无动画静态叠加duration未设置默认使用 1 秒delay未设置默认为 0即图片出现时立即开始动画iterCount未设置默认为 1 次rotate / zoomIn 无法读取图片尺寸rotate 使用坐标最大值估算对角线zoomIn 退化为固定坐标叠加GIF 动图输入需标记animatedtrueFFmpeg 会启用-ignore_loop 0和-stream_loop -1驱动循环播放六、踩坑记录坑1PNG 单帧图片时间戳恒为 0导致所有依赖时间的动画失效现象fadeIn / fadeInOut / zoomIn / rotate 等动画完全没有效果图片从第一帧就以最终状态静止显示。原因PNG 是单帧静态图片FFmpeg 读入后其视频流的时间戳t始终为 0无论视频播放到哪一秒动画表达式中的t都不会递增导致所有基于时间的动画表达式失效。解决方案在动画滤镜链前插入looploop-1:size1,setptsN/25/TB让 FFmpeg 以 25fps 的帧率无限循环单帧图片并通过setpts为每一帧生成单调递增的时间戳使后续滤镜中的时间变量t/T正常工作。坑2colorchannelmixer不支持时间变量fadeIn / fadeInOut / blink 无效果现象使用colorchannelmixeraaif(lt(t,...),...)尝试实现逐帧 alpha 渐变结果图片 alpha 始终为固定值渐变不生效。原因colorchannelmixer滤镜不支持在参数表达式中使用时间变量t它只会在滤镜初始化时求值一次不会逐帧重新计算。解决方案fadeIn / fadeInOut改用geq滤镜其a参数支持逐像素、逐帧动态计算可使用时间变量T注意 geq 中时间变量为大写 T。blink改用 overlay 的enable表达式控制图片整体的显示与隐藏彻底绕开 alpha 通道操作。坑3geq滤镜时间变量是大写T写成小写t不生效现象将colorchannelmixer替换为geq后渐变仍然不生效alpha 值始终为 0 或 1。原因geq滤镜中的时间变量是大写T而大多数其他滤镜如 overlay 的 enable 表达式使用小写t。写成小写t时geq将其视为未定义变量求值结果为 0导致 alpha 计算错误。解决方案在geq的a表达式中所有时间引用改为大写T。坑4动画时间基准用了相对delay实际动画在视频绝对时间轴上偏移错误现象配置了delay2、字幕startTime10的动画动画在第 2 秒就开始播放而不是第 12 秒。原因动画表达式中的时间条件直接使用了delay作为基准但 FFmpeg 的时间轴是视频的绝对时间delay只是相对于字幕出现时刻的偏移量必须加上startTime才能换算为绝对时间。解决方案统一计算animStart startTime delay所有动画表达式包括 fadeIn、fadeInOut、zoomIn、slideIn、rotate、bounce、marquee均以animStart作为时间基准。坑5rotate 旋转时图片四角被裁剪出现残影现象图片旋转过程中四个角周期性地消失又出现产生明显的闪烁残影尤其是宽高比悬殊的图片更为严重。原因FFmpeg 的rotate滤镜默认输出画布尺寸等于原图的宽高owiw:ohih当图片旋转到对角线方向时四个角会超出这个画布范围并被裁剪掉下一帧旋转回来时又重新出现形成残影。解决方案将输出画布扩展为图片对角线长度√(w²h²)向上取整到偶数确保任意旋转角度下图片内容都不会被裁剪。同时对 overlay 坐标进行补偿使旋转中心始终保持在目标位置。坑6rotate 扩展画布后背景色使用cnone实际渲染出黑色背景现象旋转效果的扩展区域本应透明但叠加到视频上后显示为黑色方块。原因rotate滤镜的cnone参数在部分 FFmpeg 版本或像素格式下不能正确输出透明背景扩展区域实际为不透明黑色。解决方案改为cblack0黑色但 alpha0即透明并在 rotate 前加formatrgba确保像素格式支持 alpha 通道扩展区域才能正确透明。坑7zoomIn 使用scale0导致首帧闪烁为原始尺寸现象zoomIn 动画开始的第一帧图片突然以原始尺寸出现然后才开始从小到大缩放产生明显的闪烁。原因FFmpeg 的scale滤镜在目标尺寸为 0或经浮点截整后为 0时会自动回退为原始尺寸导致动画起始帧图片以全尺寸闪现一帧。最初尝试用0.001作为最小值但浮点截整后仍可能为 0。解决方案将最小值改为max(2, ...)保证像素数始终 ≥ 2FFmpeg 不会触发回退逻辑首帧仅显示 2×2 的极小图片视觉上等同于从零开始。坑8marquee 循环模式的 enable 范围不受字幕时间段约束导致图片在应消失后仍然滚动现象跑马灯设置了字幕显示时间段如第 515 秒但looptrue时图片超过第 15 秒后依然继续滚动显示。原因最初 enable 表达式写的是gte(t, delay)即从动画起始时间开始一直到视频结束没有设置上边界导致不受字幕endTime约束。解决方案循环模式改为between(t, animStart, endTime)将 overlay 的显示范围限定在字幕时间段内非循环模式则取animStart duration * iterCount与endTime的最小值作为上边界。坑9loop 模式下 overlay 挂起视频无法正常结束现象使用loop-1无限循环单帧图片时最终输出视频的overlay滤镜一直等待循环流的结束信号导致处理进程挂起或视频被无限延长。原因loop-1生成的是一个无限长的流overlay 默认以最长输入流为准因此视频流有限结束后overlay 仍在等待循环图片流无限结束。解决方案overlay 加上shortest1参数使滤镜以最短输入流即视频流为结束基准循环流在视频结束时自动截断。