Animation規則
基于時間:你設置動畫的初始狀態,最終狀態,及持續時間,Silverlight會計算幀速率。
作用于屬性(properties):一個Silverlight動畫只能做一件事情,在某段時間內修改某個屬性的值。這似乎是一個很大的限制,但你可以通過同時修改多個屬性來創造令人驚訝的動畫效果。
不同的數據類型需要不同的動畫類。比如:Button.Width是double類型的,要創作針對這個屬性的動畫,你就要使用DoubleAnimation類。如果你想改變背景色,你就需要ColorAnimation 類。
Silverlight只有相當有限的幾個Animation 類,你能修改的屬性只限于以下幾種類型:double, object,Color,Point.但是你也可以創建自己的animation類,用于不同的數據類型,你只需要繼承System.Windows.Media.Animation,并指明值是如何隨著時間改變的。
創建簡單動畫
創建一個簡單的動畫是多步驟的過程。你需要三個獨立的成分:執行動畫的對象,管理動畫的故事板,和一個觸發故事板的處理程序。
Animation Class
Silverlight有兩種動畫類,它們都用不同的策略來實現對值的改變。
線性插值:在動畫期間,屬性值連續不斷的平衡變化。(你可以很容易的創建出包含加速與減速的復雜動作)。Silverlight包含三個這樣的類:DoubleAnimation, PointAnimation, and ColorAnimation
關鍵幀動畫:可以從一個值迅速轉移到另一個值,或者他們可以結合跳躍和線性插值。Silverlight包含四個這樣的類:
ColorAnimationUsingKeyFrames, DoubleAnimationUsingKeyFrames,
PointAnimationUsingKeyFrames, and ObjectAnimationUsingKeyFrames
動畫是通過XAML語言來標記的。雖然動畫類不是元素,但它們可以用相同的XAML語法來創建。例如,下面的標記創建了一個DoubleAnimation:
<DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation>
這個動畫持續5秒(就如Duration屬性指明的一樣,它的格式是 Hours:Minutes:Seconds),動畫運行時,它會把目標值從160平穩的改變到300。
然而,上面這段XAML丟失了一個重要的細節:我們并沒有指明要改變的是哪個屬性。這是我們所要說的另一個類--故事板所要做的工作。
The Storyboard Class
故事板管理動畫的時間軸。你可以用故事板來分組多個動畫,并對動畫進行管理--暫停,停止,改變位置。但故事板最基本的特性是它提供了兩個屬性TargetProperty與TargetName來指明動畫作用的屬性與名稱。換言之,故事板是動畫與屬性之間的橋梁。
這里說明如何定義一個故事板,通過DoubleAnimation 來改變名稱為cmdGrow的button的Width屬性。
<Storyboard x:Name="storyboard" Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width"> <DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard>
Storyboard.TargetProperty表明了你想改變的屬性(在這里是width)。如果你不指定一個類名,故事板會使用父元素。如果你想設置附加屬性(例如,Canvas.Left or Canvas.Top),你需要用括號包起來:
<Storyboard x:Name="storyboard" Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="(Canvas.Left)"> ... </Storyboard>
如果TargetProperty與TargetName都是附加屬性,可以像下面那樣指定:
<Storyboard x:Name="storyboard"> <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard>
上面這種語法更常見,因為它允許你把幾個動畫放在相同的故事板,但每個動畫設置不同的元素和屬性。雖然你不能同時改變多個動畫的同一個屬性,但你可以(比較經常的)同時改變同一個對象的不同屬性
Starting an Animation with an Event Trigger
定義一個故事板和動畫對象是創建動畫的第一步。要使故事板生效,你需要一個事件觸發器。一個事件觸發器響應故事板的執行事件。目前Silverlight通過BeginStoryboard來啟動
故事板(包括它包含的動畫)。
下面的例子使用頁面的觸發器集合附加一個加載動畫事件。當Silverlight內容第一次呈現在瀏覽器中,且元素已加載完成,button就開始變形。5s之內,它的寬度由160變到300.
<UserControl ... > <UserControl.Triggers> <EventTrigger> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </UserControl.Triggers> <Grid x:Name="LayoutRoot" Background="White"> <Button x:Name="cmdGrow" Width="160" Height="30" Content="This button grows"></Button> </Grid> </UserControl>
不幸的是,相對于WPF事件,Silverlight有著及大的限制。Silverlight只支持頁面第一次創建的時候觸發Loaded事件。對于其它事件不能作出相應的響應,比如點擊,按鍵和鼠標移動。
Starting an Animation with Code
在代碼中,我們可以通過任意事件與故事板交互來啟動Silverlight動畫。我們要做的第一步,便是把故事板移到資源文件中。
<UserControl ... > <UserControl.Resources> <Storyboard x:Name="storyboard"> <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" From="160" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <Button x:Name="cmdGrow" Width="160" Height="30" Click="cmdGrow_Click" Content="This button grows"></Button> </Grid> </UserControl>
現在故事板有了一個名字,你可以在你的代碼中操作它了。如:
private void cmdGrow_Click(object sender, RoutedEventArgs e) { storyboard.Begin(); }
單擊按鈕,啟動動畫,按鈕的寬度會從160px拉伸到300px,如下圖所示:
Configuring Animation Properties
要深入了解Silverlight動畫,你需要仔細看看那些看似簡單的基本屬性,包括From,To,Duration等。
FROM
From用來設置起始值。有前面的例子中,動畫起始于160px.因些,每次你單擊按鈕啟動動畫,寬度會重置為160px,動畫再次運行。即使你單擊的時候,剛好有一個動畫正在運行也是如此。(這也說明一個Silverlight動畫細節:依賴屬性只難存在于一個動畫,當你開始第二個動畫的時候,第一個動畫便被丟棄)。
如果你在上面的例子中移除掉From屬性,你可以多次單擊按鈕而不會將動畫重置。每點一次,都開始一個新動畫,在當前值開始變動。但是,當按鈕到達最大值時,繼續點擊會變得無效,除非你讓按鈕回到原始值。
在上面的例子中,我們還可以設置ActualWidth屬性,表示按鈕的當前寬度。你不能用動畫來改變它,因為它是只讀的,但你可以用它來設置動畫的起始位置。
TO
就如可以省略FROM屬性一樣,我們也可以省略TO屬性。如:
<DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" Duration="0:0:5"></DoubleAnimation>
乍一看,這似乎什么都做不了,因為這既沒有設置FROM屬性與沒有設置TO屬性,他們都是相同的值。但這里有一個微妙且重要的區別。
當我們省略FROM時,Silverlight會將元素的當前值考慮到動畫中,如前面例子中按鈕的當前寬度。然而,當我們省略TO屬性時,Silverlight豪不猶豫的使用當前值,即TO屬性會變成最初的那個值。
BY
我們也可以使用BY屬性,而不是TO。動畫可以通過BY屬性設置每一次的變化量,而不是最動畫最終的值。比如,你可以通過BY屬性,將當前寬度每次增加10PX.
<DoubleAnimation Storyboard.TargetName="cmdGrow" By="10" Storyboard.TargetProperty="Width" Duration="0:0:5"></DoubleAnimation>
BY屬性并不適合所有的非數值改變的動畫類,比如ColorAnimation。
Duration
Duration表示動畫從開始到結束之間的時間間隔。Duration屬性的值要求是Duration類型的,類似TimeSpan。事實上,Duration類型可以隱匿的通過TimeSpan類型轉換過來。
widthAnimation.Duration = TimeSpan.FromSeconds(5);
為什么微軟要設置Duration類型,而不直接使用TimeSpan?因為Duration有兩個特別的值是不能TimeSpan來表示的:Duration.Automatic與Duration.Forever。Duration.Automatic表示1s的持續時間,而Duration.Forever則表示永遠的,這會讓動畫不產生任何影響。
Animation Lifetime
技術上來說Silverlight動畫都是暫時的,這意味著它們不改變對象的基本特征。一旦啟動了一個動畫,它只是改覆蓋了屬性的當前值。因為這就是依賴屬性的工作方式,這是一個很容易忽略的細節,并極易造成混亂。
一個單向動畫(就像上面中按鈕寬度的增長),當它完成運行后仍然是活動的。這是因為動畫需要保持按鈕寬度在新的尺寸。這會導致一個很常見的問題:當你在動畫完成后,用代碼修改屬性的值,你的代碼似乎沒有任何影響。你的代碼分配了一個新的本地值,但動畫擁有較高的優先級。
對于這個問題,有下面幾種解決方法:
1.創建一個動畫,將你的元素設置城初始狀態:這種方法不能設置TO屬性。
2.創建一個可逆的動畫:設置AutoReverse屬性為true.
3.改變FillBehavior屬性:通常,將FillBehavior屬性設置HoldEnd,意味著動畫結束時,目標屬性就會回到原來的值。
4.動畫結束時回收動畫對象:在Completed事件中處理動畫或故事板對象。
這里舉一下第四種做法,在Completed將動畫對象設置為原來的值:
private void storyboard_Completed(object sender, RoutedEventArgs e) { Storyboard storyboard = (Storyboard)sender; storyboard.Stop(); double currentWidth = cmdGrow.Width; storyboard.Stop(); cmdGrow.Width = currentWidth; }
RepeatBehavior
RepeatBehavior屬性允許你控制動畫的重復方式。如果你想指定一個重復次數,用數字表明次數,并緊跟著一個x。例如,下面指明這個動畫重復兩次:
<DoubleAnimation Storyboard.TargetName="cmdGrow" RepeatBehavior="2x" Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"></DoubleAnimation>
在后臺代碼中,可以這樣寫:
widthAnimation.RepeatBehavior = new RepeatBehavior(2);
除了可以設置重復次數外,你還可以設置動畫的重復時間,在該段時間內,動畫會一直重復執行。如:
<DoubleAnimation Storyboard.TargetName="cmdGrow" RepeatBehavior="0:0:13" Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"></DoubleAnimation>
對應的后臺代碼:
widthAnimation.RepeatBehavior = new RepeatBehavior(TimeSpan.FromSeconds(13));
除此之外,你還可以將RepeatBehavior的值設置成Forever,讓動畫一直不停的運行下去。
<DoubleAnimation Storyboard.TargetName="cmdGrow" RepeatBehavior="Forever" Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"></DoubleAnimation>
Simultaneous Animations
故事板有能力管理多個動畫。最重要的是,將這些動畫進行分組,就可以同時啟動這些動畫。
這里我們將兩個動畫放在同一個故事板中,一個動畫作用于按鈕的Width屬性,另一個作用于Height屬性。
<Storyboard x:Name="storyboard2" Storyboard.TargetName="cmdGrow"> <DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:5"></DoubleAnimation> <DoubleAnimation Storyboard.TargetProperty="Height" To="300" Duration="0:0:5"></DoubleAnimation> </Storyboard>
有兩個屬于對多個動畫時很有用,一個是BeginTime:控制動畫的開始時間。一個是SpeedRatio:控制動畫的變化速度。
Controlling Playback
Storyboard不單單有begin,end等方法,還有pause(暫停),resume(恢復),Seek(跳轉)等方法。
例如:
Grid設置:
<Grid> <Image Source="night.jpg"></Image> <Image Source="day.jpg" x:Name="imgDay"></Image> </Grid>
故事板設置:
<Storyboard x:Name="fadeStoryboard"> <DoubleAnimation x:Name="fadeAnimation" Storyboard.TargetName="imgDay" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:10"> </DoubleAnimation> </Storyboard>
后臺代碼:
private void cmdStart_Click(object sender, RoutedEventArgs e) { fadeStoryboard.Begin(); } private void cmdPause_Click(object sender, RoutedEventArgs e) { fadeStoryboard.Pause(); } private void cmdResume_Click(object sender, RoutedEventArgs e) { fadeStoryboard.Resume(); } private void cmdStop_Click(object sender, RoutedEventArgs e) { fadeStoryboard.Stop(); } private void cmdMiddle_Click(object sender, RoutedEventArgs e) { // Start the animation, in case it's not currently underway. fadeStoryboard.Begin(); // Move to the time position that represents the middle of the animation. fadeStoryboard.Seek( TimeSpan.FromSeconds(fadeAnimation.Duration.TimeSpan.TotalSeconds/2)); }
private void sldSpeed_ValueChanged(object sender, RoutedEventArgs e)
{
// To nothing if the page is still being initialized.
if (sldSpeed == null) return;
// This also restarts the animation if it's currently underway.
fadeStoryboard.SpeedRatio = sldSpeed.Value;
lblSpeed.Text = sldSpeed.Value.ToString("0.0");
}
效果如圖:
文章列表