WPF动画笔记

4 篇文章 0 订阅

一、基本动画

WPF提供了一个更高级的模型,通过该模型可以只关注动画的定义,而不考虑它们的渲染方式,这个模型基于依赖项属性基础架构,本质上,WPF动画只不过是在一段时间间隔内修改依赖项属性值的一种方式。不过还有另外一个限制,为了实现属性的动态化,需要有支持相应数据类型的动画类,例如Button.Width属性使用双精度数据类型,为实现属性的动态化,需要使用DoubleAnimation类,但Button.Padding属性使用的是Thickness结构,所以需要使用ThicknessAnimation类。为了为属性应用动画,可以针对相应的数据类型创建自己的动画类,你将发现,System.Windows.Media.Animation名称空间已经为希望使用的大多数数据类型提供了动画类。

二、Animation类

实际上有两种类型的动画,一种是在开始值和结束值之间以逐步增加的方式改变属性的动画(线性插值过程),另一种是从一个值突然变成另一值得动画(关键帧动画)。所有关键帧动画都使用 "类型名 + AnimationUsingKeyFrames " 的形式进行命名,比如StringAnimationUsingKeyFrames和ObjectAnimationUsingKeyFrames。某些数据类型有关键帧动画类,但没有插值动画类。例如,可使用关键帧为字符串应用动画,不能使用插值为字符串应用动画。然而,所有数据类型都支持关键帧动画,除非他们根本不支持动画。所有具有(使用插值)常规动画类的数据类型,也都有相应的关键帧动画的动画类型,如线性插值的DoubleAnimation对应DoubleAnimationUsingKyyFrames。另外还有一种基于路径的动画。因此,WPF动画使用三种方法:线性插值、关键帧和路径。在System.Windows.Media.Animation名称空间中将发现以下内容:

7个 “类型名+Animation类” 这些类使用插值动画。

22个 “类型名+AnimationUsingKeyFrames” 这些类使用关键帧动画。

3个 "类型名+AnimationUsingPath"类这类使用基于路径的动画。

三、使用代码创建动画

wpf中,最常用的动画技术是线性插值动画,标准的帧速率是60秒/帧,使用动画的最简单方式是实例化在前面列出的其中一个动画类,然后使用修改元素的BeginAnimation()方法,所有wpf元素,从UIElement基类开始,都继承了BeginAnimation()方法,该方法是IAnimatable接口的一部分。

1.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>

2.后台代码

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);
}

3.效果图

null
其中, From属性是元素的开始值,To属性是元素属性的结束值,Duration是整个动画执行的时间。即使不使用To属性,也可以使用By属性,By值被简单地增加到From值上,使其达到To值。不过,对于非数值数据类型来说,By属性是没有意义的。

四、同时发生的动画

就是创建多个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);
}

五、动画的生命周期。

从技术的角度看,WPF动画只是暂时的,这意味着它们不能真正改变基本属性的值,当动画处于活动状态时,只是覆盖了属性的值。

单向动画,在动画运行结束后会保持处于活动状态,这是因为动画需要将按钮的宽度保持为新值,这回导致如下常见问题,如果尝试使用代码在动画完成后修改属性值,代码将不会起作用,因为代码只是为属性指定了一个新的本地值,但仍会先试用动画之后的属性值。

为了解决动画完成后能修改属性的值,有以下方法可解决。

a)、设置AutoReverse属性,如果将该属性设置为true,将会反向运动,返回原始的值(不适合动画完成后,再为属性设置最后的值,只是还原为动画之前的值)。

b)、改变FillBehavior属性。通常,FillBehavior属性设置为HoldEnd,这意味着当当动画结束时,会继续为目标元素应用最后的值。如果将FillBehavior属性改为Stop,只要动画结束,属性就会恢复为原来的值(适用于动画结束后,再次为其设置新值,一般不与AutoReverse配合着使用,这两个用其中一个就行了)。

六、动画的Completed事件。

使用Completed事件时,要将事件设置BeginAnimation()方法之前,否则不起作用。在Completed中,可通过调用BeginAnimation()方法来渲染不活动的动画,为此,只需要指定属性,并为动画对象传递null引用。

void doubleAnimationHeight_Completed(object sender, EventArgs e)
{
    MessageBox.Show("动画的高执行完毕了!!!");
    //设置空引用。
    btn1.BeginAnimation(Button.HeightProperty, null);
}

七、TimeLine类。

TimeLine类的常用属性。

TimeLine类的常用属性
名称说明
BeginTime设置将被添加到动画开始之前的延时时间(TimeSpan类型),这一延时总被加载到总时间,具有5秒延时的5秒动画,总时间是10秒。
Duration动画开始到结束的运行时间。
SpeedRatio提高或减慢动画速度。SpeedRatio属性值是1,如果增加该属性值为5,动画的速度就会变成原来的5倍。
AcclerationRatio ,DecelerationRatio使动画不是线性的,从而开始时较慢,或者开始时较快,这两个属性的值都在0~1之间,这两个属性值之和不能超过1。
AutoReverse如果为true,当动画完成时会自动反向播放,返回到原始值。
FillBehavior决定当动画结束时,如何操作。
RepeatBehavior通过该属性,可以使用指定的次数或时间间隔重复动画,用于设置这个属性的RepeatBehavior对象决

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);
}

RepeatBehavior属性。

使用RepeataBehavior属性可以控制如何重复运行动画,如果希望重复固定次数,应为RepeatBehavior构造函数传递合适的次数。

还可设置RepeatBehavior为永久重复。

//设置重复次数为3次。
doubleAnimation.RepeatBehavior = new RepeatBehavior(3);
//设置永久重复动画。
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;

八、故事板

WPF动画通过一组动画类表示,使用少数几个属性设置相关信息,如开始值、结束值以及持续时间。这显然使他们非常适合于XAMl,不是很清晰的是,如何为特定的事件和属性关联动画,以及如何在正确的时间触发动画。

故事板:故事板是BeginAnimation()方法的XAML等价物,通过故事板将动画指定到合适的元素和属性。

事件触发器:事件触发器响应属性变化或事件(如按钮的Click事件),并控制故事板。

1.故事板

故事板是增强的时间线,可用来分组多个动画,而且具有控制动画播放的能力—暂停、停止以及播放位置。然而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>

2.事件触发器

可以在以下4个位置定义事件触发器。

a)、在样式中(Style.Triggers集合)。

b)、在数据模板中(DataTemplate.Triggers集合)。

c)、在控件模板中(ControlTemplate.Triggers集合)。

d)、直接在元素中定义事件触发器(FrameworkElement.Triggers集合)。

当创建事件触发器时,需要指定开始触发器的路由事件和触发器执行的一个或多个动作。对于动画,最常用的动作是BeginStoryboard,该动作相当于调用BeginAnimation()方法。所有事件触发器都可以启动动作,所有动作都由继承自System.Windows.TriggerAction的类表示。

3.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>

4.效果图

九、使用样式关联触发器。

有三种基本类型的WPF触发器:属性触发器、数据触发器以及事件触发器。使用触发器是关联动画的最常用方式,但并不是唯一的选择。

1.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>        

2.效果图

在这里插入图片描述

十、同步的动画。

StoryBoard类间接地继承自TimeLineGroup类,所以StoryBoard类能包含多个动画,这些动画可以作为一组进行管理,这意味着它们可以在同一时间开始。

1.xaml代码

<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>

2.效果图

在这里插入图片描述

十一、控制播放。

到目前为止,已经在事件触发器中使用了一个动作,加载动画的BeginStoryboard动作,然而,一旦创建故事版,就可以使用其他动作控制故事板,这些动作类都继承自ControllableStoryboardAction类,控制故事版的主要类如下:

控制故事板的动作类
名称说明
PauseStoryboard停止播放动画并且保持当前位置
ResumeStoryboard恢复播放暂停的动画。
StopStoryboard停止播放动画,并将动画时钟重新设置到开始位置。
SeekStoryboard跳到动画时间线中的特定位置,如果当前动画正在播放,就继续从新位置播放。如果当前动画是暂停的,就继续保持暂停。
SetStoryboardSpeedRatio改变整个故事板的SpeedRatio属性值。
SkipStoryboardToFill将故事板移动到时间线的终点。FillBehavior属性设置为HoldEnd,动画继续保持最后的值。
RemoveStoryboard移除故事板,停止所有正在运行的动画,并将属性返回为原来的、最后一次设置的数值。

为成功地执行这些动作,必须在同一个Triggers集合中定义所有的触发器,如果将BeginStoryboard动作的触发器和PauseStoryboard动作的触发器放置到不同的集合中,PauseStoryboard动作就无法工作。

1.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>

2.效果图

在这里插入图片描述

分析:在包含元素的Triggers集合中(在这里是Window.Triggers集合),使用EventTrigger.SourceName属性关联这些事件触发器,只要SourceName属性和为按钮设置的Name属性相匹配,触发器就会用到恰当的按钮上。还必须要问BeginStoryboard动作指定名称,这样其他触发器BeginStoryboardName属性指定这个名称,连接到相同的故事板,然后进行控制。

十二、故事板事件。

故事板事件
Completed动画已经到达终点
CurrentGlobalSpeedInvalidated速度发生了变化,或者动画被暂停、重新开始、停止或移到某个新的位置。
CurrentStateInvalidated动画已经开始或结束。
CurrentTimeInvalidated动画时钟已经向前移动了一个步长,正在更改动画。当动画开始、停止或结束时也会引发该事件。
RemoveRequested动画正在被移除。

1.监视动画进度

如果要监视动画,要用到Storyboard的一些事件。在这里使用的是CurrentTimeInvalidated事件,每次向前移动动画时钟都会引发该事件。当引发CurrentTimeInvalidated事件时,发送者是Clock对象(Color类位于System.Windows.Media.Animation名称空间中),可通过Clock对象检索当前时间。当前时间使用TimeSpan对象表示,并且可检索当前进度,当前进度使用0~1之间的数值表示。

2.后台代码

就是在上面的例子中为故事板加一个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;
    }
}

3.效果图

在这里插入图片描述

十三、动画缓动

线性动画有一个缺点,通常让人觉得机械和不够自然。改进动画并创建更趋自然的动画的秘诀是改变变化速率。不是创建以固定不变的速率改变属性的动画,而是需要设计根据某种方式加速或减速的动画,实现更趋自然的动画的最简单方法是使用预置的缓动函数(EasingFunction)。EasyingFunction属性只能接受单个缓动函数对象,所以不能为同一个动画结合不同的缓动函数。

1.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>    

2.效果图

在这里插入图片描述

十四、缓动函数类。

在继续分析不同的缓动类之前,理解缓动函数的应用时机是很重要的。所有的缓动函数类都继承自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
PowerEasePower
ExponentialEaseExponent

十五、自定义缓动函数。

创建自定义缓动函数一般需要以下几个步骤:

a)、新建一个类,让其继承自EasingFunctionBase类。

b)、重写EaseInCore()方法和CreateInstanceCore()方法。

c)、定义依赖属性。

d)、引用。

1.后台代码(自定义类)

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;
        }
    }
}

2.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>

3.效果图

在这里插入图片描述

十六、WPF动画性能和帧率。

通常,为用户界面应用动画,只不过是创建并配置正确的动画和故事版对象。但在其他情况下,特别是同时发生多个动画时,可能更加需要关注性能。WPF试图保持以60帧/秒的速度进行动画,可以确保从开始到结束得到平滑流畅的动画。帧速率越低,会发生抖动现象。帧速率越高,占用的CPU也就越高。通过TimeLine.DesiredFrameRate属性进行调整。

1.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>

2.效果图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值