WPF - sequential animation simple example

后端 未结 3 1378
逝去的感伤
逝去的感伤 2020-12-17 02:28

I\'m learning about WPF animation, and am confused about how to apply animations sequentially. As a simple example, I\'ve got four rectangles in a uniform grid, and would l

相关标签:
3条回答
  • 2020-12-17 02:50

    There should be an event ani.Completed - handle that event and start the next phase of the animation, then start the first one running and each phase will trigger the next.

    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);
            };
        }
    }
    

    Try pasting the above into your program. It does what you need, but in a way that may be useful to you in other contexts. It's still event driven, but it uses an "iterator method" (with yield return) to create the impression that it is sequential coding that blocks while the animation is going on.

    The nice thing about this is that you can play around with the AnimationSequence method in a very intuitive way - you could write out the timeline of the animation in a series of statements, or use loops, or whatever you want.

    0 讨论(0)
  • 2020-12-17 03:01

    The solution I've tried is to use a Queue like so. This will let you add to the animation chain dynamically. I'm not sure if the lock is necessary, but I left it in just to be safe.

    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 讨论(0)
  • 2020-12-17 03:06

    This can be accomplished by using a class with the contradictory name ParallelTimeline and carefully adjusting the BeginTime property. Note in the example below how the BeginTime property of the second DoubleAnimation is set to the duration of the first.

    <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>
    
    0 讨论(0)
提交回复
热议问题