更新动画管理器和绘制帧

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

概述

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

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

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

若要在应用程序中使用计时器驱动的动画,必须将动画管理器连接到动画计时器并提供计时器事件处理程序。 当动画管理器连接到计时器时,计时器可以告知管理器何时应随着时间的进行而更新动画状态。 应用程序应为每个计时器计时周期绘制一个帧。 动画管理器可以在播放动画时告知计时器,这样计时器就可以在不需要重绘的空闲时段关闭自身。 为了避免在播放任何动画时进行不必要的绘制,应将计时器配置为自动禁用自身。 有关详细信息,请参阅 计时器驱动动画

示例代码

Application-Driven动画

以下示例代码取自 Windows 动画示例 应用程序驱动的动画网格布局中的 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 动画示例 应用程序驱动的动画中的 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();
}

由于管理器事件处理程序保留对main窗口对象的引用,因此应通过将 NULL 传递给 SetManagerEventHandler) 清除管理器事件处理程序 (,否则动画管理器应在销毁main窗口之前完全释放。

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

// 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 动画示例 计时器驱动动画中的 MainWindow.cpp;请参阅 CMainWindow::InitializeAnimation 方法。 它调用动画管理器对象上的 QueryInterface 方法以获取指向 IUIAnimationTimerUpdateHandler 的指针,然后通过使用 IUIAnimationTimer::SetTimerUpdateHandler 方法将动画管理器设置为计时器的更新处理程序来连接 UIAnimationManager 和 UIAnimationTimer 对象。 请注意,无需显式清除此连接;应用程序同时释放动画管理器和动画计时器后,将安全清除连接。

// 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 动画概述