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>
<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>
<Button Width="150" Height="70" Content="同步动画"></Button>
</Grid>
11、控制播放。
到目前为止,已经在事件触发器中使用了一个动作,加载动画的BeginStoryboard动作,然而,一旦创建故事版,就可以使用其他动作控制故事板,这些动作类都继承自ControllableStoryboardAction类,控制故事版的主要类如下:
控制故事板的动作类
PauseStoryboard
停止播放动画并且保持当前位置
ResumeStoryboard
恢复播放暂停的动画。
StopStoryboard
停止播放动画,并将动画时钟重新设置到开始位置。
SeekStoryboard
跳到动画时间线中的特定位置,如果当前动画正在播放,就继续从新位置播放。如果当前动画
是暂停的,就继续保持暂停。
为成功地执行这些动作,必须在同一个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.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;
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>
<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;
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;
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>
点击进入推荐链接