WPF-顺序动画简单示例
我正在学习WPF动画,对如何按顺序应用动画感到困惑。作为一个简单的例子,我在一个统一的网格中有四个矩形,我想按顺序更改每个矩形的颜色。以下是我目前掌握的情况:WPF-顺序动画简单示例,wpf,animation,storyboard,Wpf,Animation,Storyboard,我正在学习WPF动画,对如何按顺序应用动画感到困惑。作为一个简单的例子,我在一个统一的网格中有四个矩形,我想按顺序更改每个矩形的颜色。以下是我目前掌握的情况: public partial class Window1 : Window { Rectangle blueRect; Rectangle redRect; Rectangle greenRect; Rectangle yellowRect; public Window1() {
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();
}
最简单的方法是什么?注释中的代码是我的第一个猜测,但它工作不正常。应该有一个事件
ani。已完成
-处理该事件并开始动画的下一个阶段,然后开始运行第一个阶段,每个阶段将触发下一个阶段
ColorAnimation ani = // whatever...
ani.Completed += (s, e) =>
{
ColorAnimation ani2 = // another one...
// and so on
};
newBrush.BeginAnimation(SolidColorBrush.ColorProperty, ani);
更新:
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);
};
}
}
公共部分类窗口1:窗口
{
矩形blueRect;
矩形redRect;
矩形greenRect;
矩形黄色矩形;
公共窗口1()
{
初始化组件();
blueRect=new Rectangle(){Fill=System.Windows.Media.brush.Blue,Name=“Blue”};
redRect=new Rectangle(){Fill=System.Windows.Media.brushs.Red,Name=“Yellow”};
greenRect=new Rectangle(){Fill=System.Windows.Media.brush.Green,Name=“Green”};
yellowRect=新矩形(){Fill=System.Windows.Media.brush.Yellow,Name=“Yellow”};
UniformGrid1.Children.Add(blueRect);
UniformGrid1.Children.Add(redRect);
UniformGrid1.Children.Add(greenRect);
UniformGrid1.Children.Add(yellowRect);
}
IEnumerable AnimationSequence()
{
对于(;;)
{
返回AnimateCell(blueRect,Colors.Blue);
返回AnimateCell(redRect,Colors.Red);
收益返回AnimateCell(greenRect,Colors.Green);
收益返回AnimateCell(yellowRect,Colors.Yellow);
}
}
私人IEnumerator_行动;
私有void RunNextAction()
{
如果(_actions.MoveNext())
_动作。当前(RunNextAction);
}
已加载私有无效窗口(对象发送器、路由目标)
{
_actions=AnimationSequence().GetEnumerator();
RunNextAction();
}
私有操作AnimateCell(矩形,颜色来自颜色)
{
返回已完成=>
{
颜色toColor=颜色。白色;
ColorAnimation ani=新的ColorAnimation(toColor,
新的持续时间(时间跨度从毫秒(300));
ani.AutoReverse=真;
ani.Completed+=(s,e)=>Completed();
SolidColorBrush newBrush=新的SolidColorBrush(fromColor);
ani.BeginTime=从秒开始的时间跨度(2);
矩形。填充=新刷;
newBrush.BeginAnimation(SolidColorBrush.ColorProperty,ani);
};
}
}
尝试将上述内容粘贴到您的程序中。它可以满足你的需要,但在其他情况下可能对你有用。它仍然是事件驱动的,但它使用“迭代器方法”(带有yield-return)来创建这样一种印象,即在动画进行时,它是顺序编码阻塞的
这一点的好处是,您可以非常直观地使用AnimationSequence方法—您可以在一系列语句中写出动画的时间线,或者使用循环,或者任何您想要的方法。我尝试的解决方案是使用这样的队列。这将允许您动态添加到动画链。我不确定这把锁是否有必要,但我把它留在里面只是为了安全
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);
}
}
}
Queue animationQueue=new Queue();
void sequentialAnimation(双动画da、可设置动画a、从属属性dp)
{
da.Completed+=新事件处理程序(da_Completed);
锁定(动画队列)
{
if(animationQueue.Count==0)//没有挂起的动画
{
Enqueue(新对象[]{da,a,dp});
a、 初生(dp,da);
}
其他的
{
Enqueue(新对象[]{da,a,dp});
}
}
}
无效数据已完成(对象发送方,事件参数e)
{
锁定(动画队列)
{
Object[]completed=animationQueue.Dequeue();
如果(animationQueue.Count>0)
{
Object[]next=animationQueue.Peek();
DoubleAnimation da=(DoubleAnimation)next[0];
可设置动画a=(可设置动画)下一步[1];
DependencyProperty dp=(DependencyProperty)next[2];
a、 初生(dp,da);
}
}
}
这可以通过使用名称相互矛盾的类ParallelTimeline
并仔细调整BeginTime
属性来实现。请注意,在下面的示例中,如何将第二个DoubleAnimation
的BeginTime
属性设置为第一个的持续时间
<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>
如果动画是动态的怎么办?我想打电话说蓝,绿,红,蓝,绿,红;每个动画可能比上一个动画晚2秒。有没有办法让animateCell的调用者阻塞直到触发ani.Completed?可读性更高,队列与序列的想象相匹配。实现了一个动作队列,其中一个动作刚刚开始一个动画。