本文章是由機器翻譯。

Windows 與 C++

DirectComposition:轉換與動畫

Kenny Kerr

Kenny KerrDirectComposition 的視覺效果提供了更多隻是偏移量和我已經說明了我最後的幾個列的內容屬性。當你開始影響到他們與變換和動畫,視覺效果真的會蘇醒過來。在這兩種情況下,Windows 排版引擎是一種處理器,該輪到你來計算或興建的變換矩陣,以及三次函數和正弦波的動畫曲線。值得慶倖的是,Windows API 提供了必要的支援,有一雙非常好的補充的 Api。Direct2D 提供大力支援用於定義變換矩陣,使輕的描述旋轉、 規模、 角度來看、 翻譯和更多的工作。同樣,Windows 動畫管理員使您免于要在數學中,嚮導使您得以描述動畫使用豐富的動畫過渡庫、 演示圖板和關鍵幀和更多的負載。當您將 Windows 動畫管理員、 Direct2D 和 DirectComposition 合併到單個應用程式你可以真正體驗說一不二的權力,在您的指尖。

在我以前的專欄 (msdn.microsoft.com/magazine/dn759437) 我展示如何 DirectComposition API 可用於同時 Direct2D 得到最好的保留模式和即時模式的圖形。該列所附帶的樣例專案說明了這一概念有一個簡單的應用程式,允許您創建圈子,向四周移動和控制它們的 Z 順序很簡單。在本專欄中,我想告訴你它是多麼容易添加一些功能強大的效果與變換和動畫。首先要知道是 DirectComposition 為很多它們的標量性質提供重載。你可能已經注意到這如果你一直遵循過去幾個月,我已經瞭解的 API。例如,視覺化物件的偏移量可以如下設置的 SetOffsetX 和 SetOffsetY 的方法中:

ComPtr<IDCompositionVisual2> visual = ...
HR(visual->SetOffsetX(10.0f));
HR(visual->SetOffsetY(20.0f));

但是來自于 IDCompositionVisual2 的 IDCompositionVisual 介面還提供的這些重載方法接受的動畫物件,而不是一個浮點數。此動畫物件具體化為 IDCompositionAnimation 介面。例如,我可以設置視覺化物件的偏移量與一個或兩個動畫物件,取決於是否需要進行動畫處理的一個或兩個軸,就像這樣:

ComPtr<IDCompositionAnimation> animateX = ...
ComPtr<IDCompositionAnimation> animateY = ...
HR(visual->SetOffsetX(animateX.Get()));
HR(visual->SetOffsetY(animateY.Get()));

但組合引擎也能遠遠超過只是視覺化物件的偏移量進行動畫處理。視覺效果也支援 2D 和 3D 轉換。Visual SetTransform 方法可用於應用二維變換,給出一個標量值:

D2D1_MATRIX_3X2_F matrix = ...
HR(visual->SetTransform(matrix));

在這裡,DirectComposition 實際上依靠 3 x 2 矩陣由 Direct2D API 定義的。你可以像旋轉、 轉換、 縮放和扭曲視覺的東西。此座標空間的視覺內容預計,但它只局限在兩個三維圖形的 X 軸和 Y 軸的影響。

當然,IDCompositionVisual 介面提供超載的 SetTransform 方法,但它並不直接接受動畫物件。你看,動畫物件是只負責對一個單一的值隨著時間的推移進行動畫處理。一個矩陣,根據定義,由組成的值的數目。你可能想要進行動畫處理任何數目的其成員,具體取決於你想要達到的效果。所以,相反,SetTransform 重載接受一個 transform 物件:

ComPtr<IDCompositionTransform> transform = ...
HR(visual->SetTransform(transform.Get()));

它是變換物件,具體的各種介面來自 IDCompositionTransform,提供接受標量值或動畫物件的重載的方法。這種方式,您可能定義動畫的旋轉角度但一個固定的中心點和軸的旋轉矩陣。您的動畫是,當然,取決於你。下面是一個簡單的例子:

ComPtr<IDCompositionRotateTransform> transform = ...
HR(transform->SetCenterX(width / 2.0f));
HR(transform->SetCenterY(height / 2.0f));
HR(transform->SetAngle(animation.Get()));
HR(visual->SetTransform(transform.Get()));

IDCompositionRotateTransform 介面從 IDCompositionTransform 派生,並表示影響視覺繞 Z 軸的旋轉 2D 轉換。我在這裡,我設置為一個固定的值,基於一定的寬度和高度,並使用動畫物件來控制角度的中心點。

這就是基本的模式。我只描述二維變換,但 3D 轉換的工作在很多方式相同。現在讓我說明更為實際的是由向您展示如何才能我以前的專欄所附帶的樣例專案和變換和對進行動畫處理以各種方式説明 Direct2D 和 Windows 動畫管理員。

說明轉換和列印的雜誌中的動畫並開始推動什麼可以輕易被盯著一個靜態頁面的極限。如果你想要看到它在行動中,查閱我的線上課程,你在哪裡可以看作為所有涉及到的生命 (bit.ly/WhKQZT)。若要使概念有點更多列印,我會調整示例專案從我以前的專欄,使用方塊,而不是圈子裡的自由。這應該使各種轉換更加明顯一點在紙上。首先,我會用矩形幾何形狀替換 SampleWindow m_geometry 成員變數:

ComPtr<ID2D1RectangleGeometry> m_geometry;

然後在 SampleWindow CreateFactoryAndGeometry 方法中我會 Direct2D 工廠來創建矩形幾何形狀而不是橢圓幾何:

D2D1_RECT_F const rectangle =
  RectF(0.0f, 0.0f, 100.0f, 100.0f);
HR(m_factory->CreateRectangleGeometry(
  rectangle,
  m_geometry.GetAddressOf()));

這就是全部。該應用程式的其餘部分只是將用於呈現幾何抽象和點擊測試像以前一樣。您可以看到在結果圖 1

說明了變換和動畫而不是圓的方格子
圖 1 說明了變換和動畫而不是圓的方格子

接下來,我要去添加一個簡單的訊息處理常式,對 WM_KEYDOWN 消息做出回應。SampleWindow MessageHandler 方法中,我將添加一個 if 語句為此:

else if (WM_KEYDOWN == message)
{
  KeyDownHandler(wparam);
}

像往常一樣,該處理常式將需要必要的錯誤處理,以從設備損失中恢復過來。圖 2 提供了釋放的設備資源和不正確視窗,所以 WM_PAINT 訊息處理常式可以重建裝置堆疊的常規模式。我也限制對 Enter 鍵要避免混亂與控制的關鍵,添加形狀時所使用的處理常式。

設備丟失恢復圖 2 腳手架

void KeyDownHandler(WPARAM const wparam)
{
  try
  {
    if (wparam != VK_RETURN)
    {
      return;
    }
    // Do stuff!
  }
  catch (ComException const & e)
  {
    TRACE(L"KeyDownHandler failed 0x%X\n",
      e.result);
    ReleaseDeviceResources();
    VERIFY(InvalidateRect(m_window,
                          nullptr,
                          false));
  }
}

在這一點上,我準備好開始嘗試變換與動畫。讓我們開始只是一個基本的二維旋轉變換。首先,我需要確定將代表 Z 軸的中心點或圍繞的旋轉點。因為 DirectComposition 預計物理圖元座標,只可用 GetClientRect 函數在這裡旅遊:

RECT bounds {};
VERIFY(GetClientRect(m_window, &bounds));

我可以然後派生的視窗客戶區的中心點,如下所示:

D2D1_POINT_2F center
{
  bounds.right / 2.0f,
  bounds.bottom / 2.0f
};

我也可以靠 Direct2D 矩陣 helper 函數來構造描述 30 度的二維旋轉變換的矩陣:

D2D1_MATRIX_3X2_F const matrix =
  Matrix3x2F::Rotation(30.0f, center);

然後,可以簡單地設置一個視覺變換屬性將更改提交到視覺化樹。我只會將此更改應用到的根視覺效果為簡單起見:

HR(m_rootVisual->SetTransform(matrix));
HR(m_device->Commit());

當然,可以將任意數量的變化應用到任意數量的視覺效果和組合引擎將照顧協調它都不費吹灰之力。你可以看到在這簡單的二維變換的結果圖 3。你可能會注意到有些走樣。即使 Direct2D 預設為抗鋸齒,它假定,繪圖將出現在呈現它的座標空間。組合引擎沒有任何知識的幾何形狀組成表面用,呈現,所以它也沒有辦法糾正這個。在任何情況下,一旦動畫添加到組合中,頻譜混疊將短暫而且很難辨認。

簡單二維變換
圖 3 簡單二維變換

要將動畫添加到這種轉變,我需要切換出成分變換的矩陣結構。我會用一個單一的旋轉變換替換的 D2D1_POINT_2F 和 D2D1_MATRIX_3X2_F 的結構。首先,我需要創建使用組成設備的旋轉變換:

ComPtr<IDCompositionRotateTransform> transform;
HR(m_device->CreateRotateTransform(transform.GetAddressOf()));

請記住,即使是這種看似簡單的物件必須被丟棄,如果設備已丟失並重新創建。然後,我可以設置的中心點和使用介面方法,而不是一個 Direct2D 矩陣結構的角度:

HR(transform->SetCenterX(bounds.right / 2.0f));
HR(transform->SetCenterY(bounds.bottom / 2.0f));
HR(transform->SetAngle(30.0f));

編譯器將選取適當的重載來處理組成變換:

HR(m_rootVisual->SetTransform(transform.Get()));

運行這將產生同樣的效果,如圖 3,因為我沒有添加的任何動畫。創建動畫物件是很簡單的:

ComPtr<IDCompositionAnimation> animation;
HR(m_device->CreateAnimation(animation.GetAddressOf()));

然後我可以使用此動畫物件而不是恒定值設置角度時:

HR(transform->SetAngle(animation.Get()));

當您嘗試配置動畫時,它會更有趣。簡單的動畫是相對簡單的。正如我剛才所說,三次函數與正弦波描述了動畫。我可以製作動畫這旋轉角度與線性過渡,哪裡值進展從 0 到 360 超過 1 第二通過添加一個立方的函數:

float duration = 1.0f;
HR(animation->AddCubic(0.0,
                       0.0f,
                       360.0f / duration,
                       0.0f,
                       0.0f));

AddCubic 方法的第三個參數指示線性係數,所以,很有意義。如果我把它忘在那裡的視覺會永恆每秒旋轉 360 度。我可以決定帶動畫到結束,一旦它達到 360 度,如下所示:

HR(animation->End(duration, 360.0f));

End 方法的第一個參數指示從動畫,無論動畫函數的最值的開頭的偏移量。第二個參數是動畫的最終值。牢記這一點因為動畫將"捕捉"到該值如果它不符合動畫曲線,並且這會產生不和諧的視覺效果。

這種線性的動畫是很容易的原因,但更複雜的動畫可以變得極其複雜。這是 Windows 動畫管理員進來的地方。 而無需調用 IDCompositionAnimation 添加正弦波和三次多項式片段、 重複的段,和更多的各種方法,我可以與 Windows 動畫管理員和轉換其豐富圖書館構建動畫演示圖板。當這樣做了時,我可以使用生成的動畫變數來填充組成動畫。這涉及到更多的代碼,但好處是更大的權力和控制您的應用程式的動畫。首先,我需要創建動畫管理員本身:

ComPtr<IUIAnimationManager2> manager;
HR(CoCreateInstance(__uuidof(UIAnimationManager2),
  nullptr, CLSCTX_INPROC, __uuidof(manager),
  reinterpret_cast<void **>(manager.GetAddressOf())));

是的 Windows 動畫管理員依賴于 COM 啟動,所以一定要致電 CoInitializeEx 或 RoInitialize 來初始化運行時。我還需要創建轉換庫:

ComPtr<IUIAnimationTransitionLibrary2> library;
HR(CoCreateInstance(__uuidof(UIAnimationTransitionLibrary2),
  nullptr, CLSCTX_INPROC, __uuidof(library),
  reinterpret_cast<void **>(library.GetAddressOf())));

通常情況下,應用程式將緊緊抓住這兩個物件他們一輩子因為他們需要連續的動畫,並針對速度匹配。接下來,我需要創建動畫演示圖板:

ComPtr<IUIAnimationStoryboard2> storyboard;
HR(manager->CreateStoryboard(storyboard.GetAddressOf()));

演示圖板是什麼轉換關聯的動畫變數,並定義隨著時間的推移他們相對的排程。演示圖板是能夠聚合各種轉換應用於不同的動畫變數 ; 它可以確保它們保持同步 ; 並計畫作為一個整體的演示圖板。當然,您可以創建多個故事板來安排獨立的動畫。現在,我需要問動畫管理員來創建動畫變數:

ComPtr<IUIAnimationVariable2> variable;
  HR(manager->CreateAnimationVariable(
    0.0, // initial value
    variable.GetAddressOf()));

一旦計畫了演示圖板,動畫管理員負責該變數保持為最新的所以應用程式可以在任何時間請求有效的值。在這種情況下,我只要去使用動畫變數來填充與線段組成動畫,然後丟棄它。現在我可以使用強大的轉換庫創建一個有趣的過渡效果的動畫變數:

ComPtr<IUIAnimationTransition2> transition;
HR(library->CreateAccelerateDecelerateTransition(
  1.0,   // duration
  360.0, // final value
  0.7,   // acceleration ratio
  0.3,   // deceleration ratio
  transition.GetAddressOf()));

過渡將導致動畫變數來加快和放慢在給定的持續時間內直到它來到休息在其最終值。比例用來影響如何相對快速的變數將加速,然後減速。請記住,比率結合不能超過一個價值之一。隨著動畫變數和過渡準備去,我可以將它們添加到分鏡腳本:

HR(storyboard->AddTransition(variable.Get(),
                             transition.Get()));

演示圖板是現在準備安排:

HR(storyboard->Schedule(0.0));

進度計畫方法的單一參數告訴調度程式目前的動畫時間。這是協調動畫和協調與組合引擎刷新率,更有用,但它將做為現在。在這一點上,動畫變數引和我可以通過它來填充曲線組成的動畫:

HR(variable->GetCurve(animation.Get()));

在這種情況下,就好像我打電話就是組成動畫,如下所示:

HR(animation->AddCubic(0.0, 0.0f, 0.0f, 514.2f, 0.0f));
HR(animation->AddCubic(0.7, 252.0f, 720.0f, -1200.0f, 0.0f));
HR(animation->End(1.0, 360.0f));

那是當然很少的代碼比花了使用 Windows 動畫管理員中,但是,數學的意義並不是那麼簡單。Windows 動畫管理員還提供了協調能力和順利過渡運行動畫,事情會變得非常困難,若要手動執行。


Kenny Kerr 是一個位於加拿大,以及作者 Pluralsight 和微軟最有價值球員的電腦程式員。他的博客 kennykerr.ca ,你可以跟著他在 Twitter 上 twitter.com/kennykerr

感謝以下的微軟技術專家對本文的審閱:Leonardo 布蘭科和James克拉克