更新动画管理器和绘制帧

每次应用程序计划情节提要时,应用程序都必须向动画管理器提供当前时间。 当指示动画管理器更新其状态并将所有动画变量设置为适当的内插值时,还需要当前时间。

概述

Windows动画支持两种配置:应用程序驱动的动画和计时器驱动的动画。

若要在应用程序中使用应用程序驱动的动画,必须在绘制每个帧之前更新动画管理器,并使用适当的机制来频繁地绘制帧以便动画。 使用应用程序驱动动画的应用程序可以使用任何机制来确定当前时间,但Windows动画计时器对象在动画管理器接受的单位中返回精确时间。 为了避免在未播放动画时不必要的绘制,还应提供管理器事件处理程序,以在计划动画时开始重绘,并在每个帧后检查是否可暂停重绘。 有关详细信息,请参阅 应用程序驱动动画

在应用程序驱动配置中,应用程序可以调用 IUIAnimationManager::GetStatus 方法,以验证动画当前是否已计划,并在需要时继续绘制帧。 由于重绘在计划没有动画时停止,因此下次计划动画时需要重新启动它。 应用程序可以注册管理器事件处理程序,当动画管理器的状态从空闲 (当前没有将动画计划) 为忙碌时通知。

若要在应用程序中使用计时器驱动的动画,必须将动画管理器连接到动画计时器并提供计时器事件处理程序。 当动画管理器连接到计时器时,计时器可以告诉管理器何时应随着时间的进行而更新动画状态。 应用程序应为每个计时器刻度绘制一个帧。 动画管理器可以反过来告诉计时器何时播放动画,因此计时器可以在不必要的重新绘制期间关闭自身。 为了避免在未播放动画时不必要的绘图,应将计时器配置为自动禁用自身。 有关详细信息,请参阅 计时器驱动的动画

示例代码

Application-Driven动画

以下示例代码摘自 Windows 动画示例 Application-Driven AnimationGrid Layout 中的 ManagerEventHandler.h。 它定义管理器事件处理程序。

#include "UIAnimationHelper.h"

// Event handler object for manager status changes

class CManagerEventHandler :
    public CUIAnimationManagerEventHandlerBase<CManagerEventHandler>
{
public:

    static HRESULT
    CreateInstance
    (
        CMainWindow *pMainWindow,
        IUIAnimationManagerEventHandler **ppManagerEventHandler
    ) throw()
    {
        CManagerEventHandler *pManagerEventHandler;
        HRESULT hr = CUIAnimationCallbackBase::CreateInstance(
            ppManagerEventHandler,
            &pManagerEventHandler
            );
        if (SUCCEEDED(hr))
        {
            pManagerEventHandler->m_pMainWindow = pMainWindow;
        }
        
        return hr;
    }

    // IUIAnimationManagerEventHandler

    IFACEMETHODIMP
    OnManagerStatusChanged
    (
        UI_ANIMATION_MANAGER_STATUS newStatus,
        UI_ANIMATION_MANAGER_STATUS previousStatus
    )
    {
        HRESULT hr = S_OK;

        if (newStatus == UI_ANIMATION_MANAGER_BUSY)
        {
            hr = m_pMainWindow->Invalidate();
        }

        return hr;
    }

    ...

};

以下示例代码取自Windows动画示例 Application-Driven Animation 中的 MainWindow.cpp;请参阅 CMainWindow::InitializeAnimation。 此示例使用 CreateInstance 方法创建管理器事件处理程序的实例,并使用 IUIAnimationManager::SetManagerEventHandler 方法将其传递给动画管理器。

// Create and set the ManagerEventHandler to start updating when animations are scheduled

IUIAnimationManagerEventHandler *pManagerEventHandler;
HRESULT hr = CManagerEventHandler::CreateInstance(
    this,
    &pManagerEventHandler
    );
if (SUCCEEDED(hr))
{
    hr = m_pAnimationManager->SetManagerEventHandler(
        pManagerEventHandler
        );
    pManagerEventHandler->Release();
}

由于管理器事件处理程序保留对主窗口对象的引用,因此应通过将 NULL 传递给 SetManagerEventHandler) 或动画管理器在主窗口被销毁之前将其清除 (。

以下示例代码取自 Windows Animation 示例 Application-Driven Animation 中的 MainWindow.cpp;请参阅 CMainWindow::OnPaint 方法。 它调用 IUIAnimationManager::GetTime 方法以 IUIAnimationManager::Update 方法所需的单位检索时间。

// Update the animation manager with the current time

UI_ANIMATION_SECONDS secondsNow;
HRESULT hr = m_pAnimationTimer->GetTime(
    &secondsNow
    );

if (SUCCEEDED(hr))
{
    hr = m_pAnimationManager->Update(
        secondsNow
        );

    ...

}

以下示例代码取自Windows动画示例 Application-Driven AnimationGrid Layout 中的 MainWindow.cpp;请参阅 CMainWindow::OnPaint 方法。 它假定应用程序使用图形 API 自动同步到监视器刷新率 ((例如 Direct2D),其默认设置) ,在这种情况下,对 InvalidateRect 函数的调用足以确保绘制代码在绘制下一帧时再次调用。 最好检查是否仍有使用 GetStatus 计划的任何动画,而不是无条件调用 InvalidateRect

// Read the values of the animation variables and draw the client area

hr = DrawClientArea();
if (SUCCEEDED(hr))
{          
    // Continue redrawing the client area as long as there are animations scheduled
    UI_ANIMATION_MANAGER_STATUS status;
    hr = m_pAnimationManager->GetStatus(
        &status
        );
    if (SUCCEEDED(hr))
    {
        if (status == UI_ANIMATION_MANAGER_BUSY)
        {
            InvalidateRect(
                m_hwnd,
                NULL,
                FALSE
                );
        }
    }
}

Timer-Driven动画

以下示例代码取自 Windows 动画示例 Timer-Driven Animation 中的 TimerEventHandler.h。 示例代码定义计时器事件处理程序,使窗口的工作区在每次更新动画状态后都会导致重新绘制。

#include "UIAnimationHelper.h"

// Event handler object for timer events

class CTimerEventHandler :
    public CUIAnimationTimerEventHandlerBase<CTimerEventHandler>
{
public:

    static HRESULT
    CreateInstance
    (
        CMainWindow *pMainWindow,
        IUIAnimationTimerEventHandler **ppTimerEventHandler
    ) throw()
    {
        CTimerEventHandler *pTimerEventHandler;
        HRESULT hr = CUIAnimationCallbackBase::CreateInstance(
            ppTimerEventHandler,
            &pTimerEventHandler
            );

        if (SUCCEEDED(hr))
        {
            pTimerEventHandler->m_pMainWindow = pMainWindow;
        }
        
        return hr;
    }

    // IUIAnimationTimerEventHandler

    IFACEMETHODIMP
    OnPreUpdate()
    {
        return S_OK;
    }

    IFACEMETHODIMP
    OnPostUpdate()
    {
        HRESULT hr = m_pMainWindow->Invalidate();

        return hr;
    }

    IFACEMETHODIMP
    OnRenderingTooSlow
    (
        UINT32 /* fps */
    )
    {
        return S_OK;
    }

    ...

};

以下示例代码取自 Windows 动画示例 Timer-Driven Animation 中的 MainWindow.cpp;请参阅 CMainWindow::InitializeAnimation。 此示例使用 CreateInstance 方法创建计时器事件处理程序的实例,并使用 IUIAnimationTimer::SetTimerEventHandler 方法将其传递给计时器。 由于计时器事件处理程序保留对主窗口对象的引用,因此应通过将 NULL 传递给 SetTimerEventHandler) 或主窗口销毁之前完全释放的计时器来清除计时器处理程序 (。

// Create and set the timer event handler

IUIAnimationTimerEventHandler *pTimerEventHandler;
hr = CTimerEventHandler::CreateInstance(
    this,
    &pTimerEventHandler
    );
if (SUCCEEDED(hr))
{
    hr = m_pAnimationTimer->SetTimerEventHandler(
        pTimerEventHandler
        );
    pTimerEventHandler->Release();
}

以下示例代码取自 Windows 动画示例 Timer-Driven Animation 中的 MainWindow.cpp;请参阅 CMainWindow::InitializeAnimation 方法。 它调用动画管理器对象上的 QueryInterface 方法以获取指向 IUIAnimationTimerUpdateHandler 的指针,然后通过将动画管理器设置为计时器的更新处理程序,使用 IUIAnimationTimer::SetTimerUpdateHandler 方法连接 UIAnimationManagerUIAnimationTimer 对象。 请注意,不需要显式清除此连接;应用程序在释放动画管理器和动画计时器后安全清除连接。

// Connect the animation manager to the timer

IUIAnimationTimerUpdateHandler *pTimerUpdateHandler;
hr = m_pAnimationManager->QueryInterface(
    IID_PPV_ARGS(&pTimerUpdateHandler)
    );

if (SUCCEEDED(hr))
{
    hr = m_pAnimationTimer->SetTimerUpdateHandler(
        pTimerUpdateHandler
        UI_ANIMATION_IDLE_BEHAVIOR_DISABLE  // timer will shut itself off when there are no animations playing
        );
    pTimerUpdateHandler->Release();
    if (SUCCEEDED(hr))
    {
        // Create and set the timer event handler

        ...

    }
}

如果未使用 UI_ANIMATION_IDLE_BEHAVIOR_DISABLE ,则还需使计时器开始计时。

上一步

在开始此步骤之前,应已完成此步骤: 创建动画变量

下一步

完成此步骤后,下一步是: 读取动画变量值

IUIAnimationManager::GetStatus

IUIAnimationManager::SetManagerEventHandler

IUIAnimationManager::Update

IUIAnimationTimer::GetTime

IUIAnimationTimer::SetTimerUpdateHandler

UIAnimationManager

UIAnimationTimer

Windows动画概述