<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Microsoft.Samples.KeyFrameAnimations.KeyFramesIntroduction"
WindowTitle="KeyFrame Animations">
<Border Width="400" BorderBrush="Black">
<Rectangle Fill="Blue"
Width="50" Height="50"
HorizontalAlignment="Left">
<Rectangle.RenderTransform>
<TranslateTransform
x:Name="MyAnimatedTranslateTransform"
X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown">
<BeginStoryboard>
<Storyboard>
<!-- Animate the TranslateTransform's X property
from 0 to 350, then 50,
then 200 over 10 seconds. -->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="MyAnimatedTranslateTransform"
Storyboard.TargetProperty="X"
Duration="0:0:10">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="350" KeyTime="0:0:2" />
<LinearDoubleKeyFrame Value="50" KeyTime="0:0:7" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:8" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Border>
</Page>
前面部分提到了某些关键帧动画支持多种内插方法。 动画的内插对动画在其持续时间内在值之间进行过渡的方式进行了描述。 通过选择动画要使用哪种关键帧类型,可以定义该关键帧段的内插方法。 有三种不同类型的内插方法:线性、离散和曲线。
使用线性内插,动画将以段持续时间的固定速度进行播放。 例如,如果关键帧段从 0 过渡到 10,持续时间为 5 秒,则动画会在指定时间输出以下值:
曲线内插可用于达到更现实的计时效果。 由于动画通常用于模拟现实世界中出现的效果,因此开发人员可能需要精确地控制对象的加速和减速,并需要严格地对计时段进行操作。 通过自由绘制曲线关键帧,可以使用曲线内插进行动画处理。 使用其他关键帧,可以指定 Value 和 KeyTime。 使用自由绘制曲线关键帧,还可以指定 KeySpline。 以下示例显示了 DoubleAnimationUsingKeyFrames 的单个自由绘制曲线关键帧。 请注意 KeySpline 属性,它就是自由绘制曲线关键帧与其他类型的关键帧的不同之处。
<SplineDoubleKeyFrame Value="500" KeyTime="0:0:7" KeySpline="0.0,1.0 1.0,0.0" />
一条三次方贝塞尔曲线由一个起点、一个终点和两个控制点定义。 自由绘制曲线关键帧的 KeySpline 属性定义从 (0,0) 延伸到 (1,1) 的贝塞尔曲线的两个控制点。 第一个控制点控制贝塞尔曲线前半部分的曲线因子,第二个控制点控制贝塞尔线段后半部分的曲线因子。 生成的曲线描述了该自由绘制曲线关键帧的变化率。 曲线陡度越大,关键帧更改其值的速度越快。 曲线趋于平缓时,关键帧更改其值的速度也趋于缓慢。
可以使用 KeySpline 模拟下落的水滴或跳动的球等的物理轨迹,或者将其他“渐入”和“渐出”效果应用于运动动画。 对于用户交互效果(例如背景淡入/淡出或控制按钮弹跳等),可能要应用曲线内插,以便以特定方式提高或降低动画的变化率。
以下示例将 KeySpline 指定为 0,1 1,0,从而创建了以下贝塞尔曲线。
控制点为 (0.0, 1.0) 和 (1.0, 0.0) 的主曲线
<SplineDoubleKeyFrame Value="500" KeyTime="0:0:7" KeySpline="0.0,1.0 1.0,0.0" />
此关键帧的动画处理在开始时快速进行,减速,然后再次加速,直到结束。
以下示例将 KeySpline 指定为 0.5,0.25 0.75,1.0,从而创建如下贝塞尔曲线。
控制点为 (0.25, 0.5) 和 (0.75, 1.0) 的主曲线
<SplineDoubleKeyFrame Value="350" KeyTime="0:0:15" KeySpline="0.25,0.5 0.75,1" />
由于贝塞尔曲线的曲度变化幅度很小,因此该关键帧的动画处理速率几乎固定不变;只在接近结束时才开始减速。
以下示例使用 DoubleAnimationUsingKeyFrames 为矩形的位置添加动画效果。 因为 DoubleAnimationUsingKeyFrames 使用 SplineDoubleKeyFrame 对象,每个关键帧值之间的转换都使用曲线内插。
<!-- This rectangle is animated using a key frame animation
with splined interpolation. -->
<Rectangle
Width="50"
Height="50"
Fill="Purple">
<Rectangle.RenderTransform>
<TranslateTransform
x:Name="SplineAnimatedTranslateTransform"
X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<!-- Animate the TranslateTransform's X property
from its base value (0) to 500, then 200,
then 350 over 15 seconds. -->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="SplineAnimatedTranslateTransform"
Storyboard.TargetProperty="X"
Duration="0:0:15"
RepeatBehavior="Forever">
<SplineDoubleKeyFrame Value="500" KeyTime="0:0:7" KeySpline="0.0,1.0 1.0,0.0" />
<SplineDoubleKeyFrame Value="200" KeyTime="0:0:10" KeySpline="0.0,0.0 1.0,0.0" />
<SplineDoubleKeyFrame Value="350" KeyTime="0:0:15" KeySpline="0.25,0.5 0.75,1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
曲线内插可能很难理解;使用不同的设置进行体验有助于理解。 通过主曲线动画示例,可以更改主曲线值,并查看由此所产生的动画结果。
组合内插方法
可在一个关键帧动画中使用具有不同内插类型的关键帧。 如果两个具有不同内插的关键帧动画彼此跟随,第二个关键帧的内插方法将用于创建从第一个值到第二个值的过渡。
在以下示例中,将创建一个使用线性、曲线和离散内插的 DoubleAnimationUsingKeyFrames。
<!-- This rectangle is animated using a key frame animation
with a combination of interpolation methods. -->
<Rectangle
Width="50"
Height="50"
Fill="Orange">
<Rectangle.RenderTransform>
<TranslateTransform
x:Name="ComboAnimatedTranslateTransform"
X="0" Y="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<!-- Animate the TranslateTransform's X property
from its base value (0) to 500, then 200,
then 350 over 15 seconds. -->
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ComboAnimatedTranslateTransform"
Storyboard.TargetProperty="X"
Duration="0:0:15"
RepeatBehavior="Forever">
<DiscreteDoubleKeyFrame Value="500" KeyTime="0:0:7" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:10" />
<SplineDoubleKeyFrame Value="350" KeyTime="0:0:15"
KeySpline="0.25,0.5 0.75,1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
有关持续时间和关键时间的更多信息
与其他动画一样,关键帧动画具有 Duration 属性。 除了指定动画的 Duration 外,还需要指定向每个关键帧分配持续时间内的多长一段时间。 可以通过为动画的每个关键帧描述 KeyTime 来实现此目的。 每个关键帧的 KeyTime 指定该关键帧的结束时间。
KeyTime 属性并不指定关键时间的播放时长。 关键帧的播放时长由关键帧的结束时间、前一个关键帧的结束时间以及动画的持续时间确定。 关键时间可以指定为时间值、百分比或特殊值 Uniform 或 Paced。
下表描述了指定关键时间的不同方式。
TimeSpan 值
可以使用 TimeSpan 值来指定 KeyTime。 该值应大于或等于 0 并且小于或等于动画的持续时间。 以下示例演示一个持续时间为 10 秒钟、有四个关键帧(这些关键帧的关键时间指定为时间值)的动画。
在前 3 秒钟内,第一个关键帧在基值和 100 之间进行动画处理,结束时间 = 0:0:03。
第二个关键帧在 100 和 200 之间进行动画处理。 它在第一个关键帧结束后开始(开始时间 = 3 秒),播放 5 秒钟,结束时间 = 0:0:8。
第三个关键帧在 200 和 500 之间进行动画处理。 它在第二个关键帧结束时开始(开始时间 = 8 秒),播放 1 秒钟,结束时间 = 0:0:9。
第四个关键帧在 500 和 600 之间进行动画处理。 它在第三个关键帧结束时开始(开始时间 = 9 秒),播放 1 秒钟,结束时间 = 0:0:10。
<!-- This rectangle is animated with KeyTimes using TimeSpan values.
Goes to 100 in the first 3 seconds, 100 to 200 in
the next 5 seconds, 300 to 500 in the next second,
and 500 to 600 in the final second. -->
<Rectangle Width="50" Height="50" Fill="Blue">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="TranslateTransform01" X="10" Y="30" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="TranslateTransform01"
Storyboard.TargetProperty="X"
Duration="0:0:10"
RepeatBehavior="Forever">
<!-- KeyTime properties are expressed as TimeSpan values
which are in the form of "hours:minutes:seconds". -->
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:3" />
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:8" />
<LinearDoubleKeyFrame Value="500" KeyTime="0:0:9" />
<LinearDoubleKeyFrame Value="600" KeyTime="0:0:10" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
百分比值指定关键帧在动画的 Duration 内的某百分比处结束。 在 XAML 中,指定百分比作为 %
符号后的数字。 在代码中,使用 FromPercent 方法并向其传递一个表示百分比的 Double。 该值必须大于或等于 0 并且小于或等于 100%。 以下示例演示一个持续时间为 10 秒钟、有四个关键帧(这些关键帧的关键时间指定为百分比)的动画。
在前 3 秒钟内,第一个关键帧将在基值和 100 之间进行动画处理,结束时间 = 0:0:3。
第二个关键帧在 100 和 200 之间进行动画处理。 它在第一个关键帧结束后开始(开始时间 = 3 秒),播放 5 秒钟,结束时间 = 0:0:8 (0.8 * 10 = 8)。
第三个关键帧在 200 和 500 之间进行动画处理。 它在第二个关键帧结束时开始(开始时间 = 8 秒),播放 1 秒钟,结束时间 = 0:0:9 (0.9 * 10 = 9)。
第四个关键帧在 500 和 600 之间进行动画处理。 它在第三个关键帧结束时开始(开始时间 = 9 秒),播放 1 秒钟,结束时间 = 0:0:10 (1 * 10 = 10)。
<!-- Identical animation behavior to the previous rectangle
but using percentage values for KeyTimes rather then TimeSpan. -->
<Rectangle Height="50" Width="50" Fill="Purple">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="TranslateTransform02" X="10" Y="110" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="TranslateTransform02"
Storyboard.TargetProperty="X"
Duration="0:0:10"
RepeatBehavior="Forever">
<!-- KeyTime properties are expressed as Percentages. -->
<LinearDoubleKeyFrame Value="100" KeyTime="30%" />
<LinearDoubleKeyFrame Value="200" KeyTime="80%" />
<LinearDoubleKeyFrame Value="500" KeyTime="90%" />
<LinearDoubleKeyFrame Value="600" KeyTime="100%" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
如果希望每个关键帧的持续时间都相同,应使用 Uniform 计时。
Uniform 关键时间按关键帧的数量平均分配可用时间,以确定每个关键帧的结束时间。 以下示例演示一个持续时间为 10 秒钟、有四个关键帧(这些关键帧的关键时间指定为 Uniform)的动画。
在前 2.5 秒钟内,第一个关键帧在基值和 100 之间进行动画处理,结束时间 = 0:0:2.5。
第二个关键帧在 100 和 200 之间进行动画处理。 它在第一个关键帧结束后开始(开始时间 = 2.5 秒),播放大约 2.5 秒钟,结束时间 = 0:0:5。
第三个关键帧在 200 和 500 之间进行动画处理。 它在第二个关键帧结束时开始(开始时间 = 5 秒),播放 2.5 秒钟,结束时间 = 0:0:7.5。
第四个关键帧在 500 和 600 之间进行动画处理。 它在第二个关键帧结束时开始(开始时间 = 7.5 秒),播放 2.5 秒钟,结束时间 = 0:0:1。
<!-- This rectangle is animated with KeyTimes using Uniform values. -->
<Rectangle Height="50" Width="50" Fill="Red">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="TranslateTransform03" X="10" Y="190" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="TranslateTransform03"
Storyboard.TargetProperty="X"
Duration="0:0:10"
RepeatBehavior="Forever">
<!-- KeyTime properties are expressed with values of Uniform.
When a key time is set to "Uniform" the total allotted
time of the animation is divided evenly between key frames.
In this example, the total duration of the animation is
ten seconds and there are four key frames each of which
are set to "Uniform", therefore, the duration of each key frame
is 3.3 seconds (10/3). -->
<LinearDoubleKeyFrame Value="100" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="200" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="500" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="600" KeyTime="Uniform" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
特殊值 Paced
如果希望以固定速率进行动画处理,应使用 Paced 计时。
Paced 关键时间根据每一关键帧的长度来分配可用时间,以确定每帧的持续时间。 这样,动画的速度或速率将保持不变。 以下示例演示一个持续时间为 10 秒钟、有三个关键帧(这些关键帧的关键时间指定为 Paced)的动画。
<!-- Using Paced Values. Rectangle moves between key frames at
uniform rate except for first key frame
because using a Paced value on the first KeyFrame in a
collection of frames gives a time of zero. -->
<Rectangle Height="50" Width="50" Fill="Orange">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="TranslateTransform04" X="10" Y="270" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="TranslateTransform04"
Storyboard.TargetProperty="X"
Duration="0:0:10"
RepeatBehavior="Forever">
<!-- KeyTime properties are expressed with values of Paced.
Paced values are used when a constant rate is desired.
The time allocated to a key frame with a KeyTime of "Paced"
is determined by the time allocated to the other key
frames of the animation. This time is calculated to
attempt to give a "paced" or "constant velocity"
for the animation. -->
<LinearDoubleKeyFrame Value="100" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="200" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="500" KeyTime="Paced" />
<LinearDoubleKeyFrame Value="600" KeyTime="Paced" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
请注意,如果最后一个关键帧的关键时间为 Paced 或 Uniform,则其解析的关键时间将设置为 100%。 如果多帧动画中的第一个关键帧为固定速度,其解析的关键时间将设置为 0。 (如果关键帧集合仅包含一个关键帧,并且是一个固定速度的关键帧,其解析的关键时间将设置为 100%。)
一个关键帧动画中的不同关键帧可使用不同的关键时间类型。
组合关键时间,顺序紊乱的关键帧
可在同一动画中使用具有不同 KeyTime 值类型的关键帧。 尽管建议以关键帧的实际播放顺序来添加关键帧,但此操作不是必需的。 动画和计时系统能够处理顺序紊乱的关键帧。 将忽略关键时间无效的关键帧。
下表描述了为关键帧动画的关键帧解析关键时间的过程。
解析 TimeSpanKeyTime 值。
确定动画的总内插时间,即关键帧动画完成向前迭代所需的全部时间。
如果动画的 Duration 不是 Automatic 或 Forever,则总内插时间是动画的 Duration 属性的值。
否则,总内插时间是其关键帧中所指定的 TimeSpanKeyTime 最大值(如果存在这样的值)。
否则,总内插时间为 1 秒。
使用总内插时间值来解析 PercentKeyTime 值。
如果最后一个关键帧尚未在之前步骤中解析,则将解析该关键帧。 如果最后一个关键帧的 KeyTime 为 Uniform 或 Paced,则其解析时间将等于总内插时间。
如果第一个关键帧的 KeyTime 为 Paced 并且此动画有多个关键帧,则将其 KeyTime 值解析为零;如果只有一个关键帧且其 KeyTime 值为 Paced,则解析为总内插时间,如上一步所述。
解析其余的 UniformKeyTime 值:它们将平均分配可用时间。 在此过程中,会暂时将未解析的 PacedKeyTime 值视为 UniformKeyTime 值,并获得一个临时解析时间。
使用距离最近的、具有已解析的 KeyTime 值的已声明关键帧,解析关键时间未指定的关键帧的 KeyTime 值。
解析剩余 PacedKeyTime 值。 PacedKeyTime 使用相邻关键帧的 KeyTime 值来确定其解析时间。 目的是确保动画速度在此关键帧的解析时间内保持固定不变。
将关键帧按解析时间(主键)以及声明顺序(次键)进行排序,换言之,根据解析关键帧 KeyTime 值按固定顺序进行排序。
KeyTime
KeySpline
Timeline
主曲线动画示例
关键帧动画示例
演示图板概述
关键帧操作说明主题
计时行为概述