动画在 XAML 中也有,而且基本上与 WPF 中的用法一样。不过,在 UWP 中,动画还有一种表现方式—— 通过 UI Composition 来创建。

基于 UI Composition 的动画,相对于 XAML 动画,有以下优点:

1、不使用 UI 线程,XAML 动画是共享 UI 线程的,而 Composition 中的动画是使用辅助线程的。

2、Composition 动画支持表达式(计算公式)来产生动画,相对灵活。

老周的建议是:两者都用,因为基于 XAML 和基于 Composition 的动画各有特点,在应用程序中都可以混合来用。我们不要被一些不健康的思想所毒害,世界上没有什么技术可以取代和不取代,只要用得上,哪怕是 1000 年前的技术也同样适用(事实也表明有些东西我们现在科技这么发达竟然做不到,可咱们祖先在 N 千万年前反而能做到)。所以,我们应该向庄子先生学习,思维要灵活,合理应用一切可用的资源。

对于动画,不管是啥类型的,其实基本要素都一样,首先,动画是基于时间变化而产生的“眼球欺骗”技术,只是一个个帧随着时间变化不断改变,利用人眼的视觉延时误差,让我们觉得目标好像在动。其实,人看着在动,但是猫的眼睛看就不见得是这样了。故,动画会有时间线,可以说是动画的时长。

其次是值,比如,你要让绿色变成红色,那么在特定的时间点上,你就应该给一个颜色值;再比如,一只猪从屏幕左边滑到右边,那么在对应的时间上,你要给出一个坐标值,表明这头猪滑行了多长距离。

然后就是动画的作用目标,就是你要把动画应用到哪个对象的哪个属性上,要是想改变不透明度,就会选择应用到 K 对象的 Opacity 属性上。

在 Composition API 中,Visual 类的属性都支持动画,如 Offset,Size 等属性。

下面我们先介绍一种最经典的动画类型——关键帧。

所谓关键帧动画,就是在时间线上添加 N 个(N 肯定是有效数字)时间点,这些时间点会与一个目标值对应,当动画播放到这个关键帧时,会改变目标值。而关键帧之间的部分,就交给某些算法去计算过度动画。

举个例子,用关键帧动画改变某对象的 Opacity 属性(不透明度),时间线总长为 10 秒,在第 0 秒时设定值为 0,即全透明,然后,在第 5 秒时设定值为 0.5,即半透明,最后在第 10 秒处将值设定为 1,表示完全不透明。

下面咱们玩一个例子。

XAML 代码如下。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Canvas>
<Image Name="img" Height="200" Source="Assets/1.png"/>
</Canvas>
<StackPanel Grid.Row="1" Margin="8" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="开始" Click="OnStart"/>
<Button Content="停止" Margin="20,0,0,0" Click="OnStop"/>
</StackPanel>
</Grid>

由于老周比较穷,所以界面放的东西不多。Image 控件用来显示多拉 B 梦的照片,然后,对,下面两个按钮,一个用来启动播放动画,另一个用来停止动画。

Image 为啥要放到 Canvas 容器中呢,因为这个容器,你懂的,它是绝对定位。如果是一个 Grid,可能会受到对齐方式的影响,这样后面我们要对这个对象的位置进行修改时就很不好弄。

切换到代码视图,在页面类中声明两个变量。

        Vector3KeyFrameAnimation Animation = null;
Visual imageVs = null;

之所以在类级别声明它们,因为稍后要用。这里,Vector3KeyFrameAnimation 表示关键帧动画是针对 Vector3 这种值进行处理的,待会我们要让 Image 控件中的 多拉 B 梦 移动。通过老周前面的介绍,大伙应该记得,Offset 属性表示对象的位置,它有三个值:X、Y、Z,所以,我们要用 Vector3 而不是 Vector2,Vector2 只有两个值,适用于 Size 属性。

如果你要对颜色做动画处理,那就用 ColorKeyFrameAnimation,道理一样,它使用的值就是 Color 结构类型。如果你进行动画处理的目标属性只有一个值,比如 Opacity ,只是一个 float 值,那么,你就可以选用 ScalarKeyFrameAnimation。

在页面的构造函数中,我们初始化一下各个对象。

        public MainPage()
{
this.InitializeComponent(); // 获取可视化对象
imageVs = ElementCompositionPreview.GetElementVisual(img);
var compos = imageVs.Compositor;
// 创建关键帧动画
Animation = compos.CreateVector3KeyFrameAnimation();
// 时长为 4 秒
Animation.Duration = TimeSpan.FromSeconds(4d);
// 插入关键帧
Animation.InsertKeyFrame(0f, new Vector3(0f, 0f, 0f));
Animation.InsertKeyFrame(0.5f, new Vector3(500f, 360f, 30f));
Animation.InsertKeyFrame(0.7f, new Vector3(260f, 125f, 45f));
Animation.InsertKeyFrame(1f, new
Vector3(20f, 20f, 60f));
}

老周在前面的博文中说过,Composition 要用到的各种资源,都可以通过 Compositor 实例的 CreateXXX 方法来创建,动画也是如此。关键帧动画一定要记得添加关键帧,InsertKeyFrame 方法的第一个参数是关键帧在时间线上的位置,注意,它采用的是相对值(百分比),从 0.0 到 1.0,如果是 1 则表示关键帧在时间线 100% 处,如果是 0.5,关键帧正好位于时间线中央。

插入关键帧时要记得,它是用百分比来计算的。另外,不要忘了设置一下 Duration 属性,就是动画时间线的长度。

接下来,处理一下那两个按钮的 Click 事件,分别启动和停止动画。

        private void OnStart(object sender, RoutedEventArgs e)
{
imageVs?.StartAnimation(nameof(Visual.Offset), Animation);
} private void OnStop(object sender, RoutedEventArgs e)
{
imageVs?.StopAnimation(nameof(Visual.Offset));
}

要让动画对象与目标属性关联,可以调用可视化对象的 StartAnimation 方法,第一个参数要指定要应用到的属性名字,本示例是应用到 Offset 属性上。要停止正在播放的动画,只需要把属性名传给 StopAnimation 方法即可。

一起来看看效果,多拉B梦在家里经常这样锻炼身体的。

由于 gif 动画的帧率问题,所以你看到截图上的动画是不流畅的,想实际体验就自己动手吧。

下面,老周再给大伙伴们演示一个基于颜色值的动画。

XAML 代码很简单,就放一个 Canvas 就行了。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Canvas Name="cvs"/>
</Grid>

然后转到页面代码,初始化一下动画。

        public MainPage()
{
this.InitializeComponent(); Visual cvsv = ElementCompositionPreview.GetElementVisual(cvs);
Compositor compos = cvsv.Compositor;
// 创建颜色关键帧动画
ColorKeyFrameAnimation animat = compos.CreateColorKeyFrameAnimation();
// 时间长度
animat.Duration = TimeSpan.FromSeconds(6d);
// 让它永远循环播放
animat.IterationBehavior = AnimationIterationBehavior.Forever;
// 插入关键帧
animat.InsertKeyFrame(0f, Colors.Red);
animat.InsertKeyFrame(0.6f, Colors.Blue);
animat.InsertKeyFrame(1f, Colors.Yellow);
// 颜色变化模式
animat.InterpolationColorSpace = CompositionColorSpace.Rgb;
// 创建颜色画刷
CompositionColorBrush brush = compos.CreateColorBrush(Colors.Black);
// 创建可视化对象
SpriteVisual sv = compos.CreateSpriteVisual();
// 设置大小和位置
sv.Size = new Vector2(360f, 250f);
sv.Offset = new Vector3(150f, 140f, 0f);
// 关联画刷
sv.Brush = brush;
// 把可视化对象插入 XAML 可视化树
ElementCompositionPreview.SetElementChildVisual(cvs, sv);
// 启动动画
brush.StartAnimation(nameof(CompositionColorBrush.Color), animat);
}

代码比较长,但有些我前面文章中已经介绍过,我们重点看这段。

            // 创建颜色关键帧动画
ColorKeyFrameAnimation animat = compos.CreateColorKeyFrameAnimation();
// 时间长度
animat.Duration = TimeSpan.FromSeconds(6d);
// 让它永远循环播放
animat.IterationBehavior = AnimationIterationBehavior.Forever;
// 插入关键帧
animat.InsertKeyFrame(0f, Colors.Red);
animat.InsertKeyFrame(0.6f, Colors.Blue);
animat.InsertKeyFrame(1f, Colors.Yellow);
// 颜色变化模式
animat.InterpolationColorSpace = CompositionColorSpace.Rgb;

首先,当然要创建基于颜色的关键帧动画对象,然后设置一下参数,插入关键帧相信你都会了,跟前面那个多拉B梦移动的例子差不多,只是值的类型变成 Color 值而已。

IterationBehavior 属性用来设置动画的循环次数,如果你设置为 Count,那么,就要为动画的 IterationCount 属性指定一个数值,比如3表示播放三次。这里我设置为 Forever,表示动画永久循环播放。

InterpolationColorSpace 属性是个很好玩的东西,主要设置颜色在进行动画过程如何过度。它用 CompositionColorSpace 枚举来规范几个值。经过测试发现,貌似使用 RGB 形式动画比较正常, RgbLinear 会发生错误,但 Rgb 是正常的,所以我就选用 Rgb 模式了。

最后在启动动画时要注意,动画的作用是改变颜色,所以它的应用对象应该是画刷 CompositionColorBrush 的 Color 属性,所以,调用 StartAnimation 方法应该在画刷对象上,而不是 SpriteVisual 对象。

来,看看效果吧。

接下来,我们看一下跳跃式动画。所谓跳跃式动画,就是它可以模仿弹簧的物理特性,在动画停止之前有一个回弹的动作。这个动画用在控件特效很不错。

下面我们来个弹球球的实验。

首先,我们在 XAML 中放一个蓝色的球。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Canvas>
<Ellipse Name="ell" Width="100" Height="100" Fill="Blue" Canvas.Top="40" Canvas.Left="20"/>
</Canvas>
</Grid>

随后,转到代码视图,输入以下代码。

        public MainPage()
{
this.InitializeComponent(); Visual ellVisual = ElementCompositionPreview.GetElementVisual(ell);
var compositor = ellVisual.Compositor;
var springAnmt = compositor.CreateSpringScalarAnimation();
springAnmt.InitialValue = 0f;
springAnmt.FinalValue = 400f;
springAnmt.Period = TimeSpan.FromMilliseconds(60d);
springAnmt.DampingRatio = 0.2f;
springAnmt.StopBehavior =
AnimationStopBehavior.SetToInitialValue;

Windows.System.Threading.ThreadPoolTimer.CreateTimer(timer =>
{
ellVisual?.StartAnimation("Offset.X", springAnmt);
}, TimeSpan.FromSeconds(3d));
}

InitialValue 和 FinalValue 属性分别用于指定动画的初始值和最终值。如果不指定初始值,那就默认使用当前的值作为初始值。这里有两个属性我们要重点关注的。第一个是 DampingRatio ,它是一个大于 0 的值,它表示对象在完成动画时振动的衰减程度,就像一个球,它落到地面上会弹起来,可是,它不可能永远都在那里弹,可能弹几下它就落地不动了。弹性势能会不断地衰减。

如果你把 DampingRatio 属性设置为 0 ,那么,物体就会不停地在弹,而且振幅很大,这是不符合现实物理现象的,因此,这个值你不能用0,一般是用大于0小于1之间,如果大于/等于1,物体几乎不会振动,非但不振动,反而速度会逐渐变慢。所以,这个 DampingRatio 属性值,当值小于 1 时,就像在弹簧上弹起来,而当其大于或等于 1 时,就等同于用手按弹簧,越往下按,阻力越大。

还有一个属性,是配合 DampingRatio 使用的,它就是 Period,它表示每一轮振动的时间,时间越短,物体振动就越快。

本例的设置如下。

  springAnmt.Period = TimeSpan.FromMilliseconds(60d);
springAnmt.DampingRatio = 0.2f;

表示振动周期为 60 毫秒,振动衰减系数为 0.2,这个值振感明显,但不会振个不停。

看看效果吧。

其他的跳跃式动画的用法也一样,本例所针对的值是可视化对象的Offset 属性的 X 值,所以是单个 float 值,因此使用 SpringScalarNaturalMotionAnimation。如果处理动画的目标是其他复杂的值,可以用 SpringVector2NaturalMotionAnimation 或 SpringVector3NaturalMotionAnimation,用法都是一样的,我就不废话了,有兴趣的伙伴可以试试。

本文最后,我们看一下隐式动画。啥叫隐式动画?就是你不必调用 StartAnimation 方法来启动动画,当一些支持动画的属性更改时,会自动产生动画。比如,Visual 类的属性基本支持动画,像 Opacity、Offset、Orientation、Size 等。

Composition 对象都从 CompositionObject 类上继承了一个叫 ImplicitAnimations属性,它是一个集合,我们可以将多个动画对象加进去,然后,当指定的对象属性更改时,会自动产生动画。

ImplicitAnimationCollection 集合是以字典数据形式来存储的,Key 是要进行动画处理的属性名,Value 是对应的动画实例。这里你可以用关键帧动画,或者上面讲到过的跳跃式动画都可以。为了最大限度保证动画的兼容性,隐式动画会存在一定的自动转换功能。比如,一个针对 Vector3 的动画可以用于 Vector2 值的属性,它会从X,Y,Z中取两个值来填充 Vector2 值。

下面,我们还是用示例来说明吧。我们在 XAML 中放一个物体。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Canvas>
<Ellipse Name="ell" Fill="Green" Width="150" Height="150"/>
</Canvas>
<StackPanel Grid.Row="1" Margin="2,12" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="动作 1" Margin="0,0,24,0" Click="OnClick1"/>
<Button Content="动作 2" Margin="0,0,24,0" Click="OnClick2"/>
<Button Content="动作 3" Margin="0,0,24,0" Click="OnClick3"/>
<Button Content="动作 4" Click="OnClick4" />
</StackPanel>
</Grid>

下面的四个按钮的作用是修改上面那个圆的 Offset,Opacity 属性,说白了,就是修改它的位置和不透明度。

现在,我们转到代码视图,先在类级别声明一个 Visual 类型的变量,它表示上面的 Ellipse 对象的可视化对象引用,应用我们在四个按钮的 Click 事件处理代码中要访问它,所以把其作为类级别的字段。

Visual ell_vs;

然后,在页面类的构造函数中初始化。

        public MainPage()
{
this.InitializeComponent(); // 设置动画
ell_vs = ElementCompositionPreview.GetElementVisual(ell);
Compositor compos = ell_vs.Compositor;
ImplicitAnimationCollection implicitAnmts = compos.CreateImplicitAnimationCollection(); ScalarKeyFrameAnimation opacityAnmt = compos.CreateScalarKeyFrameAnimation();
opacityAnmt.InsertExpressionKeyFrame(0f, "this.StartingValue");
opacityAnmt.InsertExpressionKeyFrame(1f, "this.FinalValue"
);
opacityAnmt.Duration = TimeSpan.FromSeconds(1d);
opacityAnmt.Target = nameof(Visual.Opacity);

Vector3KeyFrameAnimation offsetAnmt = compos.CreateVector3KeyFrameAnimation();
offsetAnmt.InsertExpressionKeyFrame(0f, "this.StartingValue");
offsetAnmt.InsertExpressionKeyFrame(1f, "this.FinalValue"
);
offsetAnmt.Duration = TimeSpan.FromSeconds(1d);
offsetAnmt.Target = nameof(Visual.Offset);

implicitAnmts.Add(nameof(Visual.Offset), offsetAnmt);
implicitAnmts[nameof(Visual.Opacity)]
= opacityAnmt;

ell_vs.ImplicitAnimations = implicitAnmts;
}

请注意,在为动画插入关键帧时,使用的是表达式的方法,因为我们后面是对对象的不透明度和位置进行动态调整,所以,这里的代码并不能准确知道动画的最终值是什么,所以,使用了这两个关键字:

this.StartingValue:表示动画的初始值,它会根据实际情况自动填充值。

this.FinalValue:指的是动画的最终值,它会自动填充。

在这个例子中,StartingValue 就是对象上一次被修改后的值,比如,第一次把 Opacity 改为 0.5,那么下一轮动画时的初始就是这个 0.5。FinalValue就是属性的最新值,比如Opacity 原来是 1,现在你改为 0.6,那么对本次动画来说,StartingValue 就是 1,FinalValue 就是 0.6 了。

对了,还有一点,你得为动画的 Target 属性赋值,比如动画是作用于 Opacity 属性上的,就赋 Opacity 。这个隐式动画比较特殊,一定要这样赋值。

然后,我们给四个按钮弄弄 Click 事件。

        private void OnClick1(object sender, RoutedEventArgs e)
{
ell_vs.Opacity = 0.2f;
ell_vs.Offset = new Vector3(300f, 250f, -30f);
} private void OnClick2(object sender, RoutedEventArgs e)
{
ell_vs.Opacity = 0.8f;
ell_vs.Offset = new Vector3(400f, 320f, 130f);
} private void OnClick3(object sender, RoutedEventArgs e)
{
ell_vs.Opacity = 1f;
ell_vs.Offset = new Vector3(150f, 60f, -70f);
} private void OnClick4(object sender, RoutedEventArgs e)
{
ell_vs.Offset = new Vector3(20f, 200f, 50f);
ell_vs.Opacity = 0.5f;
}

好,现在可以看效果了。运行应用,分别点四个按钮,看看它们这样修改对象的属性会不会更生动。

好了,本文就讲到这里吧。

你一定会记得,还有一个表达式动画,那个咱们留到下一篇文章再聊,本篇就先聊到这里。示例的代码我都基本贴上了,所以我就不上传示例了。

 

【Win 10 应用开发】UI Composition 札记(六):动画的更多相关文章

  1. 【Win 10 应用开发】UI Composition 札记(一):视图框架的实现

    在开始今天的内容之前,老周先说一个问题,这个问题记得以前有人提过的. 设置 Windows.ApplicationModel.Core.CoreApplicationView.TitleBar.Ext ...

  2. 【Win 10 应用开发】UI Composition 札记(三):与 XAML 集成

    除了 DirectX 游戏开发,我们一般很少单独使用 UI Composition ,因此,与 XAML 互动并集成是必然结果.这样能够把两者的优势混合使用,让UI布局能够更灵活. 说到与 XAML ...

  3. 【Win 10 应用开发】UI Composition 札记(二):基本构件

    在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”. UI Composition 有一个核心类——对,就是 C ...

  4. 【Win 10 应用开发】UI Composition 札记(四):绘制图形

    使用 Win 2D 组件,就可以很轻松地绘制各种图形,哪怕你没有 D2D 相关基础,也不必写很复杂的 C++ 代码. 先来说说如何获取 Win 2D 组件.很简单,创建 UWP 应用项目后,你打开“解 ...

  5. 【Win 10 应用开发】UI Composition 札记(五):灯光

    UI Composition 除了能够为 UI 元素建立三维空间外,还有相当重要的一个部件——灯光.宇宙万物的精彩缤纷,皆源于光明,光,使我们看到各种东西,除了黑洞之外的世界都是五彩斑谰的.故而,真要 ...

  6. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  7. 【Win 10应用开发】认识一下UAP项目

    Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...

  8. 【Win 10 应用开发】在代码中加载文本资源

    记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...

  9. 【Win 10 应用开发】导入.pfx证书

    这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...

随机推荐

  1. codesmith连接Mysql提示“找不到请求的 .Net Framework Data Provider。可能没有安装。"

    1,首先需要将MySql.Data.dll复制到codesmith安装目录下bin文件夹下,注意dll的版本 2,其次因为codesmith7采用的是.net4.0的配置文件,(64位系统)找到C:\ ...

  2. python安装(python2.7)

    1.下载python 进入官网下载安装 点击打开链接(官网地址:https://www.python.org/downloads/),进入官网后根据自己需求选择python2 或者 python3 2 ...

  3. Spark SQL数据源

    [TOC] 背景 Spark SQL是Spark的一个模块,用于结构化数据的处理. ++++++++++++++ +++++++++++++++++++++ | SQL | | Dataset API ...

  4. 别指望一文读懂Java并发之从一个线程开始

    Understanding concurrent programming is on the same order of difficulty as understanding object-orie ...

  5. c语言15行实现简易cat命令

    刚刚和舍友打赌.舍友说PY20行能做xlsx文件分析整理,C20行屁都干不了.我说简单的cat还是能做的嘛.他说不信.我说不处理非文件的参数的话10行能做啊. 下面直接贴代码吧: #include & ...

  6. 使用angular4和asp.net core 2 web api做个练习项目(一)

    这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级. 我认为angular 4还是很适合企业的, 就像.net一样. 我用的是windows 10 安装工具: git for ...

  7. 个性化推荐系统(七)--- ABTest ab测试平台

    个性化推荐系统.搜索引擎.广告系统,这些系统都需要在线上不断上线,不断优化,优化之后怎么确定是好是坏.这时就需要ABTest来确定,最近想的办法.优化的算法.优化的逻辑数据是正向的,是有意义的,是提升 ...

  8. IIS部署网站时常见问题解决

    首先服务器上安装IIS和Framework\v4.0 一.打开iis服务管理器 左侧目录中选择网站右键,选择添加网站 填写网站名称.选择项目存放的路径.ip地址和端口 VS用的是4.0,iis中网站也 ...

  9. PyCharm 如何安装python第三方库及插件

    一.如何安装python第三方库: 1.有一个专门可下载安装第三方库的网址: http://www.lfd.uci.edu/~gohlke/pythonlibs/ Ctrl+f 搜索要下载的第三方库, ...

  10. 【Spring】渲染Web视图

    前言 前面学习了编写Web请求的控制器,创建简单的视图,本篇博文讲解控制器完成请求到结果渲染到用户的浏览器的过程. 渲染Web视图 理解视图解析 前面所编写的控制器方法都没有直接产生浏览器中渲染所需要 ...