1、基本动画。
WPF提供了一个更高级的模型,通过该模型可以只关注动画的定义,而不考虑它们的渲染方式,这个模型基于依赖项属性基础架构,本质上,WPF动画只不过是在一段时间间隔内修改依赖项属性值的一种方式。不过还有另外一个限制,为了实现属性的动态化,需要有支持相应数据类型的动画类,例如Button.Width属性使用双精度数据类型,为实现属性的动态化,需要使用DoubleAnimation类,但Button.Padding属性使用的是Thickness结构,所以需要使用ThicknessAnimation类。为了为属性应用动画,可以针对相应的数据类型创建自己的动画类,你将发现,System.Windows.Media.Animation名称空间已经为希望使用的大多数数据类型提供了动画类。
2、Animation类。
实际上有两种类型的动画,一种是在开始值和结束值之间以逐步增加的方式改变属性的动画(线性插值过程),另一种是从一个值突然变成另一值得动画(关键帧动画)。所有关键帧动画都使用 "类型名 + AnimationUsingKeyFrames " 的形式进行命名,比如StringAnimationUsingKeyFrames和ObjectAnimationUsingKeyFrames。某些数据类型有关键帧动画类,但没有插值动画类。例如,可使用关键帧为字符串应用动画,不能使用插值为字符串应用动画。然而,所有数据类型都支持关键帧动画,除非他们根本不支持动画。所有具有(使用插值)常规动画类的数据类型,也都有相应的关键帧动画的动画类型,如线性插值的DoubleAnimation对应DoubleAnimationUsingKyyFrames。另外还有一种基于路径的动画。因此,WPF动画使用三种方法:线性插值、关键帧和路径。在System.Windows.Media.Animation名称空间中将发现以下内容:
7个 "类型名+Animation类" 这些类使用插值动画。
22个 "类型名+AnimationUsingKeyFrames" 这些类使用关键帧动画。
3个 "类型名+AnimationUsingPath"类这类使用基于路径的动画。
3、使用代码创建动画。
wpf中,最常用的动画技术是线性插值动画,标准的帧速率是60秒/帧,使用动画的最简单方式是实例化在前面列出的其中一个动画类,然后使用修改元素的BeginAnimation()方法,所有wpf元素,从UIElement基类开始,都继承了BeginAnimation()方法,该方法是IAnimatable接口的一部分。
xaml代码:
<Button Width="150" Height="60" Grid.Row="0" Click="Button_Click">点击开始动画</Button>
<Button Grid.Row="1" Name="btn1" Width="150" Height="60" Content="动画按钮"></Button>
后台代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
//实例化一个DoubleAnimation类。
DoubleAnimation doubleAnimation = new DoubleAnimation();
//设置From属性。
doubleAnimation.From = btn1.Width;
//设置To属性。
doubleAnimation.To = 250;
//设置Duration属性。
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
//为元素设置BeginAnimation方法。
btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);
}
效果图:
其中, From属性是元素的开始值,To属性是元素属性的结束值,Duration是整个动画执行的时间。即使不使用To属性,也可以使用By属性,By值被简单地增加到From值上,使其达到To值。不过,对于非数值数据类型来说,By属性是没有意义的。
4、同时发生的动画。
就是创建多个Animation动画,然后为元素设置属性。
后台代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
//实例化一个DoubleAnimation类。
DoubleAnimation doubleAnimation = new DoubleAnimation();
//设置From属性。
doubleAnimation.From = btn1.Width;
//设置To属性。
doubleAnimation.To = 250;
//设置Duration属性。
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));
//为元素设置BeginAnimation方法。
btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);
//实例化一个DoubleAnimation动画,用于设置元素的高。
DoubleAnimation doubleAnimationHeight = new DoubleAnimation();
//设置Form属性。
doubleAnimationHeight.From = btn1.Height;
//设置To属性的值。
doubleAnimationHeight.By = 70;
//设置时间。
doubleAnimationHeight.Duration = new Duration(TimeSpan.FromSeconds(3));
//开始动画。
btn1.BeginAnimation(Button.HeightProperty, doubleAnimationHeight);
}
5、动画的生命周期。
从技术的角度看,WPF动画只是暂时的,这意味着它们不能真正改变基本属性的值,当动画处于活动状态时,只是覆盖了属性的值。
单向动画,在动画运行结束后会保持处于活动状态,这是因为动画需要将按钮的宽度保持为新值,这回导致如下常见问题,如果尝试使用代码在动画完成后修改属性值,代码将不会起作用,因为代码只是为属性指定了一个新的本地值,但仍会先试用动画之后的属性值。
为了解决动画完成后能修改属性的值,有以下方法可解决。
a)、设置AutoReverse属性,如果将该属性设置为true,将会反向运动,返回原始的值(不适合动画完成后,再为属性设置最后的值,只是还原为动画之前的值)。
b)、改变FillBehavior属性。通常,FillBehavior属性设置为HoldEnd,这意味着当当动画结束时,会继续为目标元素应用最后的值。如果将FillBehavior属性改为Stop,只要动画结束,属性就会恢复为原来的值(适用于动画结束后,再次为其设置新值,一般不与AutoReverse配合着使用,这两个用其中一个就行了)。
6、动画的Completed事件。
使用Completed事件时,要将事件设置BeginAnimation()方法之前,否则不起作用。在Completed中,可通过调用BeginAnimation()方法来渲染不活动的动画,为此,只需要指定属性,并为动画对象传递null引用。
void doubleAnimationHeight_Completed(object sender, EventArgs e)
{
MessageBox.Show("动画的高执行完毕了!!!");
//设置空引用。
btn1.BeginAnimation(Button.HeightProperty, null);
}
7、TimeLine类。
TimeLine类的常用属性。
TimeLine类的常用属性 | |
名称 | 说明 |
BeginTime | 设置将被添加到动画开始之前的延时时间(TimeSpan类型),这一延时总被加载到总时间,具有5秒延时的5秒动画,总时间是10秒。 |
Duration | 动画开始到结束的运行时间。 |
SpeedRatio | 提高或减慢动画速度。SpeedRatio属性值是1,如果增加该属性值为5,动画的速度就会变成原来的5倍。 |
AcclerationRatio | 使动画不是线性的,从而开始时较慢,或者开始时较快,这两个属性的值都在0~1之间,这两个属性值之和不能超过1。 |
AutoReverse | 如果为true,当动画完成时会自动反向播放,返回到原始值。 |
FillBehavior | 决定当动画结束时,如何操作。 |
RepeatBehavior | 通过该属性,可以使用指定的次数或时间间隔重复动画,用于设置这个属性的RepeatBehavior对象决定了确切的行为。 |
7.1)、AccelerationRatio和DeceleRation属性。
可以通过AcclerationRation和DecelerationRation属性压缩部分时间轴,使动画运行的更快,并将拉伸其他时间进行补偿,使总时间保持不变。这两个属性都表示百分比值,例如,将AcceleRation属性设置为0.3,表示希望使用动画持续时间中前30%的时间进行加速。例如在1个10秒的动画中,前3秒会加速运行,而剩余的7秒会以恒定不变的速度运行,如果将DeceleRation属性设置为0.3,那么最后3秒回减速运行。
private void Button_Click(object sender, RoutedEventArgs e)
{
//实例化一个DoubleAnimation类。
DoubleAnimation doubleAnimation = new DoubleAnimation();
//设置From属性。
doubleAnimation.From = btn1.Width;
//前5秒加速度运行。
doubleAnimation.AccelerationRatio = 0.5;
//后2秒减速运行
doubleAnimation.DecelerationRatio = 0.2;
//设置To属性。
doubleAnimation.To = 1000;
//设置Duration属性。
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(10));
//为元素设置BeginAnimation方法。
btn1.BeginAnimation(Button.WidthProperty, doubleAnimation);
}
7.2)、RepeatBehavior属性。
使用RepeataBehavior属性可以控制如何重复运行动画,如果希望重复固定次数,应为RepeatBehavior构造函数传递合适的次数。
还可设置RepeatBehavior为永久重复。
//设置重复次数为3次。
doubleAnimation.RepeatBehavior = new RepeatBehavior(3);
//设置永久重复动画。
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
8、故事版。
WPF动画通过一组动画类表示,使用少数几个属性设置相关信息,如开始值、结束值以及持续时间。这显然使他们非常适合于XAMl,不是很清晰的是,如何为特定的事件和属性关联动画,以及如何在正确的时间触发动画。
故事板:故事板是BeginAnimation()方法的XAML等价物,通过故事板将动画指定到合适的元素和属性。
事件触发器:事件触发器响应属性变化或事件(如按钮的Click事件),并控制故事板。
故事板:
故事板是增强的时间线,可用来分组多个动画,而且具有控制动画播放的能力---暂停、停止以及播放位置。然而Storyboard类提供的最基本功能是,能够使用TargetProperty和TargetName属性指向某个特定属性和特定元素,换句话说,故事板在动画和希望应用动画的属性之间架起了一座桥梁。其中TargetProperty属性和TargetName属性都是附加属性。
<!--创建一个故事板-->
<Storyboard Storyboard.TargetProperty="Width">
<!--创建一个DoubleAnimation类。-->
<DoubleAnimation To="350" RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>
<!--由于Storyboard.TargetProperty属性是附加属性,因此还可以写出-->
<Storyboard >
<!--创建一个DoubleAnimation类。-->
<DoubleAnimation Storyboard.TargetProperty = "Width" To="350" RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>
事件触发器:
可以在以下4个位置定义事件触发器。
a)、在样式中(Style.Triggers集合)。
b)、在数据模板中(DataTemplate.Triggers集合)。
c)、在控件模板中(ControlTemplate.Triggers集合)。
d)、直接在元素中定义事件触发器(FrameworkElement.Triggers集合)。
当创建事件触发器时,需要指定开始触发器的路由事件和触发器执行的一个或多个动作。对于动画,最常用的动作是BeginStoryboard,该动作相当于调用BeginAnimation()方法。所有事件触发器都可以启动动作,所有动作都由继承自System.Windows.TriggerAction的类表示。
xaml代码:
<Button Width="200" Height="80" Content="事件触发器" FontSize="20">
<!--元素触发器-->
<Button.Triggers>
<!--定义事件触发器-->
<EventTrigger RoutedEvent="Button.Click">
<!--执行一个动作-->
<EventTrigger.Actions>
<!--开始故事板-->
<BeginStoryboard>
<!--创建一个故事板-->
<Storyboard >
<!--创建一个DoubleAnimation类。-->
<DoubleAnimation Storyboard.TargetProperty = "Width" To="350" RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
效果图:
9、使用样式关联触发器。
有三种基本类型的WPF触发器:属性触发器、数据触发器以及事件触发器。使用触发器是关联动画的最常用方式,但并不是唯一的选择。
xaml代码:
<Window.Resources>
<Style TargetType="Button">
<Setter Property="FontSize" Value="20"></Setter>
<Style.Triggers>
<!--使用属性触发器-->
<Trigger Property="IsPressed" Value="True">
<!--在这里使用的是EnterActions-->
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Width">
<DoubleAnimation To="300" RepeatBehavior="Forever" Duration="0:0:3"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style>
</Window.Resources>
<Grid>
<Button Width="200" Height="80" Content="使用样式关联触发器"></Button>
</Grid>
效果图:
10、同步的动画。
StoryBoard类间接地继承自TimeLineGroup类,所以StoryBoard类能包含多个动画,这些动画可以作为一组进行管理,这意味着它们可以在同一时间开始。
<Window.Resources>
<Style TargetType="Button">
<Setter Property="FontSize" Value="20"></Setter>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="300" Duration="0:0:3"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="Height" To="100" Duration="0:0:3"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Width="150" Height="70" Content="同步动画"></Button>
</Grid>
效果图:
11、控制播放。
到目前为止,已经在事件触发器中使用了一个动作,加载动画的BeginStoryboard动作,然而,一旦创建故事版,就可以使用其他动作控制故事板,这些动作类都继承自ControllableStoryboardAction类,控制故事版的主要类如下:
控制故事板的动作类 | |
名称 | 说明 |
PauseStoryboard | 停止播放动画并且保持当前位置 |
ResumeStoryboard | 恢复播放暂停的动画。 |
StopStoryboard | 停止播放动画,并将动画时钟重新设置到开始位置。 |
SeekStoryboard | 跳到动画时间线中的特定位置,如果当前动画正在播放,就继续从新位置播放。如果当前动画 |
SetStoryboardSpeedRatio | 改变整个故事板的SpeedRatio属性值。 |
SkipStoryboardToFill | 将故事板移动到时间线的终点。FillBehavior属性设置为HoldEnd,动画继续保持最后的值。 |
RemoveStoryboard | 移除故事板,停止所有正在运行的动画,并将属性返回为原来的、最后一次设置的数值。 |
为成功地执行这些动作,必须在同一个Triggers集合中定义所有的触发器,如果将BeginStoryboard动作的触发器和PauseStoryboard动作的触发器放置到不同的集合中,PauseStoryboard动作就无法工作。
xaml代码:
<Window x:Class="控制播放.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Triggers>
<!--开始事件-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_start">
<BeginStoryboard Name="beginstoryboard1">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:6"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!--停止动画-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_pause">
<PauseStoryboard BeginStoryboardName="beginstoryboard1"></PauseStoryboard>
</EventTrigger>
<!--恢复动画-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_resume">
<ResumeStoryboard BeginStoryboardName="beginstoryboard1"></ResumeStoryboard>
</EventTrigger>
<!--停止动画-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_stop">
<StopStoryboard BeginStoryboardName="beginstoryboard1"></StopStoryboard>
</EventTrigger>
<!--移除动画-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_remove">
<RemoveStoryboard BeginStoryboardName="beginstoryboard1"></RemoveStoryboard>
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Image Name="img" Source="1.jpg"></Image>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Name="btn_start" Content="开始" Margin="10" FontSize="20" Grid.Column="0"></Button>
<Button Name="btn_pause" Content="暂停" Margin="10" FontSize="20" Grid.Column="1"></Button>
<Button Name="btn_resume" Content="恢复" Margin="10" FontSize="20" Grid.Column="2"></Button>
<Button Name="btn_stop" Content="停止" Margin="10" FontSize="20" Grid.Column="3"></Button>
<Button Name="btn_remove" Content="移除" Margin="10" FontSize="20" Grid.Column="4"></Button>
</Grid>
</Grid>
</Window>
效果图:
分析:在包含元素的Triggers集合中(在这里是Window.Triggers集合),使用EventTrigger.SourceName属性关联这些事件触发器,只要SourceName属性和为按钮设置的Name属性相匹配,触发器就会用到恰当的按钮上。还必须要问BeginStoryboard动作指定名称,这样其他触发器BeginStoryboardName属性指定这个名称,连接到相同的故事板,然后进行控制。
12、故事板事件。
故事板事件 | |
Completed | 动画已经到达终点 |
CurrentGlobalSpeedInvalidated | 速度发生了变化,或者动画被暂停、重新开始、停止或移到某个新的位置。 |
CurrentStateInvalidated | 动画已经开始或结束。 |
CurrentTimeInvalidated | 动画时钟已经向前移动了一个步长,正在更改动画。当动画开始、停止或结束时也会引发该事件。 |
RemoveRequested | 动画正在被移除。 |
监视动画进度:
如果要监视动画,要用到Storyboard的一些事件。在这里使用的是CurrentTimeInvalidated事件,每次向前移动动画时钟都会引发该事件。当引发CurrentTimeInvalidated事件时,发送者是Clock对象(Color类位于System.Windows.Media.Animation名称空间中),可通过Clock对象检索当前时间。当前时间使用TimeSpan对象表示,并且可检索当前进度,当前进度使用0~1之间的数值表示。
后台代码:
就是在上面的例子中为故事板加一个CurrentTimeInvalidated事件,然后再界面中放一个label控件(用于显示时间)和ProgressBar(用于显示进度,最大值为1,最小值为0)控件。
private void Storyboard_CurrentTimeInvalidated(object sender, EventArgs e) { Clock storyboardClock = (Clock)sender; if (storyboardClock.CurrentProgress == null) { lblTime.Content = ""; progressBar1.Value = 0; } else { lblTime.Content = storyboardClock.CurrentTime.ToString(); progressBar1.Value = (double)storyboardClock.CurrentProgress; } }
效果图:
13、动画缓动。
线性动画有一个缺点,通常让人觉得机械和不够自然。改进动画并创建更趋自然的动画的秘诀是改变变化速率。不是创建以固定不变的速率改变属性的动画,而是需要设计根据某种方式加速或减速的动画,实现更趋自然的动画的最简单方法是使用预置的缓动函数(EasingFunction)。EasyingFunction属性只能接受单个缓动函数对象,所以不能为同一个动画结合不同的缓动函数。
xaml代码:
<Window.Resources>
<Style TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard Storyboard.TargetProperty="Width">
<DoubleAnimation To="300" Duration="0:0:5">
<!--使用缓动函数-->
<DoubleAnimation.EasingFunction>
<!--设置缓动模式和振荡次数-->
<ElasticEase EasingMode="EaseOut" Oscillations="5"></ElasticEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Width="150" Height="50" Content="缓动动画" FontSize="20"></Button>
</Grid>
效果图:
14、缓动函数类。
在继续分析不同的缓动类之前,理解缓动函数的应用时机是很重要的。所有的缓动函数类都继承自EasingFunctionBase类,并且继承了EasingMode属性,EasingMode有三种值,分别是:EasyIn(在动画开始时应用缓动效果)、EasyOut(动画结束时应用缓动效果)和EasyInOut(在开始和结束时应用缓动动画)。当应用缓动函数时不会改变动画的持续时间。
缓动函数常用类 | ||
名称 | 说明 | 属性 |
BackEase | 当使用EaseIn模式应用该缓动函数时,在动画开始之前拉回动画,当使用EaseOut模式应用该缓动函数时,允许动画稍微超越,然后拉回。 | Amplitude属性决定了拉回和超越的量。默认值是1,可减小该属性值(大于0的任何值)以缩减效果,或增加该属性值放大效果。 |
ElasticEase | 当使用EaseOut模式应用该缓动函数时,使动画超越其最大值并前后摆动,逐渐减慢。当时用EaseIn模式应用该缓动函数时,动画在其开始值周围前后摆动,逐渐增加。 | Oscillations属性控制动画前后摆动的次数。 |
BounceEase | 执行与Elastic缓东函数类似的效果,只是弹跳永远不会超越初始值或最终值。 | Bounce属性控制动画回调的次数(默认是2) |
CircleEase | 使用圆函数加速(使用EaseIn模式),或减速(使用EaseOut模式) | 无 |
CublicEase | 使用基于时间立方的函数加速,其效果与Circle类似,但是加速效果更缓和。 | 无 |
QuadraticEase | 使用基于时间平方的函数加速,效果与CublicEase类似,但加速过程更明显。 | 无 |
QuarticEase | 无 | |
QuinticEase | 无 | |
SinEase | 无 | |
PowerEase | Power | |
ExponentialEase | Exponent |
15、自定义缓动函数。
创建自定义缓动函数一般需要以下几个步骤:
a)、新建一个类,让其继承自EasingFunctionBase类。
b)、重写EaseInCore()方法和CreateInstanceCore()方法。
c)、定义依赖属性。
d)、引用。
后台代码(自定义类):
public class RandomJitterEase : EasingFunctionBase
{
//声明一个Random类,用于声明随机数。
Random rand = new Random();
/// <summary>
/// 重写EaseCore方法。
/// </summary>
/// <param name="normalizedTime"></param>
/// <returns></returns>
protected override double EaseInCore(double normalizedTime)
{
//几乎所有逻辑代码都在EaseInCore方法中运行。该方法接受一个规范化的时间值,本质上是表示动画进度从
//0到1之间的值,当动画开始时,规范化的时间值是0,它从该点开始增加,直到在动画结束点达到1.
//在动画运行期间,每次更新动画的值时,WPF都会调用EaseInCore方法,确切的调用频率取决于动画的帧率。
if (normalizedTime == 1)
{
return 1;
}
else
{
return Math.Abs(normalizedTime - (double)rand.Next(0, 10) / (2010 - Jitter));
}
}
protected override System.Windows.Freezable CreateInstanceCore()
{
return new RandomJitterEase();
}
//定义一个依赖属性。
public static readonly DependencyProperty JitterProperty;
//在静态方法中注册依赖属性。
static RandomJitterEase()
{
JitterProperty = DependencyProperty.Register("Jitter", typeof(int), typeof(RandomJitterEase), new UIPropertyMetadata(1000), new ValidateValueCallback(ValidateJitter));
}
public int Jitter
{
get { return (int)GetValue(JitterProperty); }
set { SetValue(JitterProperty, value); }
}
//此方法用于判断值。
private static bool ValidateJitter(object value)
{
int jitterValue = (int)value;
if (jitterValue <= 2000 && jitterValue >= 0)
{
return true;
}
else
{
return false;
}
}
}
xaml代码:
<Window x:Class="自定义缓动函数.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:自定义缓动函数"
Title="MainWindow" Height="350" Width="525">
<Window.Triggers>
<!--事件触发器,窗体加载的Loaded事件。-->
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="480" Duration="0:0:5"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" From="0" To="480" Duration="0:0:5">
<DoubleAnimation.EasingFunction>
<!--调用自定义缓动函数类-->
<local:RandomJitterEase EasingMode="EaseIn" Jitter="1500"></local:RandomJitterEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
<Canvas ClipToBounds="True">
<Ellipse Name="ellipse1" Width="25" Height="25" Fill="Red"></Ellipse>
<Ellipse Margin="0,70,0,0" Name="ellipse2" Width="25" Height="25" Fill="Green"></Ellipse>
</Canvas>
</Window>
效果图:
16、WPF动画性能和帧率。
通常,为用户界面应用动画,只不过是创建并配置正确的动画和故事版对象。但在其他情况下,特别是同时发生多个动画时,可能更加需要关注性能。WPF试图保持以60帧/秒的速度进行动画,可以确保从开始到结束得到平滑流畅的动画。帧速率越低,会发生抖动现象。帧速率越高,占用的CPU也就越高。通过TimeLine.DesiredFrameRate属性进行调整。
xaml代码:
<Window x:Class="动画性能之帧率.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:动画性能之帧率"
Title="MainWindow" Height="500" Width="525">
<Window.Triggers>
<!--定义一个事件触发器,通过SourceName属性关联button-->
<EventTrigger RoutedEvent="Button.Click" SourceName="btn_start">
<EventTrigger.Actions>
<BeginStoryboard>
<!--通过Timeline.DesiredFrameRate属性设置帧速率-->
<Storyboard Storyboard.TargetName="ellipse" Timeline.DesiredFrameRate="{Binding ElementName=txtBox1, Path=Text}">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" From="0" To="300" Duration="0:0:10"></DoubleAnimation>
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" From="0" To="250" Duration="0:0:10"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="5*"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions >
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Canvas ClipToBounds="True" Grid.Row="0" Grid.ColumnSpan="2" Height="320" Background="Beige">
<Ellipse Name="ellipse" Fill="Red" Width="10" Height="10"></Ellipse>
</Canvas>
<Label Grid.Row="1" Content="帧速率:" FontSize="20" HorizontalAlignment="Right" VerticalAlignment="Center"></Label>
<TextBox Name="txtBox1" Text="1" Width="60" Height="30" FontSize="20" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center"></TextBox>
<Button Name="btn_start" Grid.Row="2" Grid.ColumnSpan="2" Width="200" Height="60" Content="点击动画" FontSize="20"></Button>
</Grid>
</Window>
效果图: