Silverlight 简介 Part.5(动画)

       动画(animation)是 Silverlight 一项关键特性。它提供了炫目的视觉效果,这是基于服务器编程的框架无法仿效的(例如 ASP.NET)。在 Sileverlight 中动画可以实现很多效果(例如,鼠标经过时图标变大、logo 旋转、文本滚入视图等),也可以用来实现更宏大的商业设计和基于浏览器的游戏。

       动画是 Silverlight 模型的核心部分。这意味着你不需要以计时器和事件处理代码来实现它们,而是通过少数几个类来声明并配置它们

动画基础知识

       Silverlight 动画是一个精简版的 WPF 动画系统。为了便于理解,需要了解以下关键规则:

  • Silverlight 执行以时间为基础的动画。因此,需要设定初始状态、最终状态、持续时间,而 Silverlight 计算出帧速。
  • Silverlight 使用基于属性的动画模型。这意味着一个动画只能做一件事:修改时间间隔的属性值。在很多方面,这是个很大的限制,但仅仅修改属性,也能创建出多得超乎想像的效果了。
  • 要赋予动画一个属性,就需要使用一个支持这种数据类型的动画类。例如,修改数据类型为 double 的属性,必须使用 DoubleAnimation 类。

       Silverlight 中的动画类比较少,只有有限的数据类型可以使用。目前,能用来修改动画属性的数据类型有 double、object、Color、Point

定义动画

       创建动画是一个多步的过程。需要创建 3 个要素:执行动画的动画对象、管理动画的演示图板、启动演示图板的事件触发器

       Silverlight 有两种动画类,采用不同的变更属性值策略:

  • 线性插值:使用线性策略,属性值在动画的持续时间内逐渐变化,这种策略的例子包括 DoubleAnimationColorAnimationPointAnimation
  • 关键帧动画:使用关键帧策略,属性值可以从一个值跳跃到另一个值,或者结合跳跃和线性插值策略。这种策略的例子包括 DoubleAnimationUsingKeyFramesColorAnimationUsingKeyFramesPointAnimationUsingKeyFrames

       本文只介绍最常用的动画类:DoubleAnimation 类。它采用线性插值,在最小值和最大值之间选择双精度值。和所有动画类一样,被定义在 System.Windows.Media.Animation 命名空间中。

       虽然动画并不是元素,但仍然可用 XAML 标记定义。下面的标记创建一个 DoubleAnimation

<DoubleAnimation From="160" to="300" Duration="0:0:5"></DoubleAnimation>

       这个动画持续5秒(Duration 属性格式:时:分:秒:毫秒),目标值在 160 – 300 之间变化。如果这个 DoubleAnimation 能以 Silverlight 默认的最高帧速运行,动画值每秒调整 60 次。

StoryBoard 类

       演示图板用来管理动画的时间轴,可以用来组织多个动画,也可以用它来控制动画的播放、暂停、停止和改变它的位置。StoryBoard 类提供的最基本功能却是用 TargetPropertyTargetName 属性来指定特定的属性和特定的元素。换言之,演示图板是动画和要设置的动画属性之间的桥梁

       下面的标记定义一个演示图板,为名为 cmdGrow 的按钮的 Width 属性赋予 DoubleAnimation 类:

<Storyboard x:Name="storyboard" Storyboard.TargetName="cmdGrow" 
            Storyboard.TargetProperty="Width">
    <DoubleAnimation From="160" To="300" Duration="0:0:5" ></DoubleAnimation>
</Storyboard>

       Storyboard.TargetName 定义了要应用动画的目标元素,Storyboard.TargetProperty 定义了想在目标元素中改变的属性。如果要设置一个附加属性,例如 Canvas.Top,那么需要将整个属性名用括号括起来:

<Storyboard x:Name="storyboard" Storyboard.TargetName="cmdGrow" 
            Storyboard.TargetProperty="(Canvas.Top)">
    <DoubleAnimation From="160" To="300" Duration="0:0:5" ></DoubleAnimation>
</Storyboard>

       TargetName 和 TargetProperty 都属于附加属性,这意味直接在动画上应用这些属性也是可以的:

<Storyboard x:Name="storyboard">
    <DoubleAnimation From="160" To="300" Duration="0:0:5" 
        Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width">
    </DoubleAnimation>
</Storyboard>

       这种语法是较为常见的。因为它可以让你把多个动画放在一个演示图板里,并且允许每个动画拥有不同的元素和属性。尽管不能为多个动画在同一时间指定相同的属性,但可以(而且往往会)在同一时间为相同元素指定不同的属性。

       所有的 Silverlight 元素都有一个 Resources 属性,用以提供一个集合来存储各种对象。Resources 集合的主要目的是让你能够在 XAML 中定义那些不属于元素,因而也就不能被置于可见内容区域的对象。Resources 可以在代码中进行检索或者在标记中别的地方使用。对于那些按钮增长型动画来说,Resources 集合是一个方便的存储位置:

<UserControl ...>
    <UserControl.Resources>
        <Storyboard x:Name="storyboard">
            <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" 
                             From="160" To="300" Duration="0:0:5" ></DoubleAnimation>
        </Storyboard>
    </UserControl.Resources>
 
    <Grid>
        <Button x:Name="cmdGrow" Width="160" Height="30" 
                Content="This button grows" Click="cmdGrow_Click"></Button>
    </Grid>
</UserControl>

       如果你想在启动动画之前通过程序调整它的属性,你也可以给 DoubleAnimation 添加一个名称,这样你可以在代码中访问到它。

       现在,只需要在事件处理程序中调用 StoryBoard 对象的方法即可。这些方法包括 Begin()、Stop()、Pause()、Resume()、Seek()。

private void cmdGrow_Click(object sender, RoutedEventArgs e)
{
    storyboard.Begin();
}

image image

配置动画属性

       想要将动画这个功能发挥到极致,就必须认真研究 Animation 基类。这个类定义了所有动画类的属性:

名    称

描    述

From 设置动画的初始值。很多时候不必设置,Silverlight 将使用元素的当前值。有时,你希望动画从当前值开始,而不是跳转到一个预设的 From 值。
To 设置动画的结束值。
By 使用 By 而不是 To 来创建一个持续增长的动画。给 By 设置一个值,这个值会和初始值累加在一起。之前的示例中,设置 By=10,去除 From 和 To 的设置,每次单击会使矩形按钮不断的增长
Duration 运行的持续时间
AutoReverse 如果设置为 true,动画结束之后会倒着运行一次回到初始值,持续时间会增加一倍
RepeatBehavior 你可以设置重复运行动画的次数,或者使用 Forever 无休止的重复动画
BeginTime 在动画开始之前设置一个延迟时间。如果你要将不同的动画进行同步,即同一时间启动所有动画,但是动画效果却要按序列运行,这非常有用
SpeedRatio 增加或降低动画运行的速度。默认是 1 。设置成 5 将 5 倍加速完成。
FillBehavior 决定动画结束时属性值的状态。通常,会保留属性值为动画结束时的状态(FillBehavior.HoldEnd),但也可选择让它回到初始值(FillBehavior.Stop

交互式动画实例

       有时,你需要在程序中用代码创建动画的每一个细节,这种情况是相当普遍的。通过编程、配置以及启动动画并不困难。首先,创建动画以及演示图板对象并将动画添加到演示图板中。当这些动画结束的时候,还需要响应 Storyboard.Completed 事件来清理动画

       下面的例子演示了更贴近现实的动画的使用。一开始内容区域充满了形状不规则的矩形。单击矩形它会下降到画布的底部并同时改变颜色。可以连续快速的单击多个矩形以便同时运行多个动画。网页将使用多个演示图板,每个目前正在下降的矩形使用一个演示图板。当有需要时,这些演示图板就会被创建。

image

       本例中的标记定义了一个包含有 Border 和一个 Canvas 的简单页面。它并不包含演示图板,因为这些细节需要在矩形被单击时动态创建:

<UserControl x:Class="SilverlightApplication1.AnimationPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Width="500" Height="500" Loaded="Page_Loaded">
 
    <Border BorderBrush="SteelBlue" BorderThickness="1">
        <Canvas x:Name="canvas" Background="AliceBlue"></Canvas>
    </Border>
</UserControl>

       这个画布并不包含任何其他元素,因为这里将动态生成矩形。当画布被加载时,它在随机的位置创建 20 个大小随机的矩形。这些矩形采用相同的 MouseLeftButtonDown 事件处理程序:

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    // Generate some rectangles.
    Random random = new Random();
    for (int i = 0; i < 20; i++)
    {
        Rectangle rect = new Rectangle();
        rect.Fill = new SolidColorBrush(Colors.Red);
 
        // Size and place it randomly.
        rect.Width = random.Next(10, 40);
        rect.Height = random.Next(10, 40);
        Canvas.SetTop(rect, random.Next((int)(this.Height - rect.Height)));
        Canvas.SetLeft(rect, random.Next((int)(this.Width - rect.Width)));
 
        // Handle clicks.
        rect.MouseLeftButtonDown += new MouseButtonEventHandler(rect_MouseLeftButtonDown);
 
        // Add it to the Canvas
        canvas.Children.Add(rect);
    }
}

       当一个矩形被单击时,需要创建新的演示图板及相应的动画。在本例中,你需要两个动画分别用来修改矩形的属性。DoubleAnimation 用来改变 Canvas.Top 属性使得矩形可以下降;ColorAnimation 用来混合矩形的颜色。

       这个演示图板会被加入到集合中,这样你可以轻松跟踪当前运行的演示图板以及相应的动画元素。通过一个事件处理程序,代码可以在上一个动画结束时接收到通知,然后启动这个动画。

void rect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Rectangle rect = sender as Rectangle;
 
    // Create the storyboard for the rectangle
    Storyboard storyboard = new Storyboard();
 
    // Create the animation for moving the rectangle.
    DoubleAnimation fallingAnimation = new DoubleAnimation();
    Storyboard.SetTarget(fallingAnimation, rect);
    Storyboard.SetTargetProperty(fallingAnimation, new PropertyPath("(Canvas.Top)"));
    fallingAnimation.To = this.Height - rect.Height;
    fallingAnimation.Duration = TimeSpan.FromSeconds(2);
    storyboard.Children.Add(fallingAnimation);
 
    // Create the animation for changing the rectangle's color.
    ColorAnimation colorAnimation = new ColorAnimation();
    Storyboard.SetTarget(colorAnimation, rect.Fill);
    Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("Color"));
    colorAnimation.To = Colors.Blue;
    colorAnimation.Duration = fallingAnimation.Duration;
    storyboard.Children.Add(colorAnimation);
 
    // Track the rectangle.
    animatedShapes.Add(storyboard, rect);
 
    // React when the storyboard is finished.
    storyboard.Completed += new EventHandler(storyboard_Completed);
 
    // Start the storyboard.
    storyboard.Begin();
}

       当演示图板结束时,就到了清理的时间。下面是用来完成这一任务的最简单的代码:

void storyboard_Completed(object sender, EventArgs e)
{
    Storyboard storyboard = sender as Storyboard;
    storyboard.Stop();

    animatedShapes.Remove(storyboard);
}

       然而有一个问题,动画实际上并不真正改变这些属性的值,只不过是临时覆盖它,只是你不会注意到这个事实,因为完成的动画并没有真正停止。相反,当一个动画结束时,它的属性会继续保持在动画结束时的状态。这意味着,在演示图板结束之后,完成了动画的矩形仍会停留在页面的底部,并且仍然是蓝色。

       问题是,当动态创建演示图板时,可能存在相当多的演示图板在同一个时间执行的情况。为了确保良好的性能,在动画结束时明确终止演示图板就变得非常重要。就像上述代码所作的那样

       然而,新问题又来了。终止动画会使得动画的属性回到初始值。在这个例子中,意味着每一次终止一个下降的矩形,它将会跳转到原来的位置,恢复原来的红色。解决办法是检索矩形的 Canvas.Top 属性的当前值(这就是声明 Dictionary 结合的原因,可以获取到矩形元素的引用),然后终止动画,并将这个值重新赋予这个矩形:

void storyboard_Completed(object sender, EventArgs e)
{
    // Stop the animation but keep the new position
    Storyboard storyboard = sender as Storyboard;
    Rectangle rect = animatedShapes[storyboard];
    rect.Fill = new SolidColorBrush(Colors.Blue);
    double newTop = Canvas.GetTop(rect);
    storyboard.Stop();
    Canvas.SetTop(rect, newTop);
 
    // Remove it from the tracking collection.
    animatedShapes.Remove(storyboard);
}

image       

变换

       Silverlight 动画是通过修改属性值工作的。元素有多个属性值可以修改,这非常有用。设置 Canvas.Top、Cnavas.Left 来将一个元素四处移动。改变 Opactity 可以使得元素淡入淡出

       更令人兴奋的变更,例如旋转。秘诀就是变换。变换是一个对象,通过切换坐标系来更改一个形状或者其他元素的绘制方向。在 Silverlight 用户界面中,使用变换来伸展、旋转、扭曲来操控形状、图像和文字

Silverlight 支持的变换(类)

名称

描述

重要属性

TranslateTransform 平移坐标系统。适用于在不同位置画出相同的形状 X、Y
RotateTransform 旋转坐标系统。围绕你选择的中心点进行旋转 Angle、CenterX、CenterY
ScaleTransform 拉伸坐标系统。拉伸或压缩形状 ScaleX(Y)、CenterX(Y)
SkewTransform 倾斜坐标系统。以一定度数扭曲,例如,将正方形变成一个平行四边形 AngleX、AngleY
CenterX、CenterY
MatrixTransform 改变坐标系统。用你提供的矩阵进行矩阵相乘 Matrix
TransformGroup 组合多种变换方式,使得所有方式可以一起使用。你设置的变换的排列顺序非常重要。  

       从技术上讲,所有的变换都采用矩阵数学的方式来改变形状的坐标。然而,使用预先定义好的变换(TranslateTransform、RotateTransform、ScaleTransform、SkewTransform),将会比用 MatrixTransform 并试图为想要实现的操作找到正确的矩阵简单很多

       当使用 TransformGroup 执行一系列变换时,Silverlight 会将你的变换融合成一个单一的 MatrixTransform 确保得到最佳性能。

1. 使用变换

       要变换一个元素时,需要设置想要使用的变换对象的 RenderTransform 属性。根据要使用的变换类型,你需要为其配置不同的属性(如上表)。例如,将一个按钮顺时针旋转 25 度:

<Button Content="A Button">
    <Button.RenderTransform>
        <RotateTransform Angle="25"></RotateTransform>
    </Button.RenderTransform>
</Button>

       如果用这种方式旋转一个元素,就是围绕元素的起点(左上角)开始旋转。如果要围绕不同的点来旋转一个形状,可以使用方便的 RenderTransformOrigin 属性。这个属性使用相对坐标系统来设置中心点,中心点的值介于 0 和 1 之间。换言之,点(0,0)被指定为左上角,点(1,1)则是右下角。(如果这个元素不是正方形,坐标系统就会相应地拉伸)

       在 RenderTransformOrigin 的帮助下,你可以使用如下标记围绕其中心点旋转任何元素:

<Button Content="One" RenderTransformOrigin="0.5,0.5">
    <Button.RenderTransform>
        <RotateTransform Angle="25"></RotateTransform>
    </Button.RenderTransform>
</Button>

       这段标记能达到我们想要的效果,因为无论这个形状大小是多少,点(0.5,0.5)都表示它的中心点。你也可也用 RenderTransformOrigin 属性指定形状边界之外的一个点,可以使用大于 1 或者小于 0 的值,例如,以一个非常遥远的点为圆心(5,5)。

2. 动画变换

       要想在动画中使用变换,第一步是定义变换,动画可以改变现有的变换但是不能创建新的变换。例如下面示例:

<Button x:Name="cmd" Content="A Button" RenderTransformOrigin="0.5,0.5">
    <Button.RenderTransform>
        <RotateTransform x:Name="buttonTransform"></RotateTransform>
    </Button.RenderTransform>
</Button>

       现在这里有一个动画,当鼠标经过一个按钮时,按钮将旋转。这个动画作用于 Button.RotateTransform 对象,并且使用了 Angle 属性:

<Storyboard x:Name="rotateStoryboard">
    <DoubleAnimation Storyboard.TargetName="buttonTransform"
        Storyboard.TargetProperty="Angle" To="360" Duration="0:0:0.8"
        RepeatBehavior="Forever"></DoubleAnimation>
</Storyboard>

       这个按钮每 0.8 秒旋转一次,并且会持续不断进行旋转。旋转中的按钮仍然完全可用。例如,你可以单击它并处理 Click 事件。

private void cmd_MouseEnter(object sender, MouseEventArgs e)
{
    rotateStoryboard.Begin();
}

       要停止旋转,可以响应 MouseLeave 事件。这样的话,虽然可以停止执行旋转的演示图板,但这会导致按钮一下子跳转回初始方向。

原文地址:https://www.cnblogs.com/SkySoot/p/2989247.html