Обновление диспетчера анимации и рисования кадров
Каждый раз, когда приложение планирует анимацию, оно должно предоставить текущее время диспетчеру анимации. Текущее время также требуется при перенаправлении диспетчера анимации для обновления состояния и задания всех переменных анимации соответствующим интерполированным значениям.
Обзор
Существует две конфигурации, поддерживаемые анимацией Windows: анимация на основе приложений и анимация на основе таймера.
Чтобы использовать анимацию, управляемую приложением, необходимо обновлять менеджер анимации перед отрисовкой каждого кадра и использовать соответствующий механизм, чтобы кадры отрисовывались с частотой, достаточной для анимации. Приложение, использующее анимацию на основе приложений, может использовать любой механизм для определения текущего времени, но объект таймера анимации Windows возвращает точное время в единицах, принятых диспетчером анимации. Чтобы избежать ненужной перерисовки, когда анимации не воспроизводятся, необходимо также предоставить обработчик событий диспетчера для начала перерисовки при планировании анимаций и проверки после каждого кадра, можно ли приостановить перерисовку. Дополнительные сведения см. в Application-Driven анимации.
В конфигурации на основе приложений приложение может вызывать метод IUIAnimationManager::GetStatus, чтобы убедиться, что анимации в настоящее время запланированы, и продолжать рисовать кадры, если это так. Поскольку перерисовка останавливается при отсутствии запланированных анимаций, необходимо перезапустить ее при следующем планировании анимации. Приложение может зарегистрировать обработчик событий диспетчера, чтобы получать уведомления, когда состояние диспетчера анимации изменяется с простоя (в настоящее время анимации не запланировано) на занятое.
Чтобы использовать анимацию на основе таймера в приложении, необходимо подключить диспетчер анимации к таймеру анимации и предоставить обработчик событий таймера. Когда менеджер анимации подключен к таймеру, таймер может сообщить менеджеру, когда состояние анимации должно обновляться по мере течения времени. Приложение должно рисовать рамку на каждый тик таймера. Диспетчер анимаций может, в свою очередь, сообщить таймеру, когда воспроизводятся анимации, поэтому таймер может выключиться во время простоя, когда перерисовка не требуется. Чтобы избежать ненужных рисунков при отсутствии анимации, следует настроить таймер для автоматического отключения. Дополнительные сведения см. в Timer-Driven Animation.
Пример кода
анимация Application-Driven
Следующий пример кода взят из ManagerEventHandler.h из примеров анимации Windows Application-Driven анимации и макета сетки сетки. Он определяет обработчик событий менеджера.
#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;
}
...
};
Следующий пример кода взят из файла MainWindow.cpp из примера Windows Animation Application-Driven Animation; см. функцию 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) или диспетчер анимации должен быть полностью освобожден прежде чем главное окно будет уничтожено.
Следующий пример кода взят из MainWindow.cpp в примере программы Windows Animation Application-Driven анимациии см. метод 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
);
...
}
Следующий пример кода взят из MainWindow.cpp из примеров анимации Windows Application-Driven анимации и макета сетки ; см. метод CMainWindow::OnPaint. Предполагается, что приложение использует графический API, который автоматически синхронизируется с частотой обновления монитора (например, Direct2D с параметрами по умолчанию), в этом случае вызов функции InvalidateRect достаточно, чтобы убедиться, что код рисования будет вызываться снова, когда время нарисовать следующий кадр. Вместо вызова InvalidateRect без каких-либо условий лучше проверить наличие анимаций, запланированных с помощью GetStatus.
// 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
Следующий пример кода взят из TimerEventHandler.h из примера анимации Windows Timer-Driven Animation. В примере кода определяется обработчик событий таймера, который инвалидирует клиентскую область окна, чтобы вызвать перерисовку после каждого обновления состояния анимации.
#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;
}
...
};
Следующий пример кода взят из MainWindow.cpp из примера анимации Windows Timer-Driven анимации; см. раздел 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();
}
Следующий пример кода взят из MainWindow.cpp в примере Windows Animation Timer-Driven Animation, см. метод CMainWindow::InitializeAnimation. Он вызывает метод QueryInterface в объекте диспетчера анимации, чтобы получить указатель на IUIAnimationTimerUpdateHandler, а затем подключает объекты UIAnimationManager и UIAnimationTimer, назначая диспетчера анимации обработчиком обновления таймера с помощью метода IUIAnimationTimer::SetTimerUpdateHandler. Обратите внимание, что вам не нужно явно очищать это подключение; оно будет безопасно очищено после того, как приложение освободит как диспетчер анимации, так и таймер анимации.
// 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 не используется, необходимо также включить таймер для запуска его отсчета времени.
Предыдущий шаг
Перед началом этого шага необходимо выполнить этот шаг: создать переменные анимации.
Следующий шаг
После выполнения этого шага следующий шаг: Чтение значений переменной анимации.
Связанные разделы