使用 WPF 中的 Storyboard 进行动画






4.90/5 (56投票s)
在 WPF 中使用故事板的动画。
引言
WPF 中的动画变得更容易,因为 WPF 通过修改元素的属性来实现动画。而在 Windows Forms 中,开发人员必须创建一个计时器,并在计时器的 tick 事件中修改元素的外观。WPF 使用自己的计时系统,该系统可以使用托管代码和 XAML 编写。WPF 有效地处理了屏幕重绘的内部工作。在使用 WPF 制作动画时,您只需专注于想要创建的效果,而无需担心如何实现这些效果。
为了演示这一点,我创建了一个挥舞的旗帜动画,该动画使用一系列按顺序显示的图像。
背景
DoubleAnimation
WPF 通过动画元素属性来实现动画。例如,如果您想为矩形产生放大或缩小效果,您可以动画其宽度和高度属性。以下代码通过修改矩形的宽度和高度属性来动画矩形。
<Rectangle Name="myrect" Width="1" Height="1">
<Rectangle.Fill>
<SolidColorBrush Color="Red"/>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Width" From="1" To="350"
Duration="0:0:1" BeginTime="0:0:0"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Height" From="1" To="250"
Duration="0:0:1" BeginTime="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Height" From="250"
To="1" Duration="0:0:1" BeginTime="0:0:2"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Width" From="350" To="1"
Duration="0:0:1" BeginTime="0:0:3"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
此代码在窗口加载时自动触发动画。 该代码将一个 EventTrigger
添加到矩形。 BeginStoryboard
动作运行一个故事板。 这个故事板使用四个 DoubleAnimation
。 第一个 DoubleAnimation
将矩形的宽度从 1 增加到 350。 第二个将高度从 1 增加到 250。 第三个和第四个则相反,将高度和宽度减小回 1。 通过设置 BeginTime
属性,使四个 DoubleAnimation
顺序运行,以便每个动画在前一个动画结束后开始。 Storyboard 的 RepeatBehavior
属性被赋值为 "Forever
",这使得动画无限期运行。
以下是上述代码的输出:
从代码背后控制动画
可以通过创建一个作为窗口资源的故事板,并在代码中使用 TryFindResource
方法找到它,从而从代码背后控制动画,如下所示
Storyboard s = (Storyboard)TryFindResource("sb");
以下是创建 Storyboard 资源的代码
<Window.Resources>
<Storyboard x:Key="sb" RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Width" From="1" To="350"
Duration="0:0:1" BeginTime="0:0:0"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Height" From="1" To="250"
Duration="0:0:1" BeginTime="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Height" From="250" To="1"
Duration="0:0:1" BeginTime="0:0:2"/>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Width" From="350" To="1"
Duration="0:0:1" BeginTime="0:0:3"/>
</Storyboard>
</Window.Resources>
以下是用于以编程方式控制动画的代码
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
// Locate Storyboard resource
Storyboard s = (Storyboard)TryFindResource("sb");
s.Begin(); // Start animation
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
Storyboard s = (Storyboard)TryFindResource("sb");
s.Stop(); // Stop animation
}
private void btnPause_Click(object sender, RoutedEventArgs e)
{
Storyboard s = (Storyboard)TryFindResource("sb");
s.Pause(); // Pause animation
}
private void btnResume_Click(object sender, RoutedEventArgs e)
{
Storyboard s = (Storyboard)TryFindResource("sb");
s.Resume(); // Resume animation
}
}
以下是上述代码的输出:
使用 DoubleAnimation 的淡入和淡出动画
可以使用 Opacity
属性创建淡入和淡出动画效果,如下所示
<Rectangle Name="myrect" Width="350" Height="250">
<Rectangle.Fill>
<SolidColorBrush x:Name="brush" Color="Red"/>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="myrect"
Storyboard.TargetProperty="Opacity" From="0" To="1"
Duration="0:0:1" BeginTime="0:0:0" AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
上面的代码将矩形的不透明度从 0(完全透明)更改为 1(完全不透明)。
以上代码的输出如下
ColorAnimation
我们可以使用 ColorAnimation
来动画矩形的 Color
属性。 以下是产生颜色动画的代码
<Rectangle Name="myrect" Width="350" Height="250">
<Rectangle.Fill>
<SolidColorBrush x:Name="brush" Color="Red"/>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation Storyboard.TargetName="brush"
Storyboard.TargetProperty="Color" From="Red" To="Green"
Duration="0:0:1" BeginTime="0:0:0"/>
<ColorAnimation Storyboard.TargetName="brush"
Storyboard.TargetProperty="Color" From="Green" To="Blue"
Duration="0:0:1" BeginTime="0:0:1"/>
<ColorAnimation Storyboard.TargetName="brush"
Storyboard.TargetProperty="Color" From="Blue" To="Yellow"
Duration="0:0:1" BeginTime="0:0:2"/>
<ColorAnimation Storyboard.TargetName="brush"
Storyboard.TargetProperty="Color" From="Yellow"
To="Red" Duration="0:0:1" BeginTime="0:0:3"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
此代码使用四个 ColorAnimation
来更改矩形的颜色。 颜色变化的定时由 BeginTime
属性控制。
上面的代码产生以下输出
使用 ColorAnimation 动画渐变
ColorAnimation
也可以用来创建渐变动画。 可以使用以下代码来实现此目的
<Rectangle Name="myrect" Width="350" Height="250">
<Rectangle.Fill>
<LinearGradientBrush x:Name="brush" StartPoint="0,0" EndPoint="1,1">
<GradientStop x:Name="stop1" Offset="0" Color="Red"/>
<GradientStop x:Name="stop2" Offset="0.5" Color="Green"/>
<GradientStop x:Name="stop3" Offset="1" Color="Blue"/>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<ColorAnimation Storyboard.TargetName="stop1" Storyboard.TargetProperty="Color"
From="Red" To="Green" Duration="0:0:1" BeginTime="0:0:0"/>
<ColorAnimation Storyboard.TargetName="stop1" Storyboard.TargetProperty="Color"
From="Green" To="Blue" Duration="0:0:1" BeginTime="0:0:0.5"/>
<ColorAnimation Storyboard.TargetName="stop1" Storyboard.TargetProperty="Color"
From="Blue" To="Red" Duration="0:0:1" BeginTime="0:0:1"/>
<ColorAnimation Storyboard.TargetName="stop2" Storyboard.TargetProperty="Color"
From="Green" To="Blue" Duration="0:0:1" BeginTime="0:0:0"/>
<ColorAnimation Storyboard.TargetName="stop2" Storyboard.TargetProperty="Color"
From="Blue" To="Red" Duration="0:0:1" BeginTime="0:0:0.5"/>
<ColorAnimation Storyboard.TargetName="stop2" Storyboard.TargetProperty="Color"
From="Red" To="Green" Duration="0:0:1" BeginTime="0:0:1"/>
<ColorAnimation Storyboard.TargetName="stop3" Storyboard.TargetProperty="Color"
From="Blue" To="Red" Duration="0:0:1" BeginTime="0:0:0"/>
<ColorAnimation Storyboard.TargetName="stop3" Storyboard.TargetProperty="Color"
From="Red" To="Green" Duration="0:0:1" BeginTime="0:0:0.5"/>
<ColorAnimation Storyboard.TargetName="stop3" Storyboard.TargetProperty="Color"
From="Green" To="Blue" Duration="0:0:1" BeginTime="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
上面的代码首先创建一个具有红色、绿色和蓝色线性渐变的矩形。 然后,它动画每个渐变以改变其颜色。
以下是上述代码的输出:
Using the Code
我创建了一个应用程序,它显示了一个动画的印度国旗。 该应用程序的完整 XAML 代码如下
<Window x:Class="AnimatedFlag.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Indian Flag" Height="350" Width="525">
<Grid>
<Image Name="flagImage" Margin="12">
<Image.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Name="da" Storyboard.TargetName="flagImage"
Storyboard.TargetProperty="Width" From="200" To="200"
Duration="0:0:0.1" Completed="DoubleAnimation_Completed"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</Grid>
</Window>
这段代码创建了一个虚拟动画。 我称之为虚拟动画,因为它实际上并没有改变任何属性。 <DoubleAnimation>
元素只是使用图像的 Width
属性,但 From
和 To
属性的值是相同的 (200)。 Duration
属性指定动画持续时间为 0.1 秒。 它使用 Completed
事件处理程序在完成后重新启动动画。 代码背后的代码如下
public partial class MainWindow : Window
{
int ctr = 1;
public MainWindow()
{
InitializeComponent();
}
private void DoubleAnimation_Completed(object sender, EventArgs e)
{
ShowImage(); // Display Image
da.BeginAnimation(Image.WidthProperty, da); // Start Animation
}
private void ShowImage()
{
string filename = "Images/Flag" + ctr + ".jpg";
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(filename, UriKind.Relative);
image.EndInit();
flagImage.Source = image;
ctr++;
if (ctr > 6)
{
ctr = 1; // Display first image after the last image
}
}
}
在上面的代码中,DoubleAnimation_Completed
事件处理程序调用 ShowImage()
函数以按顺序显示六个图像。 它使用 DoubleAnimation
类的 BeginAnimation()
方法来重新启动动画。 BeginAnimation
方法的第一个参数是 Image.WidthProperty
,它是一个 DependencyProperty。 第二个参数是 AnimationTimeline
对象。
程序的输出如下
关注点
我使用 Microsoft Visual C# 2010 Express Edition 创建了这个应用程序。 我希望这篇文章有助于理解 WPF 中故事板和动画的概念。