8.1 动画方案的选择

Windows Phone的动画实现方式有线性插值动画(3种类型)、关键祯动画(4种类型)和基于帧动画,甚至还有定时器动画,然后动画所改变的UI元素属性可以是普通的UI元素属性,变换特效属性和三维特效属性,面对着这么多的选择,我们要实现一个动画效果该怎么去思考动画实现的思路以及怎么选择实现的技术呢?那么我们这小节会先讲解与动画性能相关的知识,然后再讲解怎么去选择动画的实现方案。

8.1.1 帧速率

帧速率是用于测量显示帧数的量度,测量单位为“每秒显示帧数”(Frame per Second,FPS,帧率)或“赫兹”,是指每秒钟刷新的画面的帧数,也可以理解为图形处理器每秒钟能够刷新几次。由于人类眼睛的特殊生理结构,如果所看画面之帧率高于每秒约10-12帧的时候,就会认为是连贯的。对于动画而言,帧速率常用于衡量动画的流畅度,帧速率的数字越大表示动画的流畅度越高。在实现Windows Phone动画的时候,我们是不能够直接指定动画的帧速率的,动画的帧速率是由系统自动分配的,当手机的性能越好,程序的性能越好,那么动画的帧速率就越大,反之就越小。所以要判断一个动画是否能够流畅地运行,我们需要关注动画的帧速率指标是否足够高。

在Windows Phone里面虽然不能够直接设置动画的帧速率,但是可以测量出来。当在Windows Phone模拟器中运行应用时,可以使用帧速率计数器来监控应用的性能和动画的效率,模拟器的效果图如图8.1所示,每一个帧速率计数器的作用如表8.1所示。当然帧速率计数器也一样可以在手机上进行显示,在真实的 Windows Phone 手机上测试这些计数器非常重要,因为模拟器的性能和在真实的手机上是有很大区别的。对于每个计数器的值都有建议阈值和上限阈值,如表8.2所示,当计数器在红色值阈值区间表明存在潜在性能问题,这就需要引起重视,你的动画的实现方案可能有较大的问题,需要进行优化。

表8.2 建议的帧和填充速率

计数器

红色阈值*

建议值

上限阈值

构图线程帧速率

30 帧/秒

45 帧/秒

60 帧/秒

UI 线程帧速率

15 帧/秒

30 帧/秒

60 帧/秒

屏幕填充速率

>3

<= 2.5

3.0

那么帧速率计数器是可以在代码中启用或禁用的,当你在Visual Studio中创建 Windows Phone应用项目时,默认情况下会在文件 App.xaml.cs 中添加启用帧速率计数器的代码。代码如下所示:

if (System.Diagnostics.Debugger.IsAttached)

{

this.DebugSettings.EnableFrameRateCounter = true;

}

上面的代码表示当启动Debug状态调试应用程序的时候将会启用帧速率计数器。其中Application.Current.Host.Settings.EnableFrameRateCounter = true表示启用帧速率计数器,设置为false则禁用帧速率计数器。

8.1.2 UI线程和构图线程

Windows Phone的图形线程结构针对手机进行了优化,除了UI线程之外,Windows Phone 还支持构图线程。若要掌握怎么去选择最优的动画实现方案,那么需要理解Windows Phone 中UI线程和构图线程,这对做动画的优化是非常重要的。

(1)UI 线程

UI 线程是Windows Phone中的主线程,UI线程的主要任务是从 XAML 中分析并创建对象、在第一次绘制视觉效果时,将绘制所有视觉效果以及处理每帧回调并执行其他用户代码。在应用程序里面维护轻量级的UI线程是保障应用程序流畅运行的前提,同时这对于动画的实现也是一样的道理,尽量避免占用UI线程。

    (2)构图线程

构图线程可以处理某些在UI上的工作,从而分担了UI线程的部分工作,提高Windows Phone应用的性能。在Windows Phone上,构图线程的工作是,它合并图形纹理并将其传递到 GPU 以供绘制,手机上的 GPU 将在称为自动缓存的进程中,自动缓存并处理运行在构图线程上的动画。构图线程处理与变换特效(RenderTransform)和三维特效(Projection)属性关联的动画,如针对于ScaleTransform、TranslateTransform、RotateTransform和PlaneProjection的属性改变的Storyboard动画都是完全运行在构图线程上的。另外,Opacity 和 Clip 属性设置也由构图线程处理。但是,如果使用 OpacityMask或非矩形剪辑,则这些操作将被传递到 UI 线程。

    (3)动画和线程

从构图线程的作用可以知道StoryBoard动画由构图线程进行处理,那么这种动画的处理方式最为理想,因为构图线程会将这些动画传递到GPU进行处理。如果需要在动画中使用到UI线程,如改变UI元素的With属性等,那么就需要给动画相应的Animation对象的EnableDependentAnimation属性设置为True,它表示动画是否需要依赖UI线程来运行。此外,如果 CPU 超负荷,则构图线程可能比UI线程运行的更频繁。但是,有时Storyboard动画无法实现你的动画效果的时候,你可以选择在代码中驱动动画,如采用基于帧动画。这些动画按帧进行处理,每帧回调都在UI线程上进行处理,动画的更新速度与UI线程处理动画的速度相当,并且根据应用中发生的其他操作,动画显示的流畅性可能低于在构图线程上运行的动画。另外,当使用基于帧动画在代码中更新动画时,UI元素不会像在Storyboard动画中更新一样,自动进行缓存,这又加重了UI线程的负担。

8.1.3 选择最优的动画方案

上一章我们讲解了很多的动画的变成知识,这些都是Windows Phone动画编程的根基,正所谓万变不离其宗,无论你要实现的动画懂么复杂,都离不开这些基础知识。当我们要去实现一个动画效果的时候,首先需要去思考动画中的每个组成元素,思考它们的变化情况,想一下要改变UI元素的什么属性来实现动画的效果,想一下用什么动画类型来实现。当你已经想到了有多种方案可以实现这个动画效果的时候,你可以从两个方面去衡量你的实现方案,一方面是从性能效率方面,这就涉及到前面所讲的动画的帧速率,UI线程和构图线程相关的知识;另一方面是从动画实现的复杂度方面,比如要实现一个很复杂图形的形状变化的动画,你可以直接用Path图形来绘制出这个图形,然后设计Path图形的点运动的动画,也可以用多张类似的图片做图片切换的动画,如果图片切换的动画效果能达到你所想要的效果,那么就建议使用图片切换这种简单的方式来实现。

在Windows Phone中有多种实现动画的方案,关于这些方案的选择有下面的一些建议。

(1)可以用变换特效属性或者三维特效属性实现的动画,应该尽量采用变换特效属性或者三维特效属性作为动画改变的属性去实现动画。因为变换特效属性或者三维特效属性是通过构图线程对UI元素产生作用的,不会阻塞UI线程也不会重新调用UI的布局系统。

(2)可以使用线性插值动画/关键帧动画来实现的动画就采用线性插值动画/关键帧动画去实现,因为线性插值动画/关键帧动画是最优的动画实现方式,它们本身也是在构图线程上运行的。

(3)当使用线性插值动画/关键帧动画无法实现的动画效果的时候应该采用基于帧动画来实现,而不是自定义定时器来实现动画,基于帧动画比定时器动画更胜一筹,它可以根据设备和应用程序的情况动态地跳帧调用的频率。

下面我们通过一个例子来演示用两种不同的方法来实现一个相同的动画效果,所实现的动画效果是让矩形的高度慢慢地变成原来的两倍,第一种方式是用线性插值动画对矩形的Height属性进行动画处理,第二种方式也是用线性插值动画,但是针对的动画目标属性是ScaleTransform的ScaleY属性,然后我们用一个按钮点击事件阻塞UI线程2秒钟,可以看到针对Height属性的动画会暂停2秒钟再继续运行,而针对ScaleTransform的ScaleY属性不会受UI线程阻塞的影响。示例代码如下所示:

代码清单8-1两种动画的对UI线程的影响(源代码:第8章\Examples_8_1)

MainPage.xaml文件主要代码
------------------------------------------------------------------------------------------------------------------
< Page.Resources>
<Storyboard x:Name="heightStoryboard">
<!--针对Height属性的动画-->
<DoubleAnimation Storyboard.TargetName="rectangle1" Storyboard.TargetProperty="Height" RepeatBehavior="Forever" EnableDependentAnimation="True" From="100" To="200" Duration="0:0:2">
</DoubleAnimation>
</Storyboard>
<Storyboard x:Name="scaleTransformStoryboard">
<!--针对ScaleTransform的ScaleY属性的动画-->
<DoubleAnimation Storyboard.TargetName="scaleTransform1" Storyboard.TargetProperty="ScaleY" RepeatBehavior="Forever" From="1" To="2" Duration="0:0:2">
</DoubleAnimation>
</Storyboard>
</ Page.Resources>
<StackPanel>
<Button Content="阻塞UI线程" Click="Button_Click_1"></Button>
<Button x:Name="heightAnimationButton" Content="Height属性动画" Click="heightAnimationButton_Click_1"></Button>
<Button x:Name="scaleTransformAnimationButton" Content="ScaleTransform属性动画" Click="scaleTransformAnimationButton_Click_1"></Button>
<Rectangle Height="100" Fill="Blue" x:Name="rectangle1">
<Rectangle.RenderTransform>
<ScaleTransform x:Name="scaleTransform1" ></ScaleTransform>
</Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
MainPage.xaml.cs文件主要代码
------------------------------------------------------------------------------------------------------------------
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// 阻塞UI线程2秒钟
Task.Delay().Wait();
}
private void heightAnimationButton_Click_1(object sender, RoutedEventArgs e)
{
// 播放改变高度属性的动画,高度有100变成200
scaleTransformStoryboard.Stop();
heightStoryboard.Begin();
}
private void scaleTransformAnimationButton_Click_1(object sender, RoutedEventArgs e)
{
// 播放改变变换属性的动画,举行沿着X轴放大2倍
heightStoryboard.Stop();
scaleTransformStoryboard.Begin();
}

本文来源于《深入理解Windows Phone 8.1 UI控件编程》

源代码下载:http://vdisk.weibo.com/s/zt_pyrfNHoezI

欢迎关注我的微博@WP林政

WP8.1技术交流群:372552293

[WP8.1UI控件编程]Windows Phone动画方案的选择的更多相关文章

  1. [WP8.1UI控件编程]Windows Phone XAML页面的编译

    1.1.2 XAML页面的编译 Windows Phone的应用程序项目会通过Visual Studio完成XAML页面的编译,在程序运行时会通过直接链接操作加载和解析XAML,将XAML和过程式代码 ...

  2. [WP8.1UI控件编程]Windows Phone大数据量网络图片列表的异步加载和内存优化

    11.2.4 大数据量网络图片列表的异步加载和内存优化 虚拟化技术可以让Windows Phone上的大数据量列表不必担心会一次性加载所有的数据,保证了UI的流程性.对于虚拟化的技术,我们不仅仅只是依 ...

  3. [WP8.1UI控件编程]Windows Phone VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件

    11.2.2 VirtualizingStackPanel.ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件 VirtualizingStackPanel.ItemsSta ...

  4. [WP8.1UI控件编程]Windows Phone理解和运用ItemTemplate、ContentTemplate和DataTemplate

    2.2.5 ItemTemplate.ContentTemplate和DataTemplate 在理解ItemTemplate.ContentTemplate和DataTemplate的关系的之前,我 ...

  5. [WP8.1UI控件编程]Windows Phone自定义布局规则

    3.2 自定义布局规则 上一节介绍了Windows Phone的系统布局面板和布局系统的相关原理,那么系统的布局面板并不一定会满足所有的你想要实现的布局规律,如果有一些特殊的布局规律,系统的布局面板是 ...

  6. [WP8.1UI控件编程]SemanticZoom控件实现分组列表

    11.1.5 SemanticZoom实现分组列表 SemanticZoom控件可以让用户实现一种更加高级的列表,这种列表可以对列表的项目进行分组,同时这个SemanticZoom控件会提供两个具有相 ...

  7. 《深入理解Windows Phone 8.1 UI控件编程》基于最新的Runtime框架

    <深入理解Windows Phone 8.1 UI控件编程>本书基于最新的Windows Phone 8.1 Runtime SDK编写,全面深入地论述了最酷的UI编程技术:实现复杂炫酷的 ...

  8. MFC控件编程之 按钮编辑框.静态文本的使用,以及访问控件的七种方法.

    MFC控件编程之 按钮编辑框.静态文本的使用以及访问控件的七种方法. 一丶按钮.静态文本的通用属性. 他们都有一个属性.就是可以输入标题内容.以及可以自定义控件ID. 创建一个MFC Dlg对话框. ...

  9. MFC控件编程之鼠标跟键盘消息

    MFC控件编程之鼠标跟键盘消息 在MFC中鼠标消息.键盘消息我们很常用.所以说一下. 鼠标消息分为客户区消息.跟非客户区消息. 一丶客户区消息 我们可以处理消息.来进行我们相应的函数即可. MFC添加 ...

随机推荐

  1. 与你相遇好幸运,Tippecanoe用法

    //todo 基本用法: tippecanoe -o file.mbtiles [file.json ...] 参数解释: <必须>   -o myFileName.mbtiles  或者 ...

  2. sprintf_s的使用

    int sprintf_s(char *restrict buffer, rsize_t bufsz,              const char *restrict format, ...); ...

  3. win7Java开发环境配置

    win7下Java开发环境的配置 首先下载符合操作系统版本的jdk,比如最新的jdk8: 下载链接:http://www.oracle.com/technetwork/java/javase/down ...

  4. ZooKeeper 的羊群效应

    一个需要避免的问题是当一个特定的znode 改变的时候ZooKeper 触发了所有watches 的事件. 举个例子,如果有1000个客户端watch 一个znode的exists调用,当这个节点被创 ...

  5. [Tools] 使用XP远程登录Win8系统

    [背景] 完成最基本的设置后,发现xp依然不能远程访问win8桌面,搜索后发现需要进一步设置   [开工] 按照参考资料进行设置,下面的参考资料已经写的很详细了,只是参考资料2中的文件名: redss ...

  6. Java eclipse下 Ant build.xml实例详解

    在有eclipse集成环境下ant其实不是很重要,但有些项目需要用到,另外通过eclipse来学习和理解ant是个很好的途径,所以写他demo总结下要点,希望能够帮到大家. 一.本人测试环境eclip ...

  7. yaml官方介绍

    官方网站 http://yaml.org/ 数据结构类型说明 http://yaml.org/type/ YAML Specification http://yaml.org/spec/

  8. Practical JAVA(二)关于对象的类型和equals函数

    Practice5,6,9,10,11,12,13,14,15 ==判断等号两边两个变量储存的值是否相同,如果是两个对象,则判断两个变量储存的对象地址是否相同. 大多数时候,我们需要判断的不是左右两个 ...

  9. 安装Maven、Eclipse设置、添加地址JAR

    1.下载Maven 地址:http://maven.apache.org/download.cgi 2.安装Maven 系统变量:MAVEN_HOME = D:\maven\apache-maven- ...

  10. 改变图片尺寸(python)

    for name in /图片路径; do convert -resize 256x256! $name $namedone