【WPF學習】第四十九章 基本動畫


  在前一章已經學習過WPF動畫的第一條規則——每個動畫依賴於一個依賴項屬性。然而,還有另一個限制。為了實現屬性的動態化(換句話說,使用基於時間的方式改變屬性的值),需要有支持相應數據類型的動畫類。例如,Button.Width屬性使用雙精度數據類型。為實現屬性的動態化,需要使用DoubleAnimation類。但Button.Paddin屬性使用的是Thickness結構,所以需要使用ThicknessAnimation類。

  該要求不像WPF動畫的第一條規則那么絕對,第一條規則將動畫局限於依賴項屬性。這是因為對於沒有相應動畫類的依賴項屬性,為了為該屬性應用動畫,可以針對相應的數據類型創建自己的動畫類。System.Windows.Media.Animation名稱空間已經為希望使用的大多數數據類型提供了動畫類。

  因為許多數據類型實際上不使用動畫,所以沒有相應的動畫類。一個明顯的例子是枚舉類型。例如,可使用HorizontalAlignment屬性控制如何在布局面板中放置元素,該屬性使用的是HorizontalAlignment枚舉值。然而,HorizontalAlignment枚舉只允許從4個值中選擇一個(Left、Right、Center和Stretch),這極大地限制了它在動畫中的使用。盡管可在某個方向或其他方向之間進行交換,但不能將元素從一種對齊方式平滑過渡到另一中對齊方式。所以,沒有為HorizontalAlignment數據類型提供動畫類。可以自己為HorizontalAlignment數據類型構建動畫類,但仍要受到4個枚舉數值的限制。

  引用類型通常不能應用動畫,但它們的子屬性可以。例如,所有內容控件都支持Background屬性,從而可以設置Brush對象用來繪制背景。使用動畫從一個畫刷切換到另一個畫刷的效率通常不高,但可以使用動畫改變畫刷的屬性。例如,可改變SolidColorBrush畫刷的Color屬性(使用ColorAnimation類),或改變LinearGradientBrush畫刷中GradientStop對象的Offset屬性(使用DoubleAnimation類)。這擴展了WPF動畫的應用范圍,允許用戶為元素外觀的特定方面應用動畫。

一、Animation類

   根據目前為止提到的動畫類型——DoubleAnimation和ColorAnimation——可能會任務所有的動畫類都是以“類型名+Animation”方式命名。這種觀點很接近實際情況,但不是非常准確。

  實際上有兩種類型的動畫——在開始值和結束值之間以逐步增加的方式(被稱為線性插值過程)改變屬性的動畫,以及從一個值突然變成另一個值得動畫。DoubleAnimation和ColorAnimation屬於第一種動畫類型,他們使用插值平滑地改變值。然而,當改變特定的數據時,如String和引用類型的對象,插值就沒有意義的。不是使用插值,這些數據類型使用一種稱為“關鍵幀動畫”的技術在特定時刻從一個值突然改變到另一個值。所有關鍵幀動畫類都使用“類型名+AnimationUsingKeyFrames”的形式進行命名,比如StringAnimationUsingKeyFrames和ObjectAnimationUsingKeyFrames。

  某些數據類型有關鍵幀動畫類,但沒有插值動畫類。例如,可使用關鍵幀為字符串應用動畫,但不能使用插值為字符串應用動畫。然而,所有數據類型都支持關鍵幀動畫,除非它們根本不支持動畫。換句話說,所有具有(使用插值的)常規動畫類(例如DoubleAnimation和ColorAnimation)的數據類型,也都有相應的用於關鍵幀動畫的動畫類型(如DoubleAnimationUsingKeyFrames和ColorAnimationUsingKeyFrames)。

  實際上,還有一種動畫類型。這種類型稱為基於路徑的動畫,而且它們比使用插值或關鍵幀的動畫更加專業。基於路徑的動畫修改數值使其符合由PathGeometry對象描述的形狀,並且主要用於艷路徑移動元素。基於路徑的動畫類使用“類型名+AnimationUsingPath”的形式進行命名,如DoubleAnimationUsingPath和PointAnimationUsingPath。

  總之,在System.Windows.Media.Animation名稱空間中間發現以下內容:

  •   17個“類型名+Animation”類,這些類使用插值。
  •   22個“類型名+AnimationUsingKeyFrames”類,這些類使用關鍵幀動畫
  •   3個“類型名+AnimationUsingPath”類,這些類使用基於路徑的動畫

  所有這些動畫類都繼承自抽象的“類型名+AnimationBase”類,這些基類實現了一些基本功能,從而為創建自定義動畫類提供了快捷方式。如果某個數據類型支持多種類型的動畫,那么所有的動畫類都繼承自抽象的動畫基類。例如,DoubleAnimation和DoubleAnimationUsingKeyFrames都繼承自DoubleAnimationBase基類。

  可通過查看這42個類快速決定哪些數據類型為動畫提供了本地支持。下面是這42個類的完整列表:

BooleanAnimationUsingKeyFrames ByteAnimation
ByteAnimationUsingKeyFrames CharAnimationUsingKeyFrames
ColorAnimation ColorAnimationUsingKeyFrames
DecimalAnimation DecimalAnimationUsingKeyFrames
DoubleAnimation DoubleAnimationUsingKeyFrames
DoubleAnimationUsingPath Int16Animation
Int16AnimationUsingKeyFrames Int32Animation
Int32AnimationUsingKeyFrames Int64Animation
Int64AnimationUsingKeyFrames MatrixAnimationUsingKeyFrames
MatrixAnimationUsingPath ObjectAnimationUsingKeyFrames
PointAnimation PointAnimationUsingKeyFrames
PointAnimationUsingPath Point3DAnimation
Point3DAnimationUsingKeyFrames QuarternionAnimation
QuarternionAnimationUsingKeyFrames RectAnimation
RectAnimationUsingKeyFrames Rotation3DAnimation
Rotation3DAnimationUsingKeyFrames SingleAnimation
SingleAnimationUsingKeyFrames SizeAnimation
SizeAnimationUsingKeyFrames StringAnimationUsingKeyFrames
ThicknessAnimation ThicknessAnimationUsingKeyFrames
VectorAnimation VectorAnimationUsingKeyFrames
Vector3DAnimation Vector3DAnimationUsingKeyFrames

  其中許多類型的含義不言自明。例如,一旦掌握DoubleAnimation類,就不在需要再分析SingleAnimation、Int16Animation、Int32Animation以及其他所有用於簡單數值類型的動畫類,它們都以相同的方式工作。除這些用於數值類型的動畫類外,還會發現一些使用其他基本數據類型(如byte、bool、string以及char)的動畫類,以及更多的用於處理二維和三維Drawing圖元(Point、Size、Rect和Vector等)的動畫類,用於所有元素的Margin和Padding屬性的動畫類(ThicknessAnimation)、用於顏色的動畫類(ColorAnimation)以及用於任意引用類型對象的動畫類(ObjectAnimationUsingKeyFrames)。

二、使用代碼創建動畫

  最常用的動畫技術是線性插值動畫,這種技術平滑地從起點到終點修改屬性值。例如,如果將開始數值設置為1,並且將結束數值設置為10,屬性可能從1快速地變為1.1、1.2、1.3等,知道數值達到10.

  WPF使用它所需的步長以確保在當前配置的幀率下得到平滑的動畫。標准的幀率是60幀/秒。換句話說,WPF每隔1/60秒就會計算所有應用了動畫的數值,並更新相應的屬性。

  使用動畫的最簡單方法是實例化在前面列出的其中一個動畫類,配置該實例,然后使用希望修改的元素的BeginAnimation()方法。所有WPF元素,從UIElement基類開始,都繼承了BeginAnimation()方法,該方法是IAnimatable接口的一部分。其他實現了IAnimatable接口的類包括ContentElement(文檔流內容的基類)和Visual3D(3D可視化對象的基類)。

  下圖顯示了一個非簡單的、增加了按鈕寬度的動畫。當單擊按鈕時,WPF平滑地擴展按鈕的兩個側邊直到充滿窗口。

   為創建這種效果,使用動畫修改按鈕的Width屬性。當單擊按鈕時,下面的代碼創建並啟用這個動畫:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = 121;
widthAnimation.To = this.Width - 30;
widthAnimation.Duration = TimeSpan.FromSeconds(5);
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  任何使用線性插值的動畫最少需要三個細節:開始值(From)、結束值(To)和整個動畫執行的時間(Duration)。在這個示例中,結束值基於包含按鈕的窗口的當前寬度。使用插值的所有動畫類都提供了這三個屬性。

  From、To和Duration屬性看似簡單,但應注意他們的幾個重要細節。接下來將更深入地分析這些屬性。

  1.From屬性

  From值是Width屬性的開始值。如果多次單擊按鈕,每次單擊時,都會將Width屬性重新設置為121,並且重新開始運行動畫。即使當動畫已在運行時單擊按鈕也同樣如此。

  在許多情況下,可能不希望動畫從最初的From值開始。有如下兩個常見的原因:

  •   創建能夠被觸發多次,並逐次累加效果的動畫。例如,可能希望創建每次單擊時都增大一點的按鈕。
  •   創建可能相互重疊的動畫。例如,可使用MouseEnter事件觸發擴展按鈕的動畫,並使用MouseLeave事件觸發將按鈕縮小為原尺寸的互補動畫(這通常稱為“魚眼”效果)。如果連續快速地講鼠標多次移動到這種按鈕上並移開,每個新動畫就會打算上一個動畫,導致按鈕“跳”回到由From屬性設置的尺寸。

  當前示例屬於第二種情況。如果當按鈕正在增大時單擊按鈕,按鈕的寬度就會被重新設置為121像素——這可能會出現抖動效果。為了糾正這個效果,只需要忽略設置From屬性的代碼語句即可:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.To = this.Width - 30;
widthAnimation.Duration = TimeSpan.FromSeconds(5);
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  現在有一個問題。為使用這種技術,應用動畫的屬性必須有預先設置的值。在這個示例中,這意味着按鈕必須有硬編碼的寬度(不管是在按鈕標簽中直接定義的,還是通過樣式設置器應用的)。問題是在許多布局容器中,通常不指定寬度並且讓容器根據元素的對齊屬性控制寬度。對於這種情況,元素使用默認寬度,也就是特殊的Double.NaN值(這里的NaN代表"不是數字(not a number)")。不能為具有這種值得屬性使用線性插值應用動畫。

  那么,解決方法是什么呢?在許多情況下,答案是硬編碼按鈕的寬度。正如看你到的,動畫經常更精確地控制元素的尺寸和位置。實際上,對於能應用動畫的內容,最常用的布局容器是Canvas面板,因為Canvas 面板允許更方便地移動內容(可能相互重疊)以及改變內容的尺寸。Canvas面板還是量級最輕的布局容器,因為當諸如Width的屬性發生變化時不需要額外的布局工作。

  在當前示例中,還有一種選擇。可使用ActualWidth屬性檢索按鈕的當前值,該屬性給出的是按鈕當前渲染的寬度。不能為ActualWidth屬性應用動畫(該屬性是只讀的)。但可以用該屬性設置動畫的From屬性:

widthAnimation.From = btnGrow.ActualWidth;

  這種技術既可用於基於代碼的動畫(如當前示例),也可用於將后面介紹的聲明式動畫(這時需要使用綁定表達式來獲得ActualWidth屬性的值)。

  需要弄清的另一個問題是,當使用當前值作為動畫的起點時——可能改變動畫的運行速度。這時因為未調整動畫的持續時間,是動畫能夠考慮到在初始化和最終值之間的寬度變小了。例如,假設創建的按鈕不是使用From值而是從當前位置開始動畫。如果當幾乎達到最大寬度值時單擊按鈕,新的動畫就開始了。盡管只有幾個像素的空間可供使用,但這個動畫仍唄配置為持續5秒(通過Duration屬性)。所以,按鈕的增速看起來變慢了。

  只有當重新啟動解決完成的動畫時才會出現這種效果。盡管有些奇怪,但是大多數開發人員不會嘗試為解決該問題而編寫許多代碼。相反,這被認為具有可以接受的問題。

  2.To屬性

  就像可省略From屬性一樣,也可省略To屬性。實際上,可同時省略From屬性和To屬性,像下面這樣創建動畫:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.Duration = TimeSpan.FromSeconds(5);
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  乍一看,這個動畫好像根本沒有執行任何操作。這樣想是符合邏輯的,因為To屬性和From屬性都被忽略了,他們將使用相同的值。但他們之間存在一點微妙且重要的區別。

  當省略From屬性時,動畫使用當前值,並將動畫納入考慮范圍。例如,如果按鈕位於某個增長操作的中間,From值會使用擴展后的寬度。然而,當忽略To值時,動畫使用不考慮動畫的當前值。本質上,這意味着To值變為原數值——最后一次在代碼中、元素標簽中或通過樣式設置的值.

  在按鈕示例中,這意味着如果開始了一個增長動畫,然后使用上面的動畫打斷該動畫,按鈕將會從已經增長了之后的尺寸進行縮小,直到達到在XAML標記中設置的原始寬度。另一方面,如果在沒有其它動畫正在進行的情況下進行這段代碼,不會發生任何事情,這是因為From值(動畫后的寬度)和To(原始寬度)相等。

  3.By屬性

  即使不使用To屬性,也可以使用By屬性,By屬性用於創建按鈕設置的數值改變值得動畫而不是按給定目標改變值。例如,可創建一個動畫,增大按鈕的尺寸,使得比當前尺寸大10個單位,如下所示:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.By = 10;
widthAnimation.Duration = TimeSpan.FromSeconds(5);
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  在按鈕示例中,這種方法不是必需的,因為可使用簡單的計算設置To屬性來實現相同的的效果,如下所示:

widthAnimation.To = btnGrow.Width + 10;

  然而當使用XAML定義動畫時,使用By值就變得更加合理了,因為XAML沒有提供執行簡單計算的方法。

  大部分使用插值的動畫類通常都提供了By屬性,但並非全部如此。例如,對於非數值數據類型來說,By屬性是沒有意義的,比如ColorAnimation類使用的Color結構。

  另有一種方法可得到類似的行為,而不需要使用By屬性——可通過設置IsAdditive屬性創建增加數值的動畫。當創建這種動畫時,當前值被自動添加到From值和To值。例如,分析下面這個動畫:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = 0;
widthAnimation.To = -10;
widthAnimation.Duration = TimeSpan.FromSeconds(0.5);
widthAnimation.IsAdditive = true;

  這個動畫是從當前值開始的,當達到比當前值少10個單位的值時完成。另一方面,如果使用下面的動畫:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = 10;
widthAnimation.To = 50;
widthAnimation.Duration = TimeSpan.FromSeconds(0.5);
widthAnimation.IsAdditive = true;

  屬性值跳到新值(比當前值大10個單位的值),然后增加值,直到達到最后的值,最后的值比動畫開始前得的當前值大50個單位。

  4.Duration屬性

  Duration屬性很簡單——是在動畫開始時刻和結束時刻之間的時間間隔(時間間隔單位是毫秒、分鍾、小時或喜歡使用的其他任何單位)。盡管在上一個示例中,動畫的持續時間是使用TimeSpan對象設置的,但Duration結構定義了一種隱式轉換,能偶根據需要將System.TimeSpan轉換為System.Windows.Duration。這正是為什么下面的代碼完全合理的原因:

widthAnimation.Duration = TimeSpan.FromSeconds(5);

  那么,為什么使用全新的數據類型呢?因為Duration類型還提供了兩個不能通過TimeSpan對象表示的特殊值——Duration.Automatic和Duration.Forever。在當前示例中,這兩個值都沒有用處(Automatic值只將動畫設置為1秒得持續時間,而Forever值使動畫具有無限的持續時間,這會防止動畫具有任何效果)。然而,當創建更復雜的動畫時,這些值就有用處了。

三、同時發生的動畫

  可使用BeginAnimation()方法同時啟動多個動畫。BeginAnimation()方法幾乎總是立即返回,從而可以使用類似下面的代碼同時為兩個屬性應用動畫:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.From = 219;
widthAnimation.To = this.Width - 30;
widthAnimation.Duration = TimeSpan.FromSeconds(5);

DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 99;
heightAnimation.To = this.Height - 50;
heightAnimation.Duration = TimeSpan.FromSeconds(5);

btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);
btnGrow.BeginAnimation(Button.HeightProperty, heightAnimation);

  在這個示例中,兩個動畫沒有被同步,這意味着寬度和高速不會准確地再相同時間間隔內增長(通常,將看到按鈕先增加寬度,緊接着增大高速)。可通過創建綁定到同一個時間縣的動畫,突破這一限制。

四、動畫的生命周期

  從技術角度看,WPF動畫是暫時的,這意味着它們不能真正改變基本屬性的值。當動畫處於活動狀態時,只是覆蓋屬性值。這是由依賴項屬性的工作方式造成的,並且這是一個經常被忽視的細節,該細節會給用戶帶來極大的困惑。

  單向動畫(如增長按鈕的動畫)在運行結束后保持處於活動狀態,這是因為動畫需要將按鈕的寬度保持為新值。這會導致如下不常見的問題——如果嘗試使用代碼在動畫完成后修改屬性值,代碼將不起作用。因為代碼只是為屬性指定了一個新的本地值,但仍會優先使用動畫之后的屬性值。

  根據准備完成的工作,可通過如下幾種方法解決這個問題:

  •   創建將元素重新設置為原始狀態的動畫。可通過創建不設置To屬性的動畫達到該目的。例如,將按鈕的寬度減少到最后設置的尺寸的按鈕縮小動畫,之后就可能使用代碼改變該屬性了。
  •   創建可翻轉的動畫。通過將AutoReverse屬性設置為true來創建可翻轉的動畫。例如,當按鈕增長動畫不在增加按鈕的寬度時,將反向播放動畫,返回到原始寬度。動畫的總持續事件也將翻倍。
  •   改變FillBehavior屬性。通常,FillBehavior屬性被設置為HoldEnd,這意味着當動畫結束時,會繼續為目標元素應用最后的值。如果將FillBehavior屬性改為Stop,只要動畫結束,屬性就會恢復為原來的值。
  •   當動畫完成時通過處理動畫對象的Completed事件刪除動畫對象。

  前三種方法改變了動畫的行為。不管使用哪種方法,他們都將動畫后的屬性設置為原來的數值。如果這並非所希望的,那就需要使用最后一種方法。

  首先,在啟動動畫錢,關聯事件處理程序以響應動畫完成事件:

widthAnimation.Completed += widthAnimation_Completed;

  當引發Completed事件時,可通過調用BeginAnimation()方法來渲染不活動的動畫。為此,只需要指定屬性,並為動畫對象傳遞null引用:

btnGrow.BeginAnimation(Button.WidthProperty, null);

  當調用BeginAnimation()方法時,屬性返回為動畫開始之前的原始只。如果這並非所希望的結果,可記下動畫應用的當前值,刪除動畫,然后手動為屬性設置新值,如下所示:

double currentWidth = this.btnGrow.Width;
btnGrow.BeginAnimation(Button.WidthProperty, null);
btnGrow.Width = currentWidth;

  需要注意的是,現在改變了屬性的本地值。這可能影響其他動畫的運行。例如,如果為按鈕使用未指定From屬性的動畫,該動畫就會使用這個新應用的屬性值作為起點。大多數情況下,這正是所希望的行為。

五、Timeline類

  每個動畫需要使用幾個重要屬性,我們已經分析了其中幾個屬性:From和To屬性(使用插值的動畫類提供了這兩個屬性),以及Duration和FillBehavior屬性(所有動畫類都提供了這兩個屬性)。在繼續學習之前,有必要深入分析必須使用的屬性。

  下圖顯示了WPF動畫類的繼承層次結構。該圖包含了所有基類,但省略了全部42個動畫類以及相應的TypeNameAnimationBase類:

  圖 動畫類的繼承層次結構

  上圖顯示的層次結構包含了繼承自Timeline抽象類的三個主要分支。當播放音頻或視頻文件時使用MediaTimeline類。AnimationTimeline分支用於到目前為止分析過的基於屬性的動畫系統。而TimelineGroup分支則允許同步時間線並控制它們的播放。

  Timeline類中前幾個有用的成員定義了已經介紹過的Duration屬性,還有其他幾個屬性。下表列出了Timeline類的屬性:

表 Timeline類的屬性

   盡管BeginTime、Duration、SpeedRatio以及AutoReverse屬性都很簡單,但其他一些屬性需要進一步加以分析。接下來將深入分析AccelerationRatio、DecelerationRatio以及RepeatBehavior屬性。

  1.AccelerationRatio和DecelerationRatio屬性

  可以通過AccelerationRatio和DecelerationRatio屬性壓縮部分時間線,使動畫運行得更快。並將拉伸其他時間線進行補償,使總時間保持不變。

  這兩個屬性都表示百分百值。例如,將AccelerationRatio屬性設置為0.3表示希望使用動畫持續時間中前30%的時間進行加速。例如,在一個持續10秒得動畫中,前3秒會加速運行,而剩余的7秒會以恆定不變的速度運行(顯然,在最后7秒鍾得速度比沒有加速的動畫快,因為需要補償前3秒中的緩慢啟動)。如果將AccelerationRatio屬性設置為0.3,並將DecelerationRatio屬性也設置為0.3,那么前3秒會加速,在中間4秒保持固定的最大速度,在最后3秒減速。分析一下這種方式,顯然,AccelerationRatio和DecelerationRatio屬性值之和不能超過1,否則就需要超過100%的可用時間來執行所需的加速和減速。當然,可將AccelerationRatio屬性設置為1(對於這種情況,動畫速度從開始到結束一直在增加),或將DecelerationRatio屬性設置為1(對於這種情況,動畫速度從開始到結束一直在降低)。

  加速和減速的動畫常用於提供更趨自然的外觀。然而,AccelerationRatio和DecelerationRatio屬性只提供了相對簡單的控制。例如,它們不能改變加速速度或者將其設置為指定的值。如果希望得到使用可變加速度的動畫,需要定義一系列動畫,逐個進行播放,並且為每個動畫設置AccelerationRatio和DecelerationRatio屬性,或者需要使用具有關鍵樣條曲線幀動畫。盡管這種技術提供了很大的靈活性,但一直跟蹤所有細節是一件令人頭疼的事情,並且對於構建動畫來說,完美的情況是使用設計工具。

  2.RepeatBehavior屬性

  使用RepeatBehavior屬性可控制如何重復運行動畫。如果希望重復固定次數,應為RepeatBehavior構造函數傳遞合適的次數。例如,下面的動畫重復次數:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.To = this.Width - 30;
widthAnimation.Duration = TimeSpan.FromSeconds(5);
widthAnimation.RepeatBehavior = new RepeatBehavior(2);
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  當運行這個動畫時,按鈕會增大尺寸(經過5秒),調回到原來的數值,然后再次增加尺寸(經過5秒),在按鈕的寬度為整個窗口的寬度時結束。如果將AutoReverse屬性設置為true,行為稍有不同——整個動畫完成向前和向后運行(意味着先展開按鈕,然后收縮),之后再重復一次。

  除可以使用RepeatBehavior屬性設置重復次數外,還可以用該屬性設置重復的時間間隔。為此,只需要為RepeatBehavior對象的構造函數傳遞一個TimeSpan對象。例如,下面的動畫重復13秒:

DoubleAnimation widthAnimation = new DoubleAnimation();
widthAnimation.To = this.Width - 30;
widthAnimation.Duration = TimeSpan.FromSeconds(5);
widthAnimation.RepeatBehavior = new RepeatBehavior(TimeSpan.FromSeconds(13));
btnGrow.BeginAnimation(Button.WidthProperty, widthAnimation);

  在該例中,Duration屬性指定整個動畫歷經5秒。因此,將RepeatBehavior屬性設置為13秒將會引起兩次重復,然后通過第三次重復動畫,使按鈕的寬度處於中間位置(在3秒得位置)。

  最后,也可使用RepeatBehavior.Forever值使動畫不斷地重復自身:

widthAnimation.RepeatBehavior = RepeatBehavior.Forever;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM