2009-10-02 83 views
6

我正在學習WPF動畫,並且對如何順序應用動畫感到困惑。作爲一個簡單的例子,我已經在一個統一的網格中獲得了四個矩形,並且希望順序地改變每個矩形的顏色。這是我到目前爲止:WPF - 順序動畫簡單示例

public partial class Window1 : Window 
{ 
    Rectangle blueRect; 
    Rectangle redRect; 
    Rectangle greenRect; 
    Rectangle yellowRect; 

    public Window1() 
    { 
     InitializeComponent(); 
     blueRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Blue, Name="Blue"}; 
     redRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Red, Name="Yellow"}; 
     greenRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Green, Name="Green" }; 
     yellowRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Yellow, Name="Yellow" }; 

     UniformGrid1.Children.Add(blueRect); 
     UniformGrid1.Children.Add(redRect); 
     UniformGrid1.Children.Add(greenRect); 
     UniformGrid1.Children.Add(yellowRect); 

    } 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     animateCell(blueRect, Colors.Blue); 
     animateCell(redRect, Colors.Red); 
    } 

    private void animateCell(Rectangle rectangle, Color fromColor) 
    { 
     Color toColor = Colors.White; 
     ColorAnimation ani = new ColorAnimation(toColor, new Duration(TimeSpan.FromMilliseconds(300))); 
     ani.AutoReverse = true; 

     SolidColorBrush newBrush = new SolidColorBrush(fromColor); 
     ani.BeginTime = TimeSpan.FromSeconds(2); 
     rectangle.Fill = newBrush; 
     newBrush.BeginAnimation(SolidColorBrush.ColorProperty, ani); 
     //NameScope.GetNameScope(this).RegisterName(rectangle.Name, rectangle); 
     //Storyboard board = new Storyboard(); 
     //board.Children.Add(ani); 
     //Storyboard.SetTargetName(rectangle, rectangle.Name); 
     //Storyboard.SetTargetProperty(ani, new PropertyPath(SolidColorBrush.ColorProperty)); 
     //board.Begin(); 

    } 

完成此操作的最簡單方法是什麼?評論中的代碼是我的第一個猜測,但它不能正常工作。

回答

8

應該有一個事件ani.Completed - 處理該事件並開始動畫的下一個階段,然後開始第一個運行,每個階段將觸發下一個。

ColorAnimation ani = // whatever... 

ani.Completed += (s, e) => 
    { 
     ColorAnimation ani2 = // another one... 

     // and so on 
    }; 

newBrush.BeginAnimation(SolidColorBrush.ColorProperty, ani); 

UPDATE:

public partial class Window1 : Window 
{ 
    Rectangle blueRect; 
    Rectangle redRect; 
    Rectangle greenRect; 
    Rectangle yellowRect; 

    public Window1() 
    { 
     InitializeComponent(); 
     blueRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Blue, Name = "Blue" }; 
     redRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Red, Name = "Yellow" }; 
     greenRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Green, Name = "Green" }; 
     yellowRect = new Rectangle() { Fill = System.Windows.Media.Brushes.Yellow, Name = "Yellow" }; 

     UniformGrid1.Children.Add(blueRect); 
     UniformGrid1.Children.Add(redRect); 
     UniformGrid1.Children.Add(greenRect); 
     UniformGrid1.Children.Add(yellowRect); 
    } 

    IEnumerable<Action<Action>> AnimationSequence() 
    { 
     for (; ;) 
     { 
      yield return AnimateCell(blueRect, Colors.Blue); 
      yield return AnimateCell(redRect, Colors.Red); 
      yield return AnimateCell(greenRect, Colors.Green); 
      yield return AnimateCell(yellowRect, Colors.Yellow); 
     } 
    } 

    private IEnumerator<Action<Action>> _actions; 

    private void RunNextAction() 
    { 
     if (_actions.MoveNext()) 
      _actions.Current(RunNextAction); 
    } 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     _actions = AnimationSequence().GetEnumerator(); 
     RunNextAction(); 
    } 

    private Action<Action> AnimateCell(Rectangle rectangle, Color fromColor) 
    { 
     return completed => 
     { 
      Color toColor = Colors.White; 
      ColorAnimation ani = new ColorAnimation(toColor, 
            new Duration(TimeSpan.FromMilliseconds(300))); 
      ani.AutoReverse = true; 
      ani.Completed += (s, e) => completed(); 

      SolidColorBrush newBrush = new SolidColorBrush(fromColor); 
      ani.BeginTime = TimeSpan.FromSeconds(2); 
      rectangle.Fill = newBrush; 
      newBrush.BeginAnimation(SolidColorBrush.ColorProperty, ani); 
     }; 
    } 
} 

嘗試粘貼到上述程序。它可以滿足您的需求,但在某些情況下可能對您有用。它仍然是事件驅動的,但它使用「迭代器方法」(帶有回報)來創建這樣的印象,即它是在動畫發生時阻塞的順序編碼。

關於這一點的好處是,您可以以非常直觀的方式使用AnimationSequence方法 - 您可以在一系列語句中寫出動畫的時間軸,或者使用循環或任何您想要的。

+0

如果動畫是動態的呢?我希望能夠稱爲藍色,綠色,紅色,藍色,綠色,紅色;每個動畫可能在之前的2秒後。有沒有辦法讓animateCell的調用者阻止,直到ani.Completed被解僱? – 2009-10-02 21:49:28

3

我試過的解決方案是像這樣使用隊列。這可以讓你動態地添加到動畫鏈中。我不確定鎖是否有必要,但我只是爲了安全起見而將它留下。

Queue<Object[]> animationQueue = new Queue<Object[]>(); 

void sequentialAnimation(DoubleAnimation da, Animatable a, DependencyProperty dp) 
{ 
    da.Completed += new EventHandler(da_Completed); 

    lock (animationQueue) 
    { 
     if (animationQueue.Count == 0) // no animation pending 
     { 
      animationQueue.Enqueue(new Object[] { da, a, dp }); 
      a.BeginAnimation(dp, da); 
     } 
     else 
     { 
      animationQueue.Enqueue(new Object[] { da, a, dp }); 
     } 
    } 
} 

void da_Completed(object sender, EventArgs e) 
{ 
    lock (animationQueue) 
    { 
     Object[] completed = animationQueue.Dequeue(); 
     if (animationQueue.Count > 0) 
     { 
      Object[] next = animationQueue.Peek(); 
      DoubleAnimation da = (DoubleAnimation)next[0]; 
      Animatable a = (Animatable)next[1]; 
      DependencyProperty dp = (DependencyProperty)next[2]; 

      a.BeginAnimation(dp, da); 
     } 
    } 
} 
+0

更多的可讀性和隊列匹配序列的想象力。在一個動作剛開始動畫時實現一個動作隊列。 – 2014-10-01 09:11:27

3

這可以通過使用一類具有矛盾名ParallelTimeline並仔細調整BeginTime屬性來完成。請注意下面的示例中第二個DoubleAnimationBeginTime屬性如何設置爲第一個的持續時間。

<ParallelTimeline> 
     <DoubleAnimation 
      Storyboard.TargetName="FlashRectangle" 
      Storyboard.TargetProperty="Opacity" 
      From="0.0" To="1.0" Duration="0:0:1"/> 
     <DoubleAnimation BeginTime="0:0:0.05" 
      Storyboard.TargetName="FlashRectangle" 
      Storyboard.TargetProperty="Opacity" 
      From="1.0" To="0.0" Duration="0:0:2"/> 
</ParallelTimeline>