【发布时间】:2020-10-08 19:25:37
【问题描述】:
我在 WM_PAINT 期间使用BeginBufferedAnimation() 为自定义控件设置动画(交叉淡入淡出)。
我知道动画的第一帧何时绘制;当我使用 fUpdateTarget = TRUE 调用 EndBufferedAnimation() 时。
我可以通过检查来自BufferedPaintRenderAnimation() 的返回值来确定动画是否正在进行中。
问题是:我怎么知道动画的最后一帧是什么时候绘制的?
我找不到可以执行此操作的 API 调用。
想到的一件事是使用GetTickCount() 来确定自调用EndBufferedAnimation() 以来经过的时间,并对照BP_ANIMATIONPARAMS 结构的dwDuration 成员检查该时间。
但是,我不确定这种方法是否非常准确。
是否有另一种(万无一失)方法来确定 WM_PAINT 是否正在绘制动画的最后一帧?
编辑
显然,当动画通过调用BufferedPaintStopAllAnimations() 或使正在动画的客户区部分无效时过早停止时,检查已用时间不起作用。
编辑 2
这是我的(简化的)WM_PAINT 代码:
PAINTSTRUCT ps;
HDC DC = BeginPaint(Handle, &ps);
if (DC != NULL)
{
// If animation in progress, paint the next frame.
if (!BufferedPaintRenderAnimation(Handle, DC))
{
BP_ANIMATIONPARAMS ap = {sizeof(ap)};
ap.style = BPAS_LINEAR;
ap.dwDuration = 1000;
RECT R;
GetClientRect(Handle, &R);
HDC hdcFrom, hdcTo;
HANIMATIONBUFFER Animation = BeginBufferedAnimation(Handle, DC, &R, BPBF_COMPATIBLEBITMAP, NULL, &ap, &hdcFrom, &hdcTo);
if (Animation)
{
if (hdcFrom) Paint(hdcFrom, &R, OldState);
if (hdcTo ) Paint(hdcTo, &R, NewState);
// Paint the first frame.
EndBufferedAnimation(Animation, true);
}
else
Paint(DC, &R, NewState);
}
else
{
// Here, I want to check if BufferedPaintRenderAnimation() painted the last frame.
}
EndPaint(Handle, &ps);
}
编辑 3
我的自定义控件有一个子控件,需要与其父控件同步动画。为此,我通过在我的Paint() 方法中向子控件发送WM_PRINT 消息,让子控件将自己绘制到第一个和最后一个动画帧中。在绘制父控件后,子控件会收到其WM_PAINT 消息。为了防止父控件在动画时绘制子控件,我想在启动动画时隐藏子控件并在动画完成后再次显示它。
由于找不到万无一失的方法来检测动画何时结束,我决定不隐藏子控件。相反,我避免在调用EndBufferedAnimation() 之后以及当BufferedPaintRenderAnimation() 返回true 时使用RedrawWindow(ChildHandle, NULL, NULL, RDW_VALIDATE | RDW_NOFRAME) 重新绘制子控件。这边走,
BufferedPaintRenderAnimation() 在动画结束时将子控件绘制为最终状态,因此子控件不必这样做。
【问题讨论】:
-
使用animation manager,您可以通过
SetManagerEventHandler监控其statue。当状态从 BUSY 变为 IDLE 时,绘制最后一帧。 -
@RitaHan-MSFT 感谢您的回复。我认为
IUIAnimationManager::GetStatus()做同样的事情。不幸的是,这只告诉我所有动画是否都已完成。想象一下鼠标光标从一个控件移动到另一个控件。第一个控件仍然可以从“热”动画到“正常”,而第二个控件正在从“正常”动画到“热”。仅当两个动画都完成后,状态才会变为“空闲”。如果鼠标光标一直在控件上移动,这可能需要很长时间。 -
对于多个动画,您可以使用Storyboard,它可以管理多个过渡。过渡定义了单个动画变量在特定时间间隔内如何变化。
-
@RitaHan-MSFT 看起来
IUIAnimationStoryboard::GetStatus()可以产生我正在寻找的动画状态。下一个问题是:如何获取以BeginBufferedAnimation()开头的动画关联的IUIAnimationStoryboard接口?我只有一个HANIMATIONBUFFER句柄。 -
您可以保留一个标志,以记住
BufferedPaintRenderAnimation在上一个绘制周期中返回的内容。当它从true变为false时,缓冲动画就完成了。