以關聯為基礎的動畫

本文提供的簡短概觀,說明如何使用 Composition ExpressionAnimations 建立以關聯為基礎的動畫。

動態式以關聯為基礎的體驗

在應用程式中建置動作體驗時,有時候動作不是以時間為基礎,而是取決於另一個物件上的屬性。 KeyFrameAnimations 無法非常輕鬆地表達這些類型的動作體驗。 在這些特定執行個體中,動作不再需要是離散的,也不再需要預先定義。 相反地,動作可以根據與其他物件屬性的關聯性,加以動態調整。 例如,您可以根據物件的水平位置,以動畫顯示物件的不透明度。 其他範例包括動作體驗,例如黏性標頭和視差。

這些類型的動作體驗可讓您建立更有連結感的 UI,而不再感覺孤單且獨立。 對使用者而言,這會留下動態 UI 體驗的印象。

Orbiting circle

List view with parallax

使用 ExpressionAnimations

若要建立以關聯為基礎的動作體驗,您可以使用 ExpressionAnimation 類型。 ExpressionAnimations (或簡稱運算式),是一種新的動畫類型,可讓您表達數學關聯性 – 系統會使用這個關聯性,來計算每個畫面的動畫屬性值。 換句話說,運算式只是一個數學方程式,可定義每個畫面之動畫屬性的需求值。 運算式是一個非常多用途的元件,可用於各種不同的案例,包括:

使用 ExpressionAnimations 時,有幾件事值得事先說明:

  • 永不結束 – 與其同層級的 KeyFrameAnimation 不同,運算式沒有持續時間限制。 由於運算式是數學關聯性,所以它們是不斷「執行」的動畫。 您可以選擇是否有選項可停止這些動畫。
  • 不斷執行,但不會一直評估 – 對於不斷執行的動畫,效能始終是必要考量。 不過也不需要擔心,系統就足夠聰明,運算式只會在其任何輸入或參數變更時重新評估。
  • 解析為正確的物件類型 – 因為運算式是數學關聯性,所以請務必確定,要定義運算式之方程式所解析的類型,與動畫所鎖定的屬性相同。 例如,如果以動畫顯示位移,則運算式應該解析為 Vector3 類型。

運算式的元件

建置運算式的數學關聯性時,有幾個核心元件:

  • 參數 – 代表常數值或其他組合物件的參考值。
  • 數學運算子 – 運用加號 (+)、減號 (-)、乘號 (*)、除號 (/) 等一般數學運算子,將參數聯結在一起,形成方程式。 此外也包括條件運算子,例如大於 (>)、equal (==)、三元運算子 (condition ? ifTrue : ifFalse) 等。
  • 數學函式 – 以 System.Numerics 為基礎的數學函式/快捷方式。 如需支援函式的完整清單,請參閱 ExpressionAnimation

運算式也支援一組關鍵詞 – 只在 ExpressionAnimation 系統中具有不同意義的特殊片語。 這些 (以及數學函式的完整清單) 都列在 ExpressionAnimation 文件中。

使用 ExpressionBuilder 建立運算式

在 UWP 應用程式中建置運算式有兩個選項:

  1. 透過官方的公用 API,將方程式建置為字串。
  2. 透過 Windows Community Toolkit 隨附的 ExpressionBuilder 工具,在型別安全物件模型中建置方程式。

為了本文件方便說明起見,我們將使用 ExpressionBuilder 來定義運算式。

參數

參數是組成運算式的核心。 參數可分為兩種:

  • 常數:這些是代表型別 System.Numeric 變數的參數。 這些參數會在動畫啟動時取得指派的值一次。
  • 參考:這些是代表 CompositionObjects 參考的參數 – 這些參數會在動畫啟動後,持續取得更新其值。

一般而言,參考是運算式輸出可以如何動態改變的主要層面。 當這些參考改變時,運算式的輸出也會隨之改變。 如果您使用字串建立運算式,或在範本化案例中使用它們 (使用運算式以多個 CompositionObjects 為目標),則必須命名並設定參數的值。 如需詳細資訊,請參閱「範例」區段。

使用 KeyFrameAnimations

運算式也可和 KeyFrameAnimations 搭配使用。 在這些執行個體中,您想要使用運算式在某個時間點定義 KeyFrame 的值 – 這些 KeyFrame 型別就稱為 ExpressionKeyFrames。

KeyFrameAnimation.InsertExpressionKeyFrame(Single, String)
KeyFrameAnimation.InsertExpressionKeyFrame(Single, ExpressionNode)

不過,與 ExpressionAnimations 不同,只有在 KeyFrameAnimation 啟動時,才會評估 ExpressionKeyFrames。 請記住,不要傳入 ExpressionAnimation 作為 KeyFrame 的值,要傳入字串 (如果您使用的是 ExpressionBuilder,則要傳入 ExpressionNode)。

範例

現在讓我們逐步解說使用運算式的範例,特別是來自 Windows UI 範例庫的 PropertySet 範例。 我們將探討管理藍球軌道運動行為的運算式。

Orbiting circle

有三個登場元件構成整體體驗:

  1. KeyFrameAnimation,以動畫顯示紅球的 Y 位移。
  2. 具有 Rotation 屬性的 PropertySet,可協助驅動由另一個 KeyFrameAnimation 產生動畫的軌道。
  3. ExpressionAnimation,參考紅球位移和旋轉屬性來驅動藍球位移,以維持完美的軌道。

我們會將重點放在第 3 點中定義的 ExpressionAnimation。 我們也會使用 ExpressionBuilder 類別來建構此運算式。 最後會列出透過字串建置此體驗所使用的程式碼複本。

在此方程式中,您需要從 PropertySet 參考兩個屬性;一個是中心點位移,另一個是旋轉。

var propSetCenterPoint =
_propertySet.GetReference().GetVector3Property("CenterPointOffset");

// This rotation value will animate via KFA from 0 -> 360 degrees
var propSetRotation = _propertySet.GetReference().GetScalarProperty("Rotation");

接下來,您需要定義負責實際軌道旋轉的 Vector3 元件。

var orbitRotation = EF.Vector3(
    EF.Cos(EF.ToRadians(propSetRotation)) * 150,
    EF.Sin(EF.ToRadians(propSetRotation)) * 75, 0);

注意

EF 是簡略 “using” 標記法,用來定義 ExpressionFunctions。

using EF = Microsoft.Toolkit.Uwp.UI.Animations.Expressions.ExpressionFunctions;

最後,將這些元件結合在一起,並參考紅球的位置,以定義數學關聯性。

var orbitExpression = redSprite.GetReference().Offset + propSetCenterPoint + orbitRotation;
blueSprite.StartAnimation("Offset", orbitExpression);

讓我們來假設,如果您想要使用這個相同的運算式,但還要結合其他兩個視覺效果,也就是 2 組軌道圓形。 使用 CompositionAnimations,您可以重複使用動畫,並以多個 CompositionObjects 為目標。 當您使用此運算式來處理其他軌道的情況時,唯一需要變更的部分,只有視覺效果的參考。 我們稱之為範本化。

在此情況下,您可以修改稍早建置的運算式。 您不是「取得」CompositionObject 的參考,而是使用名稱建立參考,然後指派不同的值:

var orbitExpression = ExpressionValues.Reference.CreateVisualReference("orbitRoundVisual");
orbitExpression.SetReferenceParameter("orbitRoundVisual", redSprite);
blueSprite.StartAnimation("Offset", orbitExpression);
// Later on … use same Expression to assign to another orbiting Visual
orbitExpression.SetReferenceParameter("orbitRoundVisual", yellowSprite);
greenSprite.StartAnimation("Offset", orbitExpression);

如果您是透過公用 API 使用字串來定義運算式,以下是所使用的程式碼。

ExpressionAnimation expressionAnimation = compositor.CreateExpressionAnimation("visual.Offset + " +
    "propertySet.CenterPointOffset + " +
    "Vector3(cos(ToRadians(propertySet.Rotation)) * 150," + "sin(ToRadians(propertySet.Rotation)) * 75, 0)");
    
var propSetCenterPoint = _propertySet.GetReference().GetVector3Property("CenterPointOffset");
var propSetRotation = _propertySet.GetReference().GetScalarProperty("Rotation");
expressionAnimation.SetReferenceParameter("propertySet", _propertySet);
expressionAnimation.SetReferenceParameter("visual", redSprite);