關鍵影格動畫和緩動函式動畫

線性關鍵影格動畫、具有 KeySpline 值的關鍵影格動畫或緩動函式是針對大致相同場景的三種不同技術:建立更複雜的分鏡動畫,並使用從開始狀態到結束狀態的非線性動畫行為。

必要條件

請確定您已閱讀分鏡動畫主題。 本主題以分鏡動畫中解釋的動畫概念為基礎,因此不再贅述。 例如,分鏡動畫描述如何定位動畫、作為資源的分鏡、Timeline 屬性值 (例如DurationFillBehavior 等)。

使用關鍵影格動畫製作動畫

關鍵影格動畫允許在動畫時間軸上的某一點達到多個目標值。 也就是說,每個關鍵影格可以指定不同的中間值,最後到達的關鍵影格就是最終的動畫值。 透過指定多個動畫值,您可以製作更複雜的動畫。 關鍵影格動畫也支援不同的插補邏輯,每個插補邏輯都作為每個動畫類型的不同 KeyFrame 子類別實作。 具體來說,每個關鍵影格動畫類型都有其 KeyFrame 類別的 DiscreteLinearSplineEasing 變體,用於指定其關鍵影格。 例如,要指定以 Double 為目標並使用關鍵影格的動畫,您可以使用 DiscreteDoubleKeyFrameLinearDoubleKeyFrameSplineDoubleKeyFrameEasingDoubleKeyFrame 宣告關鍵影格。 您可以在單一 KeyFrames 集合中使用任何或所有這些類型,以便在每次到達新關鍵影格時變更插補補點。

對於插補行為,每個關鍵影格都會控制插補,直到達到其 KeyTime 時間。 此時也會達到其。 如果還有更多關鍵影格,該值就會成為序列中下一個關鍵影格的起始值。

在動畫開始時,如果不存在 KeyTime 為「0:0:0」的關鍵影格,則起始值是屬性的非動畫值。 這類似於沒有 From 時,From/To/By 動畫的行為。

關鍵影格動畫的持續時間隱式等於其任何關鍵影格中設定的最高 KeyTime 值。 如果需要,您可以設定顯式的 Duration,但請注意,它不能短於您自己的關鍵影格中的 KeyTime,否則您將切斷部分動畫。

除了 Duration 之外,您還可以在關鍵影格動畫上設定所有基於 Timeline 的屬性,就像使用 From/To/By 動畫一樣,因為關鍵影格動畫類別也衍生自 Timeline。 這些包括:

  • AutoReverse:到達最後一個關鍵影格後,將從最後開始以相反順序重複這些影格。 這會加倍動畫的表觀持續時間。
  • BeginTime:延遲動畫的開頭。 影格中 KeyTime 值的時間軸在到達 BeginTime 之前不會開始計算,因此不存在切斷影格的風險
  • FillBehavior:控制到達最後一個關鍵影格時的情況。 FillBehavior 不會影響任何中繼關鍵影格。
  • RepeatBehavior
    • 如果設定為永遠,則關鍵影格及其時間軸將會無限重複。
    • 如果設定為迭代計數,則時間軸會重複多次。
    • 如果設定為持續時間,則時間軸將重複,直到到達該時間。 如果它不是時間軸隱式持續時間的整數因子,這可能會截斷關鍵影格序列中的動畫。
  • SpeedRatio (不常用)

線性關鍵影格

線性關鍵影格會導致值的簡單線性插補補點,直到達到該影格的 KeyTime。 此插補行為與分鏡動畫主題中所述較簡單的 From/To/By 動畫最相似。

以下是如何使用關鍵影格動畫來縮放矩形的渲染高度 (使用線性關鍵影格)。 本範例會執行一個動畫,其中矩形的高度在前 4 秒內略微線性增加,然後在最後一秒快速縮放,直到矩形的高度達到起始高度的兩倍。

<StackPanel>
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimationUsingKeyFrames
              Storyboard.TargetName="myRectangle"
              Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <LinearDoubleKeyFrame Value="1" KeyTime="0:0:0"/>
                <LinearDoubleKeyFrame Value="1.2" KeyTime="0:0:4"/>
                <LinearDoubleKeyFrame Value="2" KeyTime="0:0:5"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </StackPanel.Resources>
</StackPanel>

離散關鍵影格

離散關鍵影格完全不使用任何插補。 當達到 KeyTime 時,只需套用新的。 根據正在設定動畫的 UI 屬性,這通常會產生看起來像「跳躍」的動畫。 請確認這真的是您想要的美學行為。 您可以透過增加宣告的關鍵影格數量來最小化明顯的跳躍,但如果您的目標是平滑的動畫,則最好使用線性或樣條關鍵影格。

注意

離散關鍵影格是使用 DiscreteObjectKeyFrame 對非 DoublePointColor 類型的值進行動畫處理的唯一方法。 本主題稍後會更詳細地討論這一點。

樣條關鍵影格

樣條關鍵影格會根據 KeySpline 屬性的值在值之間建立可變轉換。 此屬性會指定貝茲樣條的第一個和第二個控制點,其描述了動畫的加速度。 基本上,KeySpline 會定義函式時間關聯性,其中函式時間圖形是貝茲樣條的形狀。 通常在 XAML 速記屬性字串中指定 KeySpline 值,該字串具有四個以空格或逗號分隔的 Double 值。 這些值是貝塞爾樣條兩個控制點的「X,Y」組。 「X」是時間,而「Y」是值的函式修飾詞。 每個值應始終介於 0 和 1 之間 (含 0 和 1)。 如果不對 KeySpline 進行控制點修改,則從 0,0 到 1,1 的直線表示線性插補隨時間變化的函式。 您的控制點會改變該樣條的形狀,從而改變樣條動畫函式隨時間的行為。 最好以圖表的形式直觀地觀察此行為。 您可以在瀏覽器中執行 Silverlight 關鍵樣條視覺化工具範例以查看控制點如何修改樣條,以及範例動畫在將其用作 KeySpline 值時如何運作。

下一個範例顯示了套用於動畫的三個不同關鍵影格,最後一個是 Double 值的關鍵樣條動畫 (SplineDoubleKeyFrame)。 請注意套用於 KeySpline 的字串 "0.6,0.0 0.9,0.00"。 這會產生一條樣條,其中動畫起初會緩慢執行,但隨後快速達到 KeyTime 之前的值。

<Storyboard x:Name="myStoryboard">
    <!-- Animate the TranslateTransform's X property
        from 0 to 350, then 50,
        then 200 over 10 seconds. -->
    <DoubleAnimationUsingKeyFrames
        Storyboard.TargetName="MyAnimatedTranslateTransform"
        Storyboard.TargetProperty="X"
        Duration="0:0:10" EnableDependentAnimation="True">

        <!-- Using a LinearDoubleKeyFrame, the rectangle moves 
            steadily from its starting position to 500 over 
            the first 3 seconds.  -->
        <LinearDoubleKeyFrame Value="500" KeyTime="0:0:3"/>

        <!-- Using a DiscreteDoubleKeyFrame, the rectangle suddenly 
            appears at 400 after the fourth second of the animation. -->
        <DiscreteDoubleKeyFrame Value="400" KeyTime="0:0:4"/>

        <!-- Using a SplineDoubleKeyFrame, the rectangle moves 
            back to its starting point. The
            animation starts out slowly at first and then speeds up. 
            This KeyFrame ends after the 6th second. -->
        <SplineDoubleKeyFrame KeySpline="0.6,0.0 0.9,0.00" Value="0" KeyTime="0:0:6"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

緩動關鍵影格

緩動關鍵影格是套用插補的關鍵影格,插補隨時間的函式由幾個預先定義的數學公式控制。 實際上,使用樣條關鍵影格可以產生與使用某些緩動函式類型大致相同的結果,但也有一些緩動函式 (例如 BackEase) 無法使用樣條重現。

若要將緩動函式套用於緩動關鍵影格,請將 EasingFunction 屬性設定為該關鍵影格 XAML 中的屬性元素。 對於該值,請指定緩動函式類型之一的物件元素。

此範例套用 CubicEase,然後套用 BounceEase 作為 DoubleAnimation 的連續關鍵影格,以建立彈跳效果。

<Storyboard x:Name="myStoryboard">
    <DoubleAnimationUsingKeyFrames Duration="0:0:10"
        Storyboard.TargetProperty="Height"
        Storyboard.TargetName="myEllipse">

        <!-- This keyframe animates the ellipse up to the crest 
            where it slows down and stops. -->
        <EasingDoubleKeyFrame Value="-300" KeyTime="00:00:02">
            <EasingDoubleKeyFrame.EasingFunction>
                <CubicEase/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>

        <!-- This keyframe animates the ellipse back down and makes
            it bounce. -->
        <EasingDoubleKeyFrame Value="0" KeyTime="00:00:06">
            <EasingDoubleKeyFrame.EasingFunction>
                <BounceEase Bounces="5"/>
            </EasingDoubleKeyFrame.EasingFunction>
        </EasingDoubleKeyFrame>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

這只是一個緩動函式範例。 我們將在下一節中進一步說明。

Easing 函式

Easing 函式可讓您將自訂的數學公式套用至動畫。 數學運算通常用於產生在二維座標系中模擬現實世界物理行為的動畫。 例如,您可能想要物件實際表現出反彈或像是在彈簧上一樣。 您可以使用關鍵影格或甚至 From/To/By 動畫來模擬這些效果,但所需的工作量可能很大,而且和使用數學公式相比,動畫會較不精確。

緩動函式可以透過三種方式套用於動畫:

以下是緩動函式的清單:

  • BackEase︰稍微在動畫開始以指定的路徑顯示之前,撤銷動畫的移動。
  • BounceEase:建立彈跳效果。
  • CircleEase:建立使用循環函式加速或減速的動畫。
  • CubicEase:建立使用公式 f(t) = t3 加速或減速的動畫。
  • ElasticEase:建立類似彈簧來回擺動直到靜止下來的動畫。
  • ExponentialEase:建立使用指數公式加速或減速的動畫。
  • PowerEase:建立使用公式 f(t) = tp 加速或減速的動畫,其中 p 等於 Power 屬性。
  • QuadraticEase:建立使用公式 f(t) = t2 加速或減速的動畫。
  • QuarticEase:建立使用公式 f(t) = t4 加速或減速的動畫。
  • QuinticEase:建立使用公式 f(t) = t5 加速或減速的動畫。
  • SineEase:建立使用正弦公式加速或減速的動畫。

部分緩動函式有其自己的屬性。 例如,BounceEase 有兩個屬性 BouncesBounciness,它們可修改特定 BounceEase 的函式隨時間變化的行為。 其他緩動函式 (例如 CubicEase) 除了所有緩動函式共用的 EasingMode 屬性之外沒有其他屬性,並且始終產生相同的函式隨時間變化的行為。

其中部分緩動函式有一些重疊,視您在具有屬性的緩動函式上設定屬性的方式而定。 例如,QuadraticEasePower 等於 2 的 PowerEase 完全相同。 CircleEase 基本上就是一個預設值 ExponentialEase

BackEase 緩動函式是特別的,因為它可以變更由 From/To 或關鍵影格值設定的正常範圍之外的值。 它透過以正常 From/To 行為預期的相反方向變更值來啟動動畫,再次返回 From 或起始值,然後正常執行動畫。

在前面的範例中,我們示範了如何為關鍵影格動畫宣告緩動函式。 下一個範例會將緩動函式套用於 From/To/By 動畫。

<StackPanel x:Name="LayoutRoot" Background="White">
    <StackPanel.Resources>
        <Storyboard x:Name="myStoryboard">
            <DoubleAnimation From="30" To="200" Duration="00:00:3" 
                Storyboard.TargetName="myRectangle" 
                Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
                <DoubleAnimation.EasingFunction>
                    <BounceEase Bounces="2" EasingMode="EaseOut" 
                                Bounciness="2"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
    </StackPanel.Resources>
    <Rectangle x:Name="myRectangle" Fill="Blue" Width="200" Height="30"/>
</StackPanel>

當將緩動函式套用於 From/To/By 動畫時,它會更改該值在動畫持續時間內如何在 From and To 值之間進行插補的函式隨時間特徵。 如果沒有緩動函式,那將會是線性插補。

離散物件值動畫

有一個類型的動畫特別值得說明,因為只有它能將動畫值套用到非 DoublePointColor 類型的屬性。 那就是關鍵影格動畫 ObjectAnimationUsingKeyFrames。 使用 Object 值的動畫效果不同,因為無法在影格之間插補值。 當達到影格的 KeyTime 時,動畫值會立即設定為關鍵影格的 Value 中指定的值。 由於沒有插補,因此 ObjectAnimationUsingKeyFrames 關鍵影格集合中僅使用一個關鍵影格:DiscreteObjectKeyFrame

DiscreteObjectKeyFrameValue 通常使用屬性元素語法來設置,因為您嘗試設定的物件值通常無法表示為字串來填充屬性語法中的 Value。 如果使用參考 (例如 StaticResource),您仍然可以使用屬性語法。

在預設範本中使用 ObjectAnimationUsingKeyFrames 的其中一種情境是當範本屬性參考 Brush 資源時。 這些資源是 SolidColorBrush 物件,而不僅僅是 Color 值,並且它們使用定義為系統主題 (ThemeDictionaries) 的資源。 它們可以直接指派給 Brush 類型值,例如 TextBlock.Foreground,不需要使用間接定位。 但由於 SolidColorBrush 不是 DoublePointColor,因此您必須使用 ObjectAnimationUsingKeyFrames 來使用資源。

<Style x:Key="TextButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Grid Background="Transparent">
                    <TextBlock x:Name="Text"
                        Text="{TemplateBinding Content}"/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
...
                       </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

您也可以使用 ObjectAnimationUsingKeyFrames ,以動畫顯示使用列舉值的屬性。 以下是來自 Windows 執行時期預設範本命名樣式的另一個範例。 請注意它如何設定採用 Visibility 列舉常數的 Visibility 屬性。 在這種情況下,您可以使用屬性語法設定值。 您只需要列舉中的非限定常數名稱即可設定具有列舉值的屬性,例如「Collapsed」。

<Style x:Key="BackButtonStyle" TargetType="Button">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">
          <Grid x:Name="RootGrid">
            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CommonStates">
              <VisualState x:Name="Normal"/>
...           <VisualState x:Name="Disabled">
                <Storyboard>
                  <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame Value="Collapsed" KeyTime="0"/>
                  </ObjectAnimationUsingKeyFrames>
                </Storyboard>
              </VisualState>
            </VisualStateGroup>
...
          </VisualStateManager.VisualStateGroups>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

對於一個 ObjectAnimationUsingKeyFrames 影格集,您可以使用多個 DiscreteObjectKeyFrame。 這可能是一種透過對 Image.Source 的值進行動畫處理來建立「投影片放映」動畫的有趣方法,作為多個物件值可能有用的範例場景。