WPF动画

简介: 原文:WPF动画1、基本动画。 WPF提供了一个更高级的模型,通过该模型可以只关注动画的定义,而不考虑它们的渲染方式,这个模型基于依赖项属性基础架构,本质上,WPF动画只不过是在一段时间间隔内修改依赖项属性值的一种方式。
原文: WPF动画

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
DecelerationRatio

使动画不是线性的,从而开始时较慢,或者开始时较快,这两个属性的值都在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>

 

效果图:

 

目录
相关文章
|
4月前
|
C#
WPF中动画教程(DoubleAnimation的基本使用)
WPF中动画教程(DoubleAnimation的基本使用)
83 0
|
3月前
|
算法 C# Windows
不可不知的WPF动画(Animation)
【9月更文挑战第19天】在 WPF(Windows Presentation Foundation)中,动画能为应用程序增添生动性和交互性。主要类型包括线性动画和关键帧动画,可针对依赖属性和自定义属性操作。触发方式有事件触发和自动触发,支持暂停、恢复及停止控制。合理使用这些功能并注意性能优化,可创建引人入胜的用户界面。
|
4月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
290 1
|
4月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
218 0
|
4月前
|
存储 C# 数据格式
WPF动画教程(PointAnimationUsingPath的使用)
WPF动画教程(PointAnimationUsingPath的使用)
56 0
|
7月前
|
C#
WPF —— 动画缩放变换
`ScaleTransform`用于二维x-y坐标系中对象的缩放,可沿X或Y轴调整。在故事板中,通过RenderTransform.ScaleX和ScaleY属性控制缩放。示例代码展示了如何设置按钮的RenderTransformOrigin、Background等属性,并通过LayoutTransform应用ScaleTransform。当鼠标进入按钮时,EventTrigger启动DoubleAnimation实现X和Y轴的缩放动画。最后,展示了如何将动画集成到自定义按钮样式中。
188 0
|
C#
WPF技术之动画系列-上下运动
本例子展现动画小球上下循环运动
226 0
|
C#
WPF特效-鱼游动动画2
原文:WPF特效-鱼游动动画2           纯代码撸动画实践2:           原图:(png格式)                                                添加Effect以及Effect动画处理后Gif效果:                                     处理: 眼部放大缩小动画; 嘴缩放动画; 尾部收缩动画;  颜色变化效果动画。
1065 0
|
C#
WPF编游戏系列 之六 动画效果(1)
原文:WPF编游戏系列 之六 动画效果(1)        本篇主要针对界面进行动画效果处理。首先在打开或关闭界面时,使其产生动态效果而不是生硬的显示或消失(如下图)。其次在鼠标放到关闭窗口图标上时,使其出现闪动效果。
700 0
|
C# 前端开发
WPF编游戏系列 之七 动画效果(2)
原文:WPF编游戏系列 之七 动画效果(2)        上一篇已经对关闭窗口图标进行了动画效果处理,本篇将对窗口界面的显示和关闭效果进行处理。由于所有的动画效果都是针对窗口界面的Canvas,所以先要为它添加一些RenderTranform属性,这些属性不要填写任何效果,后面会由Storyboard和EventTrigger为其提供。
673 0