Silverlight
使用 XAML 和 Expression Blend 创建动画
Lawrence Moroney
本文将介绍以下内容:
|
本文使用以下技术: Silverlight 2、Expression Blend |
本文基于 Silverlight 2 的预发布版本。文中的所有信息均有可能发生变更。本文是根据 Laurence Moroney 的《Microsoft Silverlight 2 简介,第 2 版》(Microsoft Press,2008)
改编的。
目录
转换
使用 RotateTransform 属性进行旋转
使用 ScaleTransform 属性进行缩放
使用 TranslateTransform 属性移动对象
使用 SkewTransform 属性倾斜对象
使用 MatrixTransform 定义转换
合并转换
动画
使用触发器和事件触发器
使用 BeginStoryboard 和 Storyboard
定义动画参数
确定对其应用动画效果的目标
设置动画属性
设置 RepeatBehavior 属性
使用 DoubleAnimation 设置值动画效果
使用 ColorAnimation 设置颜色动画效果
使用 PointAnimation 设置点动画效果
使用关键帧
使用线性关键帧
使用离散关键帧
使用 Spline 关键帧
动画和 Expression Blend
结束语
使用 RotateTransform 属性进行旋转
使用 ScaleTransform 属性进行缩放
使用 TranslateTransform 属性移动对象
使用 SkewTransform 属性倾斜对象
使用 MatrixTransform 定义转换
合并转换
动画
使用触发器和事件触发器
使用 BeginStoryboard 和 Storyboard
定义动画参数
确定对其应用动画效果的目标
设置动画属性
设置 RepeatBehavior 属性
使用 DoubleAnimation 设置值动画效果
使用 ColorAnimation 设置颜色动画效果
使用 PointAnimation 设置点动画效果
使用关键帧
使用线性关键帧
使用离散关键帧
使用 Spline 关键帧
动画和 Expression Blend
结束语
XAML 的一个巧妙之处在于:您不仅可以使用 XML 语法声明对象,还可以用同样的方式定义要应用于这些对象的转换。您无需像编程人员一样旋转、移动和倾斜您的对象。并且,通过将动画定义为对象上随时间变化的属性,XAML 还可用于描述如何为对象设置动画效果。首先,我将介绍一下各种转换。然后介绍如何将这些转换添加到时间线以便为 Silverlight? 内容设置动画。
转换
在图形字段中,转换可定义如何将点从一个坐标空间映射到另一个空间中。通常使用转换矩阵来描述转换,转换矩阵是一种特殊的数学构造,支持从一个系统向另一个系统进行简单的数学转换。Silverlight XAML 将此矩阵抽象化,并支持旋转、缩放、倾斜和平移(运动)四组转换。Silverlight XAML 还包含另外一种特殊的转换类型,使您可以定义和实现自己的矩阵,然后可使用此矩阵来合并其他转换。
转换是通过转换属性应用的。存在的多种不同类型的转换属性可应用于不同的对象类型。
因此,当使用 Brush 类型时,可以不同的方式定义转换。一种方式是使用 Brush.Transform 属性,这适用于您希望改变画笔内容的情况,例如,如果您想先旋转某图像,然后再在 ImageBrush 中使用该图像。另一种方式是使用 Brush.RelativeTransform 属性,该属性允许您使用相对值转换画笔,例如,当使用相同画笔绘制不同大小的区域时,您便可能需要使用相对值转换画笔。
使用 Geometry 类型时,可使用 Geometry.Transform 属性进行简单转换。但请记住,此类型不支持相对转换。
最后,在使用用户界面 (UI) 元素时,可指定使用 RenderTransform 属性进行转换。例如,如果您正在转换一个椭圆,可使用 Ellipse.RenderTransform 定义所需的转换。
使用 RotateTransform 属性进行旋转
通过 RotateTransform,您可以绕指定的中心点将元素旋转一个指定的角度。使用 Angle 属性设置旋转角度以设置您希望将项目旋转多少度。要确定您自己的方向,可将指向右边的水平矢量视为 0 度,若按顺时针旋转,指向下的垂直矢量是旋转 90 度的结果。
可使用 CenterX 和 CenterY 属性设置转换的中心,从而指定中心点的坐标。默认为 0.0,因此默认旋转中心点位于容器的左上角。
在下面的 XAML 示例中,使用包含指定了 45 度旋转的 RotateTransform 的 RenderTransform 对 TextBlock 进行旋转:
<TextBlock Width="320" Height="40" Text="This is the text to rotate" TextWrapping="Wrap"> <TextBlock.RenderTransform> <RotateTransform Angle="45" /> </TextBlock.RenderTransform> </TextBlock>
在
图 1 中您可以看到,该文本围绕中心点 (0,0)(位于屏幕左上角)进行了旋转。
图 1
使用 RotateTransform 属性(单击图像可查看大图)
下面的 XAML 演示了如何使用 CenterX 和 CenterY 围绕不同点进行旋转。在此示例中,围绕 (100,200) 点进行了旋转:
<TextBlock Width="320" Height="40" Text="This is the text to rotate" TextWrapping="Wrap" > <TextBlock.RenderTransform> <RotateTransform Angle="45" CenterX="100" CenterY="200" /> </TextBlock.RenderTransform> </TextBlock>
使用 ScaleTransform 属性进行缩放
ScaleTransform 属性用于基于水平轴、垂直轴,或同时基于这两个轴更改对象的大小。如果缩放对象,至少需要指定其中一个绕其进行缩放的轴,同时还要指定希望针对该轴缩放的比例。
使用 ScaleX 属性在水平轴(X 轴)上缩放对象,使用 ScaleY 属性在垂直轴(Y 轴)上缩放对象。这两个属性均设置为双精度值,该值代表在指定轴上将对象的当前大小与其相乘的值。因此,大于 1 的值可使对象扩大相应的倍数。例如,ScaleX 值为 2 可使该对象的水平大小加倍。大于 0 小于 1 的值将使对象缩小。例如,设置为 0.5 会使对象在指定维度上的大小减小一半。
例如,下面的 XAML 将创建一个宽 96 像素、高 88 像素的红色矩形:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="112" Canvas.Top="72" />
图 2 演示了此对象在 Silverlight 中呈现时的外观。
图 2
呈现矩形(单击图像可查看大图)
要对此对象应用 ScaleTransform,可使用 RenderTransform 并将转换指定为 ScaleTransform。XAML 如下:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="112" Canvas.Top="72"> <Rectangle.RenderTransform> <ScaleTransform ScaleX="2" /> </Rectangle.RenderTransform> </Rectangle>
如果您熟悉编码,您会发现,使用 ScaleTransform,该矩形在水平上的大小扩展到了右侧。这是因为未指定缩放中心。您可以使用 CenterX 属性进行水平缩放或使用 CenterY 属性进行垂直缩放来指定缩放中心。这两个属性指定缩放中心的坐标。请注意,此坐标是相对于矩形左上角的。此外,默认坐标为 0,表示缩放会发生在水平轴的右侧和垂直轴的下侧。
如果将 CenterX 属性设置为正值(例如,50),则将绕 X 点将此矩形的最左端向右缩放 50 像素。这看起来像是 CenterX 未更改位置而矩形向左移动了一定的像素数(此大小取决于比例因子的大小)。这是因为拉伸是以该点为中心进行的,将此矩形的左侧向左推而将其右侧向右推。您可以利用相同的方式设置 ScaleY 和 CenterY 值以获得相似的效果:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="80" Canvas.Top="80"> <Rectangle.RenderTransform> <ScaleTransform ScaleX="2" CenterX="50"/> </Rectangle.RenderTransform> </Rectangle>
使用 TranslateTransform 属性移动对象
平移是指在二维平面上将对象从一个位置移动到另一个位置的一种转换。可通过设置定义对象沿其 X 轴和 Y 轴移动的矢量来定义平移。可使用转换中的 X 属性和 Y 属性设置这些矢量。要将项目水平向右移动两个单位,可将 X 属性设置为 2,要将其水平向左移动,可使用负值,例如 -2。同样,要垂直移动对象,可使用 Y 属性,使用正值可使此对象向屏幕下方移动,而使用负值可使对象向屏幕上方移动。
下面是一个平移示例:通过指定 X 和 Y 值,将我们前面看到的红色矩形的位置向左上方移动。这些值有效地构成确定该平移的矢量:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="80" Canvas.Top="80"> <Rectangle.RenderTransform> <TranslateTransform X="-50" Y="-50"/> </Rectangle.RenderTransform> </Rectangle>
结果如
图 3 所示。与
图 2 中矩形的位置相比,此矩形已移动到相对于该指定位置的左上方。
图 3
使用 TranslateTransform 属性(单击图像可查看大图)
使用 SkewTransform 属性倾斜对象
倾斜对象包括沿某一轴以渐进、统一的方式进行更改。这会产生将一个正方形或矩形变成平行四边形的效果。在二维表面上创建深度效果时这种视觉效果很有用。
您可以围绕一个中心点沿 X 轴或 Y 轴倾斜一定的角度。当然,这两种方式可以合并起来使用,这样您就可以同时沿两个轴进行倾斜。以下 XAML 可将矩形沿 X 轴倾斜 45 度:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="80" Canvas.Top="80"> <Rectangle.RenderTransform> <SkewTransform AngleX="45"/> </Rectangle.RenderTransform> </Rectangle>
结果如
图 4 所示。
图 4
使用 SkewTransform 倾斜矩形(单击图像可查看大图)
倾斜对于在图形中模拟三维效果很有帮助。例如,您可以将 SkewTransform 应用到三个相邻矩形(两个沿 X 轴倾斜,一个沿 Y 轴倾斜)来创建一个三维透视效果(请参见
图 5)。
图 5
使用三个倾斜的矩形模拟透视效果(单击图像可查看大图)
使用 MatrixTransform 定义转换
从本质上来说,所有转换都是通过将对象的坐标空间与转换矩阵相乘来执行的。到目前为止,您所看到的每一个转换都是我们熟知且定义明确的转换。矩阵数学和如何实现转换都超出了本文所要讨论的范围,但是为了语法的完整性,我将介绍如何在 Silverlight XAML 中定义它们。
请注意,MatrixTransform 中所用的矩阵都是仿射矩阵,这意味着此矩阵的最后一行始终设置为 (0 0 1),这样您仅需设置前二列。可使用转换的 Matrix 属性设置这些矩阵,该属性采用包含以空格分隔的前两行值的字符串。
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="80" Canvas.Top="80"> <Rectangle.RenderTransform> <MatrixTransform Matrix="1 0 1 2 0 1"/> </Rectangle.RenderTransform> </Rectangle>
使用此矩阵进行转换的效果是呈现一个经过拉伸和倾斜双重转换的矩形。
合并转换
在上例中您可以看到,通过使用转换仿射矩阵及指定使用 MatrixTransform 类型可以创建复杂的转换。但是,如果您不是矩阵数学方面的专家,就需要适用于使用转换的另一项技术,即通过 TransformGroup 元素将上述两步合并。这样,您只需要指定多个转换,并将每个转换的合并效果都应用到此对象即可。以下是一个示例:
<Rectangle Fill="#FFFF0404" Stroke="#FF000000" Width="96" Height="88" Canvas.Left="80" Canvas.Top="80"> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="1.2" ScaleY="1.2" /> <SkewTransform AngleX="30" /> <RotateTransform Angle="45" /> </TransformGroup> </Rectangle.RenderTransform> </Rectangle>
此示例中合并了一个 ScaleTransform,该效果将图形的大小在两个轴上各增加 20%,在 X 轴上倾斜 30 度并旋转 45 度。
“动画”字面上的意思是指“为某物赋与生命”。因此,借助动画,您可以通过在一段时间内或响应用户操作而更改对象的属性(如颜色、大小、不透明度及其他属性)来为自己的作品带来生命力。
在 XAML 中,可通过随着时间更改项目的一个或多个属性为其设置动画效果。此时间是使用时间线定义的。例如,要在 5 秒内将项目移过屏幕,应将时间线指定为 5 秒,设置 Canvas.Left 属性在此时间内从 0 变为屏幕宽度的动画效果。在下面的部分中,我将介绍每种可用的动画类型,以及使用关键帧设置这些属性的动画效果时的区别。
在了解不同的动画类型之前,您应该知道动画的框架中包括 Trigger、EventTrigger 和 Storyboard。但是,首先我将介绍以下基本概念,然后再详细讲解不同的动画类型。
使用触发器和事件触发器
Silverlight 中的动画为了响应使用触发器定义的事件而发生。目前,Silverlight XAML 中仅支持一种触发器类型,即 EventTrigger。每个 UI 属性都具有一个 Triggers 集合,用来定义一个或多个触发器(即一个或多个 EventTrigger)。
因此,将动画添加到元素的第一步是定义其 Trigger 集合;然后需要向您已创建的集合中至少添加一个 EventTrigger。例如,如果您正在为一个矩形设置动画,第一步(指定 Triggers 集合)应如下所示:
<Rectangle x:Name="rect" Fill="Red" Canvas.Top="100" Canvas.Left="100" Width="100" Height="100"> <Rectangle.Triggers> </Rectangle.Triggers> </Rectangle>
接下来,您需要定义一个 EventTrigger 并将其添加到此集合。在这个 EventTrigger 中,使用 RoutedEvent 属性指定动画运行所响应的事件。请注意,RoutedEvent 仅支持 Loaded 事件。
要实现加载矩形时开始的动画,应指定 EventTrigger,如下所示:
<EventTrigger RoutedEvent="Rectangle.Loaded"> </EventTrigger>
运行此动画的 XAML 代码段如下所示:
<Rectangle x:Name="rect" Fill="Red" Canvas.Top="100" Canvas.Left="100" Width="100" Height="100"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> </EventTrigger> </Rectangle.Triggers> </Rectangle>
下一个步骤是定义您希望使用的动画。动画包含在 Storyboards 内。
使用 BeginStoryboard 和 Storyboard
BeginStoryboard 是一个包含 Storyboard 对象的触发器操作。Storyboard 对象包含动画定义。当定义动画时,您只需在 EventTrigger 定义内嵌入这些对象。以下代码通过矩形示例演示了如何实现此操作:
<Rectangle x:Name="rect" Fill="Red" Canvas.Top="100" Canvas.Left="100" Width="100" Height="100"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
定义动画参数
现在动画的框架已建立,您可以指定希望执行的动画。从最根本上来讲,动画是定义如何随时间更改某个属性。您可为三种不同的属性类型设置动画效果。其中的每个属性类型从 From 属性中指定的值(如果尚未设置,则使用当前值)开始设置动画效果,到 To 属性中指定的值结束或者到 By 属性中指定的值结束。
Double 类型 使用 DoubleAnimation 或 DoubleAnimationUsingKeyFrames 为其设置动画效果。此方法用于为包含双精度值的属性(例如,Canvas.Left 等维度属性或不透明度等可视化属性)设置动画效果。
Point 类型 使用 PointAnimiation 或 PointAnimationUsingKeyFrames 类型为其设置动画效果。此特定方法用于为包含点值的属性(例如使用点定义的线段或曲线)设置动画效果。
Color 类型 使用 ColorAnimation 或 ColorAnimationUsingKeyFrames 类型动画为其设置动画效果。此方法用于为包含颜色值的属性(例如元素的背景或笔画)设置动画效果。
确定对其应用动画效果的目标
要定义您希望对其应用动画的对象,可在这些动画类型中使用 Storyboard.TargetName 属性,并将目标对象的名称传递给该属性(此名称在该对象上使用 x:Name 属性进行设置)。另外,还需要使用 Storyboard.TargetProperty 指定要对其设置动画效果的属性。请注意,如果要指定复杂属性或附加属性(如 Canvas.Left),应将其放在括号内。因此,举例来说,要指定 Double 动画效果以对名为 rect 的矩形使用 Canvas.Left 属性,所得到的 XAML 如下所示:
<DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" />
设置动画属性
要定义将目标属性从一个值转换到另一个值所用的时间,可使用 Duration 属性。请注意,此属性定义为 HH:MM:SS 格式,例如,如果动画的持续时间为 5 秒,则指定为 00:00:05,缩写为 0:0:5。
<DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" Duration="0:0:5" />
如果您不希望动画立即开始,可使用 BeginTime 属性和相同的语法插入延迟:
<DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" BeginTime="0:0:5" />
您还可以通过将持续时间与对比速率相乘来调整动画行为。可使用 SpeedRatio 属性完成此操作。例如,上一示例将持续时间设置为 5 秒。您可以通过将 SpeedRatio 设置为 2 来更改对比速率,使该动画持续 10 秒;或者通过将 SpeedRatio 设置为 0.2 来加快动画的速度,使动画在 1 秒内完成。
<DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" SpeedRatio="2" Duration="0:0:5" />
Silverlight 动画提供了一项功能,可以撤消对部分动画所作的更改。例如,如果在特定的时间内将双精度值从 0 移动至 500,Auto?Reverse 会使动画从 500 移回 0。
请注意,如果动画原来设置为运行 5 秒,并且 AutoReverse 设置为 true,则完整的动画需要持续 10 秒。下面是一个包含 AutoReverse 属性的 XAML 示例:
<DoubleAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" AutoReverse="True" Duration="0:0:5" />
设置 RepeatBehavior 属性
运行完动画后,您可以应用大量选项控制动画,使动画以您所希望的方式运行。可使用 RepeatBehavior 属性指定这些选项。此属性可以采用三种不同类型的值:
- 以秒为单位定义的时间。时间线会等待这段时间过后再次演示动画。
- 为实现不断重复而设置为 Forever 的重复行为。
- 通过指定后面带有 x 的数字设置的离散重复数。例如,如果您希望该动画运行 3 次,则指定值 3x。
图 6 演示了一段完整的 XAML,可以将设置动画效果的矩形从 X 轴上的 100 移动至 500,再移回 100,然后将此行为重复 3 次:
图 6 撤消和重复动画
<Rectangle x:Name="rect" Fill="Red" Canvas.Top="100" Canvas.Left="100" Width="100" Height="100"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation RepeatBehavior="3x" Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" To="500" Duration="0:0:5" AutoReverse="True" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
让我们稍微详细地介绍一下其中的每个动画类型。首先,我会介绍设置各种类型的动画分别需要的属性,然后介绍与图像想适应的相关动画关键帧类型。
使用 DoubleAnimation 设置值动画效果
利用 DoubleAnimation 对象,您可以在指定的时间线范围内指定双精度值的更改方式。动画会随着时间作为属性值间的线性插值进行计算。
设置双精度值动画效果时,可以在动画开始时使用 From 值指定该值,然后将其更改为 To 值(绝对目标),或更改为 By 值(相对目标)。例如,如果要将某个项目的 Canvas.Left 属性从 100(接近屏幕的左端)移动至 500,您可以将 From 值设置为 100,将 To 值设置为 500 或将 By 值设置为 400。请注意,如果您同时设置了 To 值和 By 值,则优先采用 To 属性,而忽略 By 属性。同样,如果此矩形已经位于 From 的目标位置,则不需要再指定 From 属性。
上一个 XAML 示例演示了此行为。此矩形位于 Canvas.Left 值为 100 的位置,而 DoubleAnimation 将 To 值指定为 500。因此,该动画会将该值从 100 移动至 500,这将使此矩形一直移动到屏幕的右侧。
使用 ColorAnimation 设置颜色动画效果
ColorAnimation 的使用方式与 DoubleAnimation 类似。您可以使用它指定元素的颜色值如何随时间更改。这样,动画会在指定的时间内作为颜色值间的线性插值进行计算。
设置颜色动画效果时,可以在动画开始时使用 From 属性指定该值。如果不指定此值,则会使用当前的颜色。可使用 To 属性指定所需的结束颜色。还可以指定 By 属性,它提供的结束颜色是将 From 颜色(或开始颜色)值与 By 颜色值相加的最终结果。
在您为基于颜色的属性设置动画效果时,不要直接为该属性的内容设置动画效果,因为该属性的内容通常是画笔而不是颜色。因此,举例来说,如果您希望使某个矩形的填充颜色具有动画效果,就不应将此矩形的 Fill 属性作为目标属性,而应指定用于执行填充行为的 SolidBrush 的 Color 属性。
图 7 中的示例演示了如何使矩形的颜色具有动画效果:使用颜色动画,在 5 秒内将该矩形的颜色由黑色变为白色。在代码中您可以看到,此 XAML 代码段指定了 SolidColorBrush 的 Color 属性,从而以目标属性填充了该图形。请注意,这是解决类似这种情况中复杂属性所使用的典型 XAML 语法。
图 7 设置颜色更改的动画效果
<Rectangle x:Name="rect" Canvas.Top="100" Canvas.Left="100" Width="100" Height="100" Fill="Black"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetName="rect" Storyboard.TargetProperty= "(Shape.Fill).(SolidColorBrush.Color)" To="#00000000" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
使用 PointAnimation 设置点的动画效果
要更改随时间而定义为点的值,需要使用 PointAnimation 类型。这样,动画会在指定的时间内作为两个值之间的线性插值进行计算。
与设置 Color 和 Double 动画效果的方法类似,您可以使用 From 指定开始值,使用 By 将目标指定为相对方向,或使用 To 将目标指定为绝对点。
图 8 中的示例演示了如何使贝塞尔曲线的终点具有动画效果。在此示例中,该贝塞尔曲线将起点定义在 (100,100),终点定义在 (300,100),控制点定义在 (200,0)。动画设置为在加载该曲线后触发,设置该曲线的终点 (Point2) 从 (300,100) 更改为 (300,600) 的动画效果的持续时间为 5 秒。
图 8 使曲线的终点具有动画效果
<Path Stroke="Black" > <Path.Data> <PathGeometry> <PathFigure StartPoint="100,100"> <QuadraticBezierSegment x:Name="seg" Point1="200,0" Point2="300,100" /> </PathFigure> </PathGeometry> </Path.Data> <Path.Triggers> <EventTrigger RoutedEvent="Path.Loaded"> <BeginStoryboard> <Storyboard> <PointAnimation Storyboard.TargetName="seg" Storyboard.TargetProperty="Point2" From="300,100" To="300,600" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Path.Triggers> </Path>
使用关键帧
您刚刚了解的三种动画类型:Color?Animation、DoubleAnimation 和 PointAnimation,全部是通过使用线性插值随时间更改定义的属性来完成的。例如,如果您将一个双精度值在 5 秒内从 100 移动至 500,则它会以每秒 80 的速度增长。
这三种动画类型中的任意一种都可以通过一组称为里程碑的关键帧对此转换进行定义。要将动画的线性行为从开始属性更改为结束属性,只需插入一个或多个关键帧。然后在这些不同的点之间定义希望的动画类型。
使用关键时间对关键帧进行定义。这些时间是相对于动画的开始时间指定的;它们同样可以指定关键帧的结束时间。例如,如果在 9 秒长的动画中需要设置 3 个间隔均匀的关键帧,您可以指定第一个关键帧在 0:0:3 结束,第二个在 0:0:6 结束,第三个在 0:0:9 结束。请记住,您不需要指定关键时间的长度,而要指定每个关键帧的结束时间。
来看另一个示例,假设一个 Double 动画,您希望其横跨从 100 到 500 的前后两个部分。该动画应在前半部分快速移动,而在后半部分缓慢移动。但首先,它总共需要 6 秒的转换时间。因为 350 是 100 和 500 之间的中点,因此应定义一个从点 350 开始的关键帧。使用关键时间 0:0:1 将该关键帧设置为在起点和中间点之间持续 1 秒,然后使用第二个关键时间 0:0:6 将中点和终点之间的持续时间设置为 5 秒。现在项目被设置为飞快移动到屏幕中的中点,然后慢吞吞地行进过其余路程。
在上一示例中,两个设置动画效果的部分都将以线性方式插入。要显示其他的灵活性,则需要提供另外两种类型的关键帧:立即从两值之间的值跳过的离散关键帧,和使用二次曲线移过起点和终点之间的值以定义插值的样条关键帧。(在后面部分中,您将看到如何使用关键帧为 Double 类型定义动画。请注意,该原理同样适用于 Point 动画类型和 Color 动画类型。)
要指定关键帧,应在动画中使用 UsingKeyFrames 后缀。这就是说,若要既定义 Double 动画又使用关键帧,需要在指定目标和属性的对象中使用 DoubleAnimationUsingKeyFrames(与使用 DoubleAnimation 的方法相同)。DoubleAnimationUsingKeyFrames 包含了对关键帧的定义。(我以前提到过,这同样适用于 PointAnimationUsingKeyFrames 或 ColorAnimationUsingKeyFrames。)
使用线性关键帧
在两个属性值之间设置动画的默认方法是使用线性插值,其中的数值将随时间均匀分配。您同样可以使用 LinearKeyFrame 类型定义帧之间的线性步骤,LinearKeyFrame 类型也使用线性插值,但它用于关键帧之间,这样您便可以设置加速或减速效果。
请仔细观察
图 9 中第一部分显示的动画代码。此处使用了 DoubleAnimationUsingKeyFrames,它定义了两个关键帧。其中一个为在 1 秒内的 Canvas.Left 更改定义了 0 至 300 间的线性插值,而另一个为在 8 秒内的 Canvas.Left 更改定义了 300 至 600 间的线性插值。这会使矩形产生快速移动到中点,然后缓慢地移动过其余路程的效果。相似的原理同样适用于 LinearPointKeyFrame 和 LinearColorKeyFrame。
图 9 具有关键帧的动画
线性插值
<Rectangle Fill="#FFFF0000" Stroke="#FF000000" Width="40" Height="40" Canvas.Top="40" x:Name="rect"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" > <LinearDoubleKeyFrame KeyTime="0:0:1" Value="300" /> <LinearDoubleKeyFrame KeyTime="0:0:9" Value="600" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
离散关键帧
<Rectangle Fill="#FFFF0000" Stroke="#FF000000" Width="40" Height="40" Canvas.Top="40" x:Name="rect"> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="rect" Storyboard.TargetProperty="(Canvas.Left)" > <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="300" /> <DiscreteDoubleKeyFrame KeyTime="0:0:9" Value="600" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
使用离散关键帧
如果希望将属性从一个值更改为另一个值,但不希望使用线性插值,则可以使用离散关键帧。这可以使对象跳至指定关键帧时间处的值。
图 9 的第二部分演示了与上一部分相同的示例,只是使用了离散关键帧。进入动画 1 秒后,该矩形将跳过屏幕一半路程。进入动画九秒后,它将跳至屏幕右端。请注意,相似的原理同样适用于 DiscretePointKeyFrame 和 DiscreteColorKeyFrame。
使用样条关键帧
要使用提供加速和减速效果的曲线值将属性从一个值更改为另一个值,可使用样条关键帧。为此,您首先要定义一条二次贝塞尔曲线,然后该属性从一个值移动到另一个值的速度由该曲线的并行投影确定。
如果感觉太抽象,请考虑以下情形:太阳正位于头顶,您将一个棒球击打到外场。您注视着该棒球的影子。棒球向空中上升时,其影子像是在加速移动。当它到达顶点时,您可以看到影子在减速。当球落下时,您会看到影子再次加速,直到它被某个人接住或者落到地面上。
在本示例中,将您的动画想象为球的影子,而将样条想象成棒球的运动曲线。使用 KeySpline 定义棒球的轨迹(一条样条)。KeySpline 定义二次贝塞尔曲线的控制点。将其具体化,以使曲线的第一点位于 0,第二点位于 1。为了得到棒球运动轨迹遵循的抛物线圆弧,KeySpline 需要包含两个以逗号分隔的具体化值。
要定义一条曲线(如棒球的飞行轨迹),可以使用 KeySpline(如 0.3,0 0.6,1)指定样条。0.3,0 0.6,1 将曲线的第一个点定义在 (0.3,0),而第二个点定义在 (0.6,1)。这将产生的动画效果是:在棒球完成大约三分之一的运动轨迹前迅速加速;接着,在棒球到达三分之二的轨迹前匀速移动;然后,设置动画效果的棒球会减速完成余下的飞行轨迹,模拟棒球落到地面上的运动。
图 10 中的示例演示如何使用 KeySpline 来定义使用 DoubleAnimationUsingKeyFrames 的此模拟中的样条。此示例为椭圆设置了动画效果,这样它在屏幕上的移动方式类似于棒球的影子,就像当棒球在空中飞行时,从棒球上方向下看时所看到的地面上的情况。
图 10 使用 KeySpline 定义样条
<Ellipse Fill="a#FF444444" Stroke="#FF444444" Width="40" Height="40" Canvas.Top="40" x:Name="ball"> <Ellipse.Triggers> <EventTrigger RoutedEvent="Ellipse.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ball" Storyboard.TargetProperty="(Canvas.Left)" > <SplineDoubleKeyFrame KeyTime="0:0:5" KeySpline="0.3,0 0.6,1" Value="600" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Ellipse.Triggers> </Ellipse>
动画和 Expression Blend
可以在 Expression Blend? 中以图形的方式定义动画。这会生成执行该动画的 XAML,从而自动为您提供不同类型的动画。
使用 Expression Blend 时,请从“窗口”菜单选择“动画工作区”。这将为您提供以图形方式设计时间线的工具,当您编辑希望使用可视编辑器更改的属性时,会生成该动画的 XAML 代码。
在屏幕的底部,您可以看到“对象和时间线”视图。这使您可以添加时间线,然后以可视的方式添加关键帧。要添加新时间线,请单击“对象和时间线”视图中的“+”按钮。
单击“+”按钮时,您将看见一个“创建 Storyboard”对话框,要求您输入要创建的 Storyboard 的名称。在本例中,我已将默认名称从 Storyboard1 更改为 Timeline1,并取消选中“创建为资源”复选框。
使用 Expression Blend 时,可在画布级别创建动画,或者将动画创建为资源。在前一种情况下,动画接着运行以响应画布上的触发器。以下是一个由 Blend 通过“创建 Storyboard”创建的 XAML 的示例,用户在此 XAML 中指定了他们不希望将动画创建为资源:
<Canvas.Triggers> <EventTrigger RoutedEvent="Canvas.Loaded"> <BeginStoryboard> <Storyboard x:Name="Timeline1"/> </BeginStoryboard> </EventTrigger> </Canvas.Triggers>
请注意,如果用户选中了“创建为资源”复选框,则将在 <Canvas.Resources> 内部创建 Storyboard,然后您必须从 JavaScript 中运行它。
“对象和时间线”视图将更改为显示刚刚创建的时间线。您可以在
图 11 的底部看到该时间线。时间线中时间 0 处的垂直线指示当前时间。(在 Expression Blend 中,此线呈黄色。)要添加关键帧,只需将此线拖动到希望设置关键帧的地方,然后单击“记录关键帧”按钮。此按钮位于 0:00:000 的左边的时间轴的正上方。
图 11
Expression Blend 动画工作区(单击图像可查看大图)
将此线拖动到 4 秒标记处,然后添加一个关键帧。您会看到该关键帧添加为时间线上的一个小椭圆形。现在时间线位于 4 秒帧上,并且已添加了一个关键帧,您可以编辑矩形的颜色、位置、不透明度或者形状,而 Blend 将计算实现该动画所需的正确转换。最后,您可能注意到,如果来回拖动该时间线指示器,您可以预览该动画并查看它在任一特定时间的状态。
在本文中,我介绍了如何在 Silverlight XAML 定义转换和动画。我向您介绍了旋转、缩放或者倾斜对象所使用的不同类型的转换,以及应用到图形时使用仿射矩阵的任意形式的转换。然后,我对动画进行了概述,并向您介绍如何定义基于 XAML 触发器运行的动画。您看到了动画如何随时间更改属性值,并看到了可为双精度值、点和颜色设置动画效果的 XAML 类型。随后我介绍了如何使用关键帧更好地控制您的动画。最后,我向您介绍了 Expression Blend 动画设计器,您可以看到如何轻松地使用 Expression Blend 以可视方式生成动画。
Laurence Moroney 是 Microsoft 的一位高级技术推广人员,专门研究 Silverlight。他著有多部有关计算主题(包括 Silverlight、AJAX、互操作性和安全性)的书籍。您可以通过 blogs.msdn.com/webnext 访问 Laurence 的博客。