到目前为止,看到的所有动画都使用线性插值从起点到终点。但如果需要创建具有多个分段的动画和不规则移动的动画。例如,可能希望创建一个动画,快速地将一个元素滑入到视图中,然后慢慢地将它移到正确位置。可通过创建两个连续的动画,并使用BeginTime属性在第一个动画之后开始第二个动画来实现这种效果。然而,还有更简单的方法——可使用关键帧动画。

  关键帧动画是由许多较短的段构成的动画。每段表示动画中的初始值,最终值或中间值当运行动画时,它平滑地从一个值移到另一个值。

  例如,分析下面的将RadialGradientBrush画刷的中心点从一个位置移到另一个位置的Point动画:

<PointAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin"
From="0.7,0.3" To="0.3,0.7" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever">
</PointAnimation>

  可使用一个效果相同的PointAnimationUsingKeyFrames对象代替这个PointAnimation对象,如下所示:

<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin" AutoReverse="True" RepeatBehavior="Forever"> 
  <LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame> 
  <LinearPointKeyFrame Value="0.3,0.7" KeyTime="0:0:10"></LinearPointKeyFrame>
</PointAnimationUsingKeyFrames>

  这个动画包含两个关键帧。当动画首次启动时第一个关键帧设置Point值(如果希望使用在RadialGradientBrush画刷中设置的当前值,可省略这个关键帧)。第二个关键帧定义结束值,这是10秒之后达到的数值。PointAnimationUsingKeyFrames对象执行线性插值。从第一个关键帧平滑移到第二个关键帧,就像PointAnimation对象对From和To值执行的操作一样。

  可使用一系列关键帧创建更有趣的示例。下面的动画通过在不同的时刻到达的一系列位置经历中心点。中心点的移动速度根据关键帧之间的持续时间以及需要移动的距离而改变。

<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin"
RepeatBehavior="Forever">
<LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.3,0.7" KeyTime="0:0:5"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></LinearPointKeyFrame>
</PointAnimationUsingKeyFrames>

  这个动画不是可反转的,但可以重复。为确保在一次迭代的最后数据和下一次迭代的开始数值之间不会出现跳跃,应使动画的结束点和开始点位于相同的中心点。

一、离散的关键帧动画

  上面示例中的关键帧动画使用线性关键帧。所以,它在关键帧值之间平滑地过渡,另一种选择是使用离散的关键帧。对于这种情况,不进行插值。当到达关键时间时,属性突然改变为新值。

  线性关键帧类使用“Linear+数据类型+KeyFrame”的形式进行命名。离散关键帧类使用“Discrete+数据类型+KeyFrame”的形式命名。下面是RadialGradientBrush画刷示例的修改版本,在该修改版本中使用的是离散关键帧:

<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin"
RepeatBehavior="Forever">
<DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></DiscretePointKeyFrame>
<DiscretePointKeyFrame Value="0.3,0.7" KeyTime="0:0:5"></DiscretePointKeyFrame>
<DiscretePointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></DiscretePointKeyFrame>
<DiscretePointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></DiscretePointKeyFrame>
<DiscretePointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></DiscretePointKeyFrame>
<DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></DiscretePointKeyFrame>
</PointAnimationUsingKeyFrames>

  当运行这个动画时,中心点在适当的时间从一个位置跳到下一个位置。这是戏剧性的(但是不平稳的)效果。

  所有关键帧动画类都支持离散关键帧,但只有一部分关键帧动画类支持线性关键帧。这完全取决于数据类型。支持线性关键帧的数据类型也支持线性插值,并提供了相应的DataTypeAnimation类,如Point、Color以及double。不支持线性插值的数据类型包括字符串和对象。

二、缓动关键帧

  通过“【WPF学习】第五十一章 动画缓动 ”的学习,看到了如何使用缓动函数改进普通动画。尽管关键帧动画被分割成多段,但每段仍使用普遍的、令人厌烦的线性插值。

  如果这不是希望的结果,可使用缓动函数为每个关键帧添加加速和减速的效果。然而,普通的线性插值关键帧类和离散关键帧类不支持该特征。相反,需要使用缓动关键帧,如EasingDoubleKeyFrame、EasingColorKeyFrame或EasingPointKeyFrame。每个缓动关键帧类和对应的线性插值关键帧类的工作方式相同,但是额外提供了EasingFunction属性。

  下面的示例使用动画缓动为前5秒得关键帧动画应用加速效果:

<PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientOrigin"
RepeatBehavior="Forever">
<LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:0"></LinearPointKeyFrame>
<EasingPointKeyFrame Value="0.3,0.7" KeyTime="0:0:5">
<EasingPointKeyFrame.EasingFunction>
<CircleEase></CircleEase>
</EasingPointKeyFrame.EasingFunction>
</EasingPointKeyFrame>
<LinearPointKeyFrame Value="0.5,0.9" KeyTime="0:0:8"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.9,0.6" KeyTime="0:0:10"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.8,0.2" KeyTime="0:0:12"></LinearPointKeyFrame>
<LinearPointKeyFrame Value="0.7,0.3" KeyTime="0:0:14"></LinearPointKeyFrame>
</PointAnimationUsingKeyFrames>

  结合使用关键帧和动画缓动是构建复杂动画模型的简便方式,但仍可能无法提供所需的控制。不使用动画缓动,可创建数学公式指示动画的进度。

三、样条关键帧动画

  还有一种关键帧类型:样条关键帧。每个支持线性关键帧的类也支持样条关键帧,它们使用“Spline+数据类型+KeyFrame”的形式进行命名。

  与线性关键帧一样,样条关键帧使用插值从一个键值平滑地移到另一个键值。区别是每个样条关键帧都是KeySpline属性。可使用该属性定义能影响插值方式的三次贝塞尔曲线。尽管为了得到希望的效果这样做有些繁琐,但这种技术能创建更加连贯的加速和减速以及更逼真的动画效果。

  在前面章节学习过,贝塞尔曲线由起点、终点以及两个控制点定义。对于关键样条,起点总是(0,0),终点总是(1,1)。用户只需要提供两个控制点。创建的曲线描述了时间(X轴)和动画值(Y值)之间的关系。

  下面的示例通过对比Canvas面板上两个椭圆的移动,演示了一个关键帧样条动画。第一个椭圆使用DoubleAnimation动画缓慢匀速地再窗口上移动。第二个椭圆使用具有两个SplineDoubleKeyFrame对象的DoubleAnimationUsingKeyFrames动画。两个椭圆同时到达目的位置(10秒后),但第二个椭圆在运动过程中会有明显的加速和减速,减速时会超过第一个椭圆而减速时又会落后于第一个椭圆。

 <DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" >
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="250" KeySpline="0.25,0 0.5,0.7"></SplineDoubleKeyFrame>
<SplineDoubleKeyFrame KeyTime="0:0:10" Value="500" KeySpline="0.25,0.8 0.2,0.4"></SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames> <DoubleAnimation
Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)"
To="500" Duration="0:0:10">
</DoubleAnimation>

  最快的加速发生在5秒后不久,也就是当进入第二个SplineDoubleKeyFrame关键帧时。贝塞尔曲线的第一个控制点将较大的表示动画进度(0.8)的Y轴值与较小的表示时间的X轴值相匹配。所以,在再次减慢速度前,椭圆在一小段距离内会增加速度。

  下图以图形方式显示了两条控制椭圆运动的曲线。为理解这些曲线,请记住它们从顶部到底部描述了动画过程。观察第一条曲线可以发现,它相对均匀地下降,在开始处有较短的暂停,在末尾处平缓下降。然而第二条曲线快速下降,运动了一个大段距离,然后对于剩余的动画部分,曲线缓缓下降。

  示例的完整XAML标记如下所示:

<Window x:Class="Animation.KeySplineAnimation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="KeySplineAnimation" Height="300" Width="624" WindowStartupLocation="CenterScreen">
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Canvas.Left)" >
<SplineDoubleKeyFrame KeyTime="0:0:5" Value="250" KeySpline="0.25,0 0.5,0.7"></SplineDoubleKeyFrame>
<SplineDoubleKeyFrame KeyTime="0:0:10" Value="500" KeySpline="0.25,0.8 0.2,0.4"></SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames> <DoubleAnimation
Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Canvas.Left)"
To="500" Duration="0:0:10">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
<Canvas Margin="10">
<Ellipse Name="ellipse1" Canvas.Left="0" Fill="Red" Width="10" Height="10"></Ellipse> <Path Stroke="Blue" StrokeThickness="1" StrokeDashArray="2 1" Canvas.Top="25">
<Path.Data>
<PathGeometry>
<PathFigure>
<BezierSegment Point1="25,0" Point2="50,70" Point3="100,100" />
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.RenderTransform>
<ScaleTransform ScaleX="2.5"></ScaleTransform>
</Path.RenderTransform>
</Path>
<Path Stroke="Blue" StrokeThickness="1" StrokeDashArray="2 1" Canvas.Left="250" Canvas.Top="25">
<Path.Data>
<PathGeometry>
<PathFigure>
<BezierSegment Point1="25,80" Point2="20,40" Point3="100,100" />
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.RenderTransform>
<ScaleTransform ScaleX="2.5"></ScaleTransform>
</Path.RenderTransform>
</Path> <Ellipse Name="ellipse2" Canvas.Top="150" Canvas.Left="0" Fill="Red" Width="10" Height="10"></Ellipse>
</Canvas>
</Window>

KeySplineAnimation

【WPF学习】第五十四章 关键帧动画的更多相关文章

  1. 【WPF学习】第十四章 事件路由

    由上一章可知,WPF中的许多控件都是内容控件,而内容控件可包含任何类型以及大量的嵌套内容.例如,可构建包含图形的按钮,创建混合了文本和图片内容的标签,或者为了实现滚动或折叠的显示效果而在特定容器中放置 ...

  2. 【WPF学习】第二十四章 基于范围的控件

    WPF提供了三个使用范围概念的控件.这些控件使用在特定最小值和最大值之间的数值.这些控件——ScrollBar.ProgressBar以及Slider——都继承自RangeBase类(该类又继承自Co ...

  3. “全栈2019”Java第五十四章:多态详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. C++ Primer Plus学习:第十四章

    第十四章 C++中的代码重用 包含对象成员的类 将类的对象作为新类的成员.称为has-a关系.使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口).使用包含时,可以获得实 ...

  5. 【WPF学习】第二十六章 Application类——应用程序的生命周期

    在WPF中,应用程序会经历简单的生命周期.在应用程序启动后,将立即创建应用程序对象,在应用程序运行时触发各种应用程序事件,你可以选择监视其中的某些事件.最后,当释放应用程序对象时,应用程序将结束. 一 ...

  6. 《机器学习实战》学习笔记第十四章 —— 利用SVD简化数据

    相关博客: 吴恩达机器学习笔记(八) —— 降维与主成分分析法(PCA) <机器学习实战>学习笔记第十三章 —— 利用PCA来简化数据 奇异值分解(SVD)原理与在降维中的应用 机器学习( ...

  7. 【WPF学习】第二十九章 元素绑定——将元素绑定到一起

    数据banding的最简单情形是,源对象时WPF元素而且源属性是依赖性属性.前面章节解释过,依赖项属性具有内置的更改通知支持.因此,当在源对象中改变依赖项属性的值时,会立即更新目标对象中的绑定属性.这 ...

  8. 学习笔记 第十四章 使用CSS3动画

    第14章   使用CSS3动画 [学习重点] 设计2D动画 设计3D动画 设计过渡动画 设计帧动画 能够使用CSS3动画功能设计页面特效样式 14.1  设计2D动画 CSS2D Transform表 ...

  9. AI - 深度学习之美十四章-概念摘要(8~14)

    原文链接:https://yq.aliyun.com/topic/111 本文是对原文内容中部分概念的摘取记录,可能有轻微改动,但不影响原文表达. 08 - BP算法双向传,链式求导最缠绵 反向传播( ...

随机推荐

  1. KMP匹配(模板)

    先粘上我入门KMP时看的大佬的博客:orz orz 从头到尾彻底理解KMP 我觉得这篇已经讲的很详细了,希望大家能坚持看下去. 步骤 ①寻找前缀后缀最长公共元素长度对于P = p0 p1 ...pj- ...

  2. springboot FreeMarker template error

    注释掉<#list>xxx</#list> 现在运行就不报错了

  3. LeetCode No.118,119,120

    No.118 Generate 杨辉三角 题目 给定一个非负整数 numRows,生成杨辉三角的前 numRows 行. 在杨辉三角中,每个数是它左上方和右上方的数的和. 示例 输入: 5 输出: [ ...

  4. 2013 ACM网络搜索与数据挖掘国际会议

    ACM网络搜索与数据挖掘国际会议" title="2013 ACM网络搜索与数据挖掘国际会议"> 编者按:ACM网络搜索与数据挖掘国际会议(6th ACM Conf ...

  5. 基于 maven 的ssm 框架搭建

    1.新建一个 maven 工程, war 包 2.引入 pom 文件(springmvc+spring+mybatis) 3.引入配置文件 4.引入页面,编写 contorller 层测试 5.编写查 ...

  6. Div水平垂直居中的三种方法

    百度了很多种方法,很多是不起作用的.下面这些方法是我亲自试过的.希望对大家有帮助! 下面是一波代码水军. <!DOCTYPE html> <html lang="en&qu ...

  7. java成神之路

    一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 http://i ...

  8. 作业:for循环,迭代法和穷举法

                                                    for()循环 四要素:初始条件,循环条件,状态改变,循环体. 执行过程:初始条件--循环条件--循环体 ...

  9. 由情感计算带来的惊喜发现——记Rosalind W. PICARD“21世纪的计算”大会主题演讲

    W. PICARD"21世纪的计算"大会主题演讲" title="由情感计算带来的惊喜发现--记Rosalind W. PICARD"21世纪的计算& ...

  10. Jump Game (Medium)

    主要有两种思路: 一. 本题只需要判断能否到达最后一个数,那么可以采用贪心策略,到达某个位置i后,在直接在这个位置的基础上走nums[i]步,主要保证能一直前进,就能达到终点: 那么,什么时候才不能一 ...