65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 WPF 中的 Storyboard 进行动画

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (56投票s)

2012年4月11日

CPOL

3分钟阅读

viewsIcon

318138

downloadIcon

11299

在 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 属性,但 FromTo 属性的值是相同的 (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 中故事板和动画的概念。

© . All rights reserved.