到目前爲止,看到的全部動畫都使用線性插值從起點到終點。但若是須要建立具備多個分段的動畫和不規則移動的動畫。例如,可能但願建立一個動畫,快速地將一個元素滑入到視圖中,而後慢慢地將它移到正確位置。可經過建立兩個連續的動畫,並使用BeginTime屬性在第一個動畫以後開始第二個動畫來實現這種效果。然而,還有更簡單的方法——可以使用關鍵幀動畫。html
關鍵幀動畫是由許多較短的段構成的動畫。每段表示動畫中的初始值,最終值或中間值當運行動畫時,它平滑地從一個值移到另外一個值。ide
例如,分析下面的將RadialGradientBrush畫刷的中心點從一個位置移到另外一個位置的Point動畫:函數
<PointAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" From="0.7,0.3" To="0.3,0.7" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever"> </PointAnimation>
可以使用一個效果相同的PointAnimationUsingKeyFrames對象代替這個PointAnimation對象,以下所示:學習
<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" AutoReverse="True" RepeatBehavior="Forever">
<LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.3,0.7" KeyTime="0:0:10"></LinearPointKeyFrame>
</PointAnimationUsingKeyFrames>
這個動畫包含兩個關鍵幀。當動畫首次啓動時第一個關鍵幀設置Point值(若是但願使用在RadialGradientBrush畫刷中設置的當前值,可省略這個關鍵幀)。第二個關鍵幀定義結束值,這是10秒以後達到的數值。PointAnimationUsingKeyFrames對象執行線性插值。從第一個關鍵幀平滑移到第二個關鍵幀,就像PointAnimation對象對From和To值執行的操做同樣。動畫
可以使用一系列關鍵幀建立更有趣的示例。下面的動畫經過在不一樣的時刻到達的一系列位置經歷中心點。中心點的移動速度根據關鍵幀之間的持續時間以及須要移動的距離而改變。spa
<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" RepeatBehavior="Forever"> <LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.3,0.7" KeyTime="0:0:5"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></LinearPointKeyFrame> </PointAnimationUsingKeyFrames>
這個動畫不是可反轉的,但能夠重複。爲確保在一次迭代的最後數據和下一次迭代的開始數值之間不會出現跳躍,應使動畫的結束點和開始點位於相同的中心點。code
1、離散的關鍵幀動畫orm
上面示例中的關鍵幀動畫使用線性關鍵幀。因此,它在關鍵幀值之間平滑地過渡,另外一種選擇是使用離散的關鍵幀。對於這種狀況,不進行插值。當到達關鍵時間時,屬性忽然改變爲新值。xml
線性關鍵幀類使用「Linear+數據類型+KeyFrame」的形式進行命名。離散關鍵幀類使用「Discrete+數據類型+KeyFrame」的形式命名。下面是RadialGradientBrush畫刷示例的修改版本,在該修改版本中使用的是離散關鍵幀:htm
<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" RepeatBehavior="Forever"> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.3,0.7" KeyTime="0:0:5"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></DiscretePointKeyFrame> </PointAnimationUsingKeyFrames>
當運行這個動畫時,中心點在適當的時間從一個位置跳到下一個位置。這是戲劇性的(可是不平穩的)效果。
全部關鍵幀動畫類都支持離散關鍵幀,但只有一部分關鍵幀動畫類支持線性關鍵幀。這徹底取決於數據類型。支持線性關鍵幀的數據類型也支持線性插值,並提供了相應的DataTypeAnimation類,如Point、Color以及double。不支持線性插值的數據類型包括字符串和對象。
2、緩動關鍵幀
經過「【WPF學習】第五十一章 動畫緩動 」的學習,看到了如何使用緩動函數改進普通動畫。儘管關鍵幀動畫被分割成多段,但每段仍使用廣泛的、使人厭煩的線性插值。
若是這不是但願的結果,可以使用緩動函數爲每一個關鍵幀添加加速和減速的效果。然而,普通的線性插值關鍵幀類和離散關鍵幀類不支持該特徵。相反,須要使用緩動關鍵幀,如EasingDoubleKeyFrame、EasingColorKeyFrame或EasingPointKeyFrame。每一個緩動關鍵幀類和對應的線性插值關鍵幀類的工做方式相同,可是額外提供了EasingFunction屬性。
下面的示例使用動畫緩動爲前5秒得關鍵幀動畫應用加速效果:
<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" RepeatBehavior="Forever"> <LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame> <EasingPointKeyFrame Value="0.3,0.7" KeyTime="0:0:5"> <EasingPointKeyFrame.EasingFunction> <CircleEase></CircleEase> </EasingPointKeyFrame.EasingFunction> </EasingPointKeyFrame> <LinearPointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></LinearPointKeyFrame> <LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></LinearPointKeyFrame> </PointAnimationUsingKeyFrames>
結合使用關鍵幀和動畫緩動是構建複雜動畫模型的簡便方式,但仍可能沒法提供所需的控制。不使用動畫緩動,可建立數學公式指示動畫的進度。
3、樣條關鍵幀動畫
還有一種關鍵幀類型:樣條關鍵幀。每一個支持線性關鍵幀的類也支持樣條關鍵幀,它們使用「Spline+數據類型+KeyFrame」的形式進行命名。
與線性關鍵幀同樣,樣條關鍵幀使用插值從一個鍵值平滑地移到另外一個鍵值。區別是每一個樣條關鍵幀都是KeySpline屬性。可以使用該屬性定義能影響插值方式的三次貝塞爾曲線。儘管爲了獲得但願的效果這樣作有些繁瑣,但這種技術能建立更加連貫的加速和減速以及更逼真的動畫效果。
在前面章節學習過,貝塞爾曲線由起點、終點以及兩個控制點定義。對於關鍵樣條,起點老是(0,0),終點老是(1,1)。用戶只須要提供兩個控制點。建立的曲線描述了時間(X軸)和動畫值(Y值)之間的關係。
下面的示例經過對比Canvas面板上兩個橢圓的移動,演示了一個關鍵幀樣條動畫。第一個橢圓使用DoubleAnimation動畫緩慢勻速地再窗口上移動。第二個橢圓使用具備兩個SplineDoubleKeyFrame對象的DoubleAnimationUsingKeyFrames動畫。兩個橢圓同時到達目的位置(10秒後),但第二個橢圓在運動過程當中會有明顯的加速和減速,減速時會超過第一個橢圓而減速時又會落後於第一個橢圓。
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" > <SplineDoubleKeyFrame KeyTime="0:0:5" Value="250" KeySpline="0.25,0 0.5,0.7"></SplineDoubleKeyFrame> <SplineDoubleKeyFrame KeyTime="0:0:10" Value="500" KeySpline="0.25,0.8 0.2,0.4"></SplineDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimation Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)" To="500" Duration="0:0:10"> </DoubleAnimation>
最快的加速發生在5秒後不久,也就是當進入第二個SplineDoubleKeyFrame關鍵幀時。貝塞爾曲線的第一個控制點將較大的表示動畫進度(0.8)的Y軸值與較小的表示時間的X軸值相匹配。因此,在再次減慢速度前,橢圓在一小段距離內會增長速度。
下圖以圖形方式顯示了兩條控制橢圓運動的曲線。爲理解這些曲線,請記住它們從頂部到底部描述了動畫過程。觀察第一條曲線能夠發現,它相對均勻地降低,在開始處有較短的暫停,在末尾處平緩降低。然而第二條曲線快速降低,運動了一個大段距離,而後對於剩餘的動畫部分,曲線緩緩降低。
示例的完整XAML標記以下所示:
<Window x:Class="Animation.KeySplineAnimation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="KeySplineAnimation" Height="300" Width="624" WindowStartupLocation="CenterScreen"> <Window.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" > <SplineDoubleKeyFrame KeyTime="0:0:5" Value="250" KeySpline="0.25,0 0.5,0.7"></SplineDoubleKeyFrame> <SplineDoubleKeyFrame KeyTime="0:0:10" Value="500" KeySpline="0.25,0.8 0.2,0.4"></SplineDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimation Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)" To="500" Duration="0:0:10"> </DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Window.Triggers> <Canvas Margin="10"> <Ellipse Name="ellipse1" Canvas.Left="0" Fill="Red" Width="10" Height="10"></Ellipse> <Path Stroke="Blue" StrokeThickness="1" StrokeDashArray="2 1" Canvas.Top="25"> <Path.Data> <PathGeometry> <PathFigure> <BezierSegment Point1="25,0" Point2="50,70" Point3="100,100" /> </PathFigure> </PathGeometry> </Path.Data> <Path.RenderTransform> <ScaleTransform ScaleX="2.5"></ScaleTransform> </Path.RenderTransform> </Path> <Path Stroke="Blue" StrokeThickness="1" StrokeDashArray="2 1" Canvas.Left="250" Canvas.Top="25"> <Path.Data> <PathGeometry> <PathFigure> <BezierSegment Point1="25,80" Point2="20,40" Point3="100,100" /> </PathFigure> </PathGeometry> </Path.Data> <Path.RenderTransform> <ScaleTransform ScaleX="2.5"></ScaleTransform> </Path.RenderTransform> </Path> <Ellipse Name="ellipse2" Canvas.Top="150" Canvas.Left="0" Fill="Red" Width="10" Height="10"></Ellipse> </Canvas> </Window>