
原文:WPF编程,通过KeyFrame 类型制作控件线性动画的一种方法。
版权声明:我不生产代码,我只是代码的搬运工。 https://blog.csdn.net/qq_43307934/article/details/87271931
From/To/By类型的动画仅支持从一个值到另一个值的线性内插,
或者有限形式的非线性内插(AccelerationRatio和DecelerationRatio)。
如果要描述一个更复杂的动画,如希望一个动画的属性从A值到B值再到C值,则用Key frame动画。
它在指定的时间提供指定的值。
这里以一个球动态移动为例
1、前台
<Canvas Background="AntiqueWhite">
<Ellipse Name="elips"
Width="48"
Height="48"
Fill="Red"
Canvas.Left="480"
Canvas.Top="96" />
<Canvas.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard TargetName="elips"
TargetProperty="(Canvas.Left)">
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
AutoReverse="True"
Duration="0:0:10">
<LinearDoubleKeyFrame KeyTime="0:0:5"
Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:5.5"
Value="48" />
<DiscreteDoubleKeyFrame KeyTime="0:0:8"
Value="244" />
<DiscreteDoubleKeyFrame KeyTime="0:0:9"
Value="480" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
以上是对控件的单个属性值进行变化。
如果需要对控件的多个属性值进行更改,代码如下:
<Canvas Background="AntiqueWhite"
>
<Ellipse Name="elips"
Width="48"
Height="48"
Fill="Red"
Canvas.Left="480"
Canvas.Top="96" />
<Canvas.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<BeginStoryboard>
<Storyboard >
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
AutoReverse="True"
Duration="0:0:10"
Storyboard.TargetName="elips"
Storyboard.TargetProperty="(Canvas.Left)">
<LinearDoubleKeyFrame KeyTime="0:0:5"
Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:5.5"
Value="48" />
<LinearDoubleKeyFrame KeyTime="0:0:8"
Value="244" />
<LinearDoubleKeyFrame KeyTime="0:0:9"
Value="480" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
AutoReverse="True"
Duration="0:0:10"
Storyboard.TargetName="elips"
Storyboard.TargetProperty="(Canvas.Top)">
<LinearDoubleKeyFrame KeyTime="0:0:2"
Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:4"
Value="58" />
<LinearDoubleKeyFrame KeyTime="0:0:6"
Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:9"
Value="96" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
RepeatBehavior="Forever" 为一直重复这个动画。
AutoReverse="True" 为来回运行。如果为false则只单程运行。
Duration="0:0:10" 为运行总时间。
2、解释:
球的开始位置是(480,96),这是通过Canvas.Left和Canvas.Top两个附加属性设置的。
动画改变Canvas.Left属性值,
第1个LinearDoubleKeyFrame 表示在动画的前5秒Canvas.Left 属性值从480改变为0,意味着球会匀速地向左运动直到Canvas的最左侧;
第2个LinearDoubleKeyFrame表示在接下来的0.5秒Canvas.Left属性值从0改变为48,意味着球会向右匀速运动;
第3个KeyFrame是一个DiscreteDoubleKeyFrame,表示在2.5秒后,Canvas.Left属性值会从48直接变化为244;
第4个DiscreteDoubleKeyFrame,表示Canvas.Left会在下秒直接变化为480。
即球在6秒时突然由横坐标48的位置突然跳到144的位置,然后在7秒时突然从144的位置跳到240的位置。
下一轮开始的时候,球回原路返回至起点。
开始下一轮的循环。
3、其它
DoubleKeyFrame对应的几种插值算法的关键帧为:
LinearDoubleKeyFrame、DiscreteDoubleKeyFrame、SplineDoubleKeyFrame、EasingDoubleKeyFrame。
在关键帧动画中,我们除了定义关键帧外,还需要定义两个关键帧之间的插值算法,这样系统才能根据关键帧和插值算法生成中间状态。WPF系统内置四种插值算法:
线性: 两个关键帧之间均匀变化
离散: 两个关键帧之间突变(到达时间点的时候硬切换,没有过渡效果)
样条: 使用贝塞尔曲线实现更精确的加速和减速控制
缓动: 使用缓动函数曲线实现弹性变化
综上来看,线性算法最常用,样条算法能实现精准加速和减速控制。离散的这种硬切换的效果虽然看起来没有什么动画效果,但用于连接关键帧还是比较常用的。另外在一些硬过渡的地方也是能用到的,例如实现闪烁效果。
这几种算法的具体效果这里就不做更多的介绍了,感兴趣的朋友可以看看如下两个链接中的描述和例子:
值得一提的是,并不是所有关键帧动画都支持这几种算法的,具体支持情况请参看MSDN:关键帧动画概述。 当然,对于不支持的也是可以自己手动实现的。
关键帧的时间点(KeyTime)
关键帧的时间点由IKeyFrame.KeyTime属性指定。它是一个KeyTime类型,它有如下几种取值类型:
时间点TimeSpan: 靠TimeSpan直接决定时间点,可以通过函数KeyTime.FromTimeSpan()创建,也可以直接用TimeSpan隐式转换。
相对时间Percent: 指定的是百分比,通过时间线的Duration来联合决定对应的时间点。通过函数KeyTime.FromPercent()创建。
特殊值Uniform: 时间线平均分布每个关键帧所需要的时间。通过函数KeyTime.Uniform创建。
特殊值Paced: 间线按固定的帧率分配所需时间,这种情况下,变化大的关键帧分配时间长,变化小的关键帧分配时间段。通过函数KeyTime.Paced创建。
用代码创建的方式这儿就不举例了,这里就仅仅列举一下如何在XAML中表示这几种时间:
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:3" />
<LinearDoubleKeyFrame Value="100" KeyTime="30%" />
<LinearDoubleKeyFrame Value="100" KeyTime="Uniform" />
<LinearDoubleKeyFrame Value="100" KeyTime="Paced" />
4、后台实现
例如,改变按钮宽度的例子,如果我们要实现如下效果:
在2秒时将宽度从 0变为350
在7秒时将宽度变为50
在9秒的时候将其宽度变为200
var widthAnimation = new DoubleAnimationUsingKeyFrames();
var keyFrames = widthAnimation.KeyFrames;
keyFrames.Add(new LinearDoubleKeyFrame(0, TimeSpan.FromSeconds(0)));
keyFrames.Add(new LinearDoubleKeyFrame(350, TimeSpan.FromSeconds(2)));
keyFrames.Add(new LinearDoubleKeyFrame(50, TimeSpan.FromSeconds(7)));
keyFrames.Add(new LinearDoubleKeyFrame(200, TimeSpan.FromSeconds(9)));
button.BeginAnimation(WidthProperty, widthAnimation);