共用方式為


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

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

先決條件

確定您已閱讀分鏡動畫主題。 本主題以 分鏡腳本動畫 中說明的動畫概念為基礎,不會再討論。 例如, 分鏡腳本動畫 描述如何以動畫為目標、分鏡腳本作為資源、 Timeline 屬性值,例如 DurationFillBehavior 等等。

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

關鍵影格動畫可以在動畫時間軸中的某個時刻使多個目標值被達成。 換句話說,每個關鍵幀可以指定一個不同的中間值,最後到達的關鍵幀是最終的動畫值。 透過指定多個要製作動畫的值,您可以製作更複雜的動畫。 關鍵影格動畫還支持不同的插補邏輯,且每種動畫類型的插補邏輯都會實作為不同的 KeyFrame 子類別。 具體而言,每個關鍵影格動畫類型都有其 KeyFrame 類別的離散線性樣條緩動變化,以指定其關鍵影格。 例如,若要指定以 Double 為目標並使用關鍵影格的動畫,您可以使用 DiscreteDoubleKeyFrameLinearDoubleKeyFrameSplineDoubleKeyFrameEasingDoubleKeyFrame 來宣告關鍵影格。 您可以在單一 KeyFrames 集合中使用所有這些類型中的任何一種,從而在每次達到新關鍵影格時改變插補方式。

對於插補行為,每個關鍵幀都會控制插補,直到達到其 KeyTime 時間為止。 它的 價值 也在那個時候達到。 如果超出此範圍還有更多關鍵影格,則該值會成為序列中下一個關鍵影格的起始值。

在動畫開始時,如果沒有 KeyTime 為 "0:0:0" 的關鍵影格存在,則起始值將是該屬性的非動畫值。 這類似於「//到動畫在沒有「從」時的行為方式。

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

除了 持續時間 之外,您還可以在關鍵影格動畫上設定所有以 Timeline 為基礎的屬性,就像在 //By 動畫中一樣,因為關鍵影格動畫類別也衍生自 Timeline。 這些包括:

  • 自動反轉:一旦到達最後一個關鍵影格,幀就會以與結尾相反的順序重複。 這會使動畫的表觀持續時間加倍。
  • BeginTime:延遲動畫的開始。 在達到 BeginTime 之前,影格中 KeyTime 值的時間軸不會開始計算,因此沒有切斷影格的風險
  • FillBehavior:控制當到達最後一個關鍵影格時的行為。 FillBehavior 對任何中間關鍵影格沒有影響。
  • RepeatBehavior
    • 如果設定為 「永久」,則關鍵影格及其時間軸會無限重複。
    • 如果設定為疊代計數,時間軸會重複多次。
    • 如果設定為 持續時間,則時間軸會重複,直到達到該時間為止。 如果動畫不是時間軸隱含持續時間的整數因數,這可能會在關鍵影格序列的中途截斷動畫。
  • SpeedRatio (不常用)

線性關鍵影格

線性關鍵影格會導致值的簡單線性插補,直到達到影格的 KeyTime 為止。 此插補行為最類似於分鏡腳本動畫主題中所述的更簡單的 //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 的直線表示隨時間變化的線性插值函數。 您的控制點會改變該曲線的形狀,從而隨著時間變化改變樣條動畫中函數的行為。 最好將其直觀地視為圖表。

下一個範例顯示三個不同的關鍵幀被應用於動畫,其中最後一個是針對 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>

這只是一個緩動函數範例。 我們將在下一節中介紹更多內容。

緩動功能

緩動函數可讓您將自訂數學公式套用至動畫。 數學運算通常有助於產生在二維座標系中模擬真實世界物理的動畫。 例如,您可能希望物件真實地彈跳或表現得像在彈簧上一樣。 您可以使用關鍵影格動畫甚至 「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 緩動函式是獨特的,因為它可以改變超出/範圍之外的值,或者是關鍵影格的值。 它會以相反於正常「/」行為預期的方向變更值來啟動動畫,再次返回到「從」或「開始」值,然後像平常一樣執行動畫。

在先前的範例中,我們示範如何宣告關鍵影格動畫的緩動函式。 下一個範例會將緩動函式套用至 //依據 動畫。

<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>

當將緩動函式套用至 //依據 動畫時,它會變更動畫在 持續時間內,值從 的插值隨時間變化的特性。 如果沒有緩動函數,這將是線性插值。

離散物件值動畫

其中一種類型的動畫值得特別提及,因為它是將動畫值套用至非 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 值來建立「投影片放映」動畫的一個有趣方式,作為多個物件值可能有用的範例。