在Windows 8中,Animation角色的重要性超出了我们的想象,因为它也算是Windows 8流畅性体验的一部分。在我看来,Animation要做的好,有利于整体的布局,自定义控件,以及控件的行为的设计等等,都是为了用户体验。
通过定义ControlTemplate,我们可以为完全重新定义我们控件外观,而ControlTemplate中最重要的部分就是用来承载它外观的Visual Tree,这个模板必须包含如下的代码,即在发生适当的条件时,控件的外观应该是如何表现的。比如Button的Pressed状态的发生等等,虽然改变Button的外表状态不像是我们想的那种花时间,复杂的动画改变状态(比如以后会介绍的ObjectAnimation),但是也归为了Animation功能。
WinRT的Animation是基于时间的动画,举个例子,当一个线程既要跑动画,又要做其他工作而导致动画丢失了一些执行时间时,基于帧(Frame-based)的动画会继续从它上次离开的地方继续执行,而基于时间(Time-based)的动画,则根据现在实际的时间,来做原来计划中这个时间点应该做的效果。个人推断,一种极端是若是线程很busy,而分配的动画时间可能是2秒,那么线程若做别的事情超过2秒,动画将会一步到位,没有2秒的过程。
基础动画入门
下面对TextBlock的FontSize进行动态改变,这样TextBock的字体就会根据我们预设的方案改变,注意,其实Animation只是我们的控制方案,也就是Control,它的目标是某个属性,而不是整个动画怎么移动,有点像指挥家,它只负责指挥,具体的动作还是通过框架的渲染系统根据属性的改变而进行重新绘制,进而达到动画的效果. 一般情况下,我们的动画控制被当作是一种资源,放在Xaml中Root元素的Resources中.一个简单的Animation包括一个Storyboard和一个XXXAnimation,如下代码片段:
<Page … > <Page.Resources> <Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" To="144" Duration="0:0:3" /> </Storyboard> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Name="txtblk" Text="Animated Text" Grid.Row="0" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center" /> <Button Content="Trigger!" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick" /> </Grid> </Page>
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } void OnButtonClick(object sender, RoutedEventArgs args) { (this.Resources["storyboard"] as Storyboard).Begin(); } }
在上述代码中,DoubleAnimation的目标类型是Double(它的类名修饰说明了这一切),也就是说,你可以控制某个控件的Double属性(必须是依赖属性),让它跟着你的”剧本”走.那”剧本”就是DoubleAnimation,而剧本的内容就是它的一些个属性.你将会看到,WinRT还提供了对Point,Color和Object(第一次看见对Object,好像它是我们唯一需要使用的,因为任何的类都是继承于它,事实是我们使用它只是为了重新给某个属性的赋值而已,一般情况就设定两个值交替使用)。
WinRT要求,一个Animation对象(比如DoubleAnimation)必须包含在Storyboard下。相反,一个Storyboard可以包含多并发执行动画的Animation对象,而它的职责就是确保它们正确,同步地执行。
上面的代码中的TargetName和TargetProperty是Storyboard定义的附加属性(attached properties),你可以在Animation对象中设置它所关心的对象名称以及对象的那个需要动画的属性。
如前面所说的,Animation只对某个值(依赖属性)感兴趣,而其实对动画的实现不感兴趣,或者说,你有计划地改变某个值完全可以不是为了动画,所以,Animation一般在其他的线程下执行(非UI线程),为的是UI线程可以响应用户的操作。然而,我们当前的操作是为了动画,大家知道,在非UI线程中不能直接去修改在UI线程中创建的控件属性(抛异常),因为它的改变会引起布局的重新刷新,即非UI线程直接修改了界面,这显然是不可取的。WinRT不希望animation运行在UI线程中,但是为了让动画代码能在UI线程下运行,你必须将DoubleAnimation的EnableDependentAnimation设置为True,来告诉WinRT这个动画是依赖于UI线程才能达到代码目的的(dependent on the user interface thread)。
剩下的From ,To,Duration属性就非常容易理解,即在3秒的时间内,FrontSize从1变到144.Duration的格式是”时:分:秒”或“时:分”或“时”,且对于秒来说,可以是小数。
当你还没按下Trigger按钮,那么文本的字大小是预设的48-pixel,一旦OnButtonClick触发了,那么动画便随着Begin()的调用而启动,然后这个文本的文字大小瞬间变成1(From的值),然后3秒后,线性地变到144(To的值).所谓线性的,那就是每秒变大的速度是一样的,如下计算:
v(单位pixel/second,p/s)=(144-1)/3=48-1/3(p/s)
字体大小(pixel)
时间(s)
1+v*0=1
0
1+v*1=48-2/3
1
1+v*2=96-1/3
2
1+v*3=144
3
你可以随时地触发Begin,即使它还在执行,也会重新从1pixel开始变大。
其他属性对Animation变化的影响
以DoubleAnimation为例,在之前的代码中,动画结束后,字体的大小不是恢复到48(预设值),而是被重新回归到To的值,也就是1pixel,就好像它准备从头再来一遍一样。控制这种行为的是Animation的FillBehavior,它是一个枚举值,默认是HoldEnd,也就是我们碰到的情况,若是改成Stop,那么我们的文本的值将会回归到48,也就是停止(stop)再次循环.修改代码如下:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" FillBehavior="Stop" From="1" To="144" Duration="0:0:3" /> </Storyboard>
我们除了可以使用From,To进行控制Double的始末值,还可以单用其中一个,或者使用By属性。
几种情况看代码:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" Duration="0:0:3" /> </Storyboard>
缺省To,这种情况相当于To值为当前预设的48pixel。
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" To="144" Duration="0:0:3" /> </Storyboard>
缺省From,相当于From值为预设值48pixel。
其实以上的From和To都是nullable类型,系统通过这样来判定它们是否有被赋值,若没有,则结合当前的预设值进行动画。
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" By=”100” Duration="0:0:3" /> </Storyboard>
使用By,那么就从当前的48pixel在3秒内增加100pixel,而且当你下次再开始动画时,它的值将从148开始增加到248,也就FillBehavior的HoldEnd不起作用,但是若设置成Stop,则它还是会回到48。
若你想让你的动画原路返回(对于以上的例子,相当于To变成1,From变成144,也是在3秒的时间内完成) ,那么可以将Animation的AutoReverse设置为true。这样,整个的动画就会花费6秒的时间:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" AutoReverse="True" From="1" To="144" Duration="0:0:3" /> </Storyboard>
以上是定义一个周期的,那么我们也可以通过设置Animation的RepeatBehavior来定义周期的数目(或者定义总的执行时间),以下的代码将执行3个周期,一个周期的定义是从1变到144,再从144变到1,一个周期6秒,总时间18秒:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" AutoReverse="True" RepeatBehavior="3x" From="1" To="144" Duration="0:0:3" /> </Storyboard>
RepeatBehavior也可以定义一个时间段(duration),比如我们可以让周期为6秒的动画执行7.5秒,那么6秒一个周期后,它又继续重新开始执行1.5秒,也就是final 的大小是1+v*1.5=72.5,它停止的时的大小将会保持在72.5,当然你还是可以使用FillBehavior设置成Stop来跳回到48。
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" AutoReverse="True" RepeatBehavior="0:0:7.5" From="1" To="144" Duration="0:0:3" /> </Storyboard>
RepeateBehavior还可以接受无数次的周期,只要输入”Forever”就可以:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" AutoReverse="Forever" RepeatBehavior="0:0:7.5" From="1" To="144" Duration="0:0:3" /> </Storyboard>
预约开始时间,是的,对于Animation还可以约定它什么时候开始动画,比如从现在开始1.5s后启动动画:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" BeginTime="0:0:1.5" From="1" To="144" Duration="0:0:3" /> </Storyboard>
到目前为止,我们所有的DoubleAnimation动画都是有固定的速度V的,也就是是线性的。那么,一种创建非线性动画的方法,就是设置DoubleAnimation的EasingFunction(缓动函数)属性的属性元素,可以设置的值是继承自EasingFunctionBase的11个类,比如:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" To="144" Duration="0:0:3" > <DoubleAnimation.EasingFunction> <ElasticEase /> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard>
所谓的缓动函数,就是让动画不局限于一种物理状态(比如匀速),比如拥有加速度,而且是接近自然的,放松不死板的运动方式。当运行以上代码时,发现文本像是一个垂直于屏幕的弹簧,再你按下以后弹了好4次以后再停止,在此期间,时间还是3秒,第一次的最大值已经超过了144,然后第二次,第三次,最后停止在To的值上,即144,所以这符合对弹簧的模拟。
EasingFunctionBase 定义了一个EasingMode属性,当然也被其11个子类所继承。它的默认值是EasingMode.EaseOut,它的含义是动画效果开始的时候是线性地完成从1到144的变化(但是时间小于3秒,显然是为了后续的效果时间,但是总时间不会大于3秒),然后再进行“弹簧”的效果。你也可以将它的值设置为EaseIn,即效果在开始动画的时候演示,或者是用EaseInOut,在动画的开始和结束都进行特效。注意:个人体验这段 代码,使用EaseIn的时候,会触发"Invalid attribute value for property FontSize."的异常,导致程序中断。
一些基于EasingFunctionBase 的子类也定义了自己的属性,比如上面提到的ElasticEase类,它可以控制来回震荡的次数,即Oscillations属性,默认值是3。你可以设置它的值为10,那么它会来回弹10次然后停止,但是时间还是3秒。那么有控制次数的,也就有控制弹跳的效果的,即弹跳的程度,那么就是Springiness属性,它的中文意思是青春期,可以说,它的值越小,也就越“年轻”,所以弹跳的“动作幅度”也就会越大,默认值也是3。尝试如下的代码体验:
<Storyboard x:Key="storyboard"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" To="144" Duration="0:0:3" > <DoubleAnimation.EasingFunction> <ElasticEase Oscillations="10" Springiness="0"/> </DoubleAnimation.EasingFunction> </DoubleAnimation> </Storyboard>
之前提到的,诸如DoubleAnimation的对象必须包含在Storyboard对象中,有趣的是,其实这两个类继承自同一个类,即TimeLine,类继承图如下:
Object DependencyObject Timeline Storyboard DoubleAnimation …
Storyboard定义了TimelineColletion类型的Children属性,以及附加属性TargetName和TargetProperty,除此之外,还定义了控制动画的过程的函数,如pause(暂停),resume(恢复)。
到目前为止,我们看见的AutoReverse,BeginTime,Duration,FillBehavior和RepeatBehavior都是定义在Timeline,那么由于这些是依赖属性,可想而知,在Storyboard上设置这些值以后,它的Children中的各种Animation就可以共享这些值。
Timeline也定义了一个名叫SpeedRatio的属性(Double类型),从名称可知是控制速度动画速度的,将原来的速度和它相乘后得出的速度就是最终速度,显然,这会影响时间,我们可以把要变化的值的范围看成是距离(等长的情况),那么时间和速度就成反比了(默认值当然就是1啦),运行如下代码:
<DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" To="144" Duration="0:0:3" SpeedRatio="0.5" > </DoubleAnimation>
你可以计算它的花费时间是6秒。在平时使用时,这个值一般放置在Storyboard(当然,本意是在哪个Timeline中都可以) ,这样的目的其实就是为了可以控制所有在Storyboard上的Animation,因为我们设计的时候,这些Animation将会以一个整体的效果出现。那么当我们在Storyboard和Animation中同时设置这个属性时,会发生什么情况呢,如运行一下的代码:
<Storyboard x:Key="storyboard" SpeedRatio="0.5"> <DoubleAnimation Storyboard.TargetName="txtblk" Storyboard.TargetProperty="FontSize" EnableDependentAnimation="True" From="1" To="144" Duration="0:0:3" SpeedRatio="2" > </DoubleAnimation> </Storyboard>
我们在Storyboard中设置速度为0.5,而在DoubleAnimation中设置速度为2,那么它的结果就是整个速度为1,也就是和将这个两个值去掉一样的效果,花费还是3秒。
Timeline还有一个Completed的事件,也就是当动画完成时会触发的事件,你可以在恰当的时候使用它。
接下去实践一下完全在C#代码中构建Animation,首先定义UI界面:
<Page …> <Page.Resources> <Style TargetType="Button"> <Setter Property="Content" Value="Trigger!" /> <Setter Property="FontSize" Value="48" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="Margin" Value="12" /> </Style> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <Grid HorizontalAlignment="Center" VerticalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Button Grid.Row="0" Grid.Column="0" Click="OnButtonClick" /> <Button Grid.Row="0" Grid.Column="1" Click="OnButtonClick" /> <Button Grid.Row="0" Grid.Column="2" Click="OnButtonClick" /> <Button Grid.Row="1" Grid.Column="0" Click="OnButtonClick" /> <Button Grid.Row="1" Grid.Column="1" Click="OnButtonClick" /> <Button Grid.Row="1" Grid.Column="2" Click="OnButtonClick" /> <Button Grid.Row="2" Grid.Column="0" Click="OnButtonClick" /> <Button Grid.Row="2" Grid.Column="1" Click="OnButtonClick" /> <Button Grid.Row="2" Grid.Column="2" Click="OnButtonClick" /> </Grid> </Grid> </Page>
我们没有在XAML中定义任何关于Animation的信息,但是都在代码中添加。以上定义了9个独立的Button对象,我们在Click它的时候将对该Button中的Fontsize进行变化。构建的策略讨论,你可以为每个Button构建一次的Storyboard和DoubleAniamtion,然后在重复地使用它们,这里的条件是重复使用时,animation的target应该是同一个对象,也就是不能借给别人用,那么第二种策略就是每次使用新的Storyboard和DoubleAniamtion,简单的方法当然就是第二种,代码如下:
void OnButtonClick(object sender, RoutedEventArgs args) { DoubleAnimation anima = new DoubleAnimation { EnableDependentAnimation = true, To = 96, Duration = new Duration(new TimeSpan(0, 0, 1)), AutoReverse = true, RepeatBehavior = new RepeatBehavior(3) }; Storyboard.SetTarget(anima, sender as Button); Storyboard.SetTargetProperty(anima, "FontSize"); Storyboard storyboard = new Storyboard(); storyboard.Children.Add(anima); storyboard.Begin(); }
和XAML中定义有点区别的是,由于XAML里只能通过为UI元素命名而相互引用,所以是使用Storyboard.SetTargetName来赋予动画的对象,但是在代码中,我们不需要知道它叫什么,而是关心它是什么,所以使用SetTarget,直接传递对象给它就可以。
还有,我们使用Duration控制动画的时间,那么可以使用一个接受TimeSpan对象的构造函数,那么我们在碰到Forever以及默认值(1秒)时,可以使用Duration的静态属性,一个是用Duration.Forever,一个是用Duration.Automatic。
当我们单击了Button时,它因为字体大小的改变而体积发生改变,那么Grid就需要重新计算如何分配每个单元格的大小,当我们多按几个Button时,你就会发现有趣的现象了。
Animation第一部分结束语:这是我看原版的Windows Programming 6th Release Preview电子版的度数笔记,是翻译和自己体会的结合。
发表评论
-
WPF 获取元素相对位置的方法
2013-01-12 16:16 3050在Canvas中,我们可以为它的子元素通过附加属性Canvas ... -
WinRT Animation
2012-09-09 20:47 0的所发生的 -
WinRT 中RandomAccessStreamReference的使用
2012-09-07 16:20 2098RandomAccessStreamReferen ... -
WinRT中的流和.NET中的Stream的相互转换以及DataReader,DataWriter用法
2012-09-07 15:14 2606随着Windows8的发布,微软给出了一个Wind ... -
await Task 在Console应用程序和WPF,Metro中的不同
2012-09-07 11:58 1926在.NET 4.5中, ... -
HttpClient .NET4.5 使用方法
2012-08-26 23:33 4915简介 本文的主 ... -
.NET4.5 Linq 中XML 基础知识以及使用方式
2012-08-21 22:43 0有空写点XML学习笔记,最近工作也用到,以后重用回来看也快。 ... -
Metro style .net app Image控件的使用
2012-07-15 16:49 2077Image 元素:Object |------De ... -
ContextAttribute与ContextBoundObject应用开发(转)
2012-02-29 15:03 1706ContextAttribute与ContextBoun ... -
C#的委托和事件
2012-01-31 16:30 0最近开始上手C# ...
相关推荐
VLC for the Windows Store Environment ...总的来说,“vlc-winrt-master.zip”是一个深入学习多媒体播放器开发、WinRT API使用和跨平台开发的宝贵资源。对于想要提升这些技能的IT专业人员,这是一个绝佳的学习机会。
1. **第一章:介绍WinRT**:这一章节从整体上介绍了WinRT的概念、特点以及其对于开发者的重要性。它解答了许多开发者对于Windows Runtime的常见疑问,并阐述了WinRT与传统Windows API的区别。 2. **第二章:构建...
如果应用使用的是C#或JavaScript,建议创建一个WinRT组件来封装WinRT DLL的接口,以便于其他语言调用。虽然某些文档提到在C#应用中可以直接引用WinRT DLL,但实践证明这可能不可行,而且创建的WinRT组件必须是C++...
WinRT 环境下很多API没有了,比如socket相关的WSASocket,WSAConnect,线程相关的CreateThread等等,导致很多库不能使用,比如boost,基于这些库的应用如果要移植到WinRT环境下,将不得不修改很多代码,甚至需要重新...
☆ 资料说明:☆ ComponentOne Studio for WinRT XAML 简单易学,功能全面,WinRT XAML 顶级用户界面开发控件套包;...- 只开发一次建立通用 App; - SSRS 浏览器(Beta); - WinRT 日程控件; - 离线地图;
C++/CX是C++针对WinRT的一个扩展,它允许C++开发者方便地访问和使用WinRT API。在C++中,你可以定义和使用WinRT类型,创建和调用WinRT组件,而这些都是C语言无法直接完成的。 为了在C文件中调用WinRT DLL,你需要...
《5种方法帮微软拯救WinRT》 微软的WinRT操作系统在过去遭遇了严峻挑战,市场反应不佳,甚至导致了高达9亿美元的损失。WinRT未能与Windows 8形成清晰的区分,导致消费者对其定位感到困惑,进而影响了其市场表现。...
标题“WPF 通过 WindowsAppSDK 使用 WinRT 的手写识别功能.rar”揭示了这个压缩包包含的内容是关于Windows Presentation Foundation(WPF)应用程序如何利用Windows App SDK来接入并使用Windows Runtime(WinRT)的...
总的来说,虽然WinRT没有直接提供与WPF PointAnimationUsingPath相同的API,但通过组合使用CompositeTransform、Storyboard和自定义逻辑,开发者仍然可以实现类似的功能,从而在Windows 8应用中创造出丰富多样的路径...
基于WinRT系统的一维条码快速识别研究论文.doc
创建一个C++/CLI项目,然后使用`#include`指令引入WinRT头文件,并使用`using namespace`导入所需命名空间。 **注意事项:** 1. 调用WinRT API时,需要注意异步编程模型,因为许多WinRT方法是异步的。 2. 某些WinRT...
C++/WinRT是微软为Windows Runtime(WinRT)提供的一种现代C++标准库接口,它使得开发者可以更直接地使用C++17语言特性来编写Windows应用。这个技术是微软为了提高开发效率和代码质量而推出的,它与之前的C++/CX不同...
WinRT是Windows 8及更高版本操作系统的核心组件,它为开发者提供了一种直接与操作系统交互的方式,支持多种编程语言,包括C#。通过WinRT,开发者可以构建现代风格的Windows应用,充分利用Windows系统的特性和功能。 ...
例如,你可以使用 `JsonConvert.SerializeObject()` 方法将一个类实例转化为JSON文本。 2. **反序列化**:将JSON字符串转换回.NET对象。使用 `JsonConvert.DeserializeObject()` 可以将JSON数据恢复为相应的类实例...
迅捷/ WinRT 桥接到WinRT(Windows运行时)到 。例子Windows运行时的基本同步调用: import WinRTdo { RoInitialize () // ...
在Windows Runtime (WinRT) 平台上,AudioPlaybackConnector 是一个关键组件,它允许开发者创建应用,通过蓝牙连接将音频流传输到其他设备,如智能手机连接到电脑进行音乐播放。这个技术尤其适用于移动设备和跨设备...
可以实现日期选择功能,有winphone,iphone等多种样式可供选择。...使用时可以在你的项目中引用DatePicker文件夹,具体用法可以参考我在http://blog.csdn.net/f10_s/article/details/10404025上写的。
在本文中,我们将深入探讨如何使用Windows Presentation Foundation (WPF) 平台,结合Windows 10的WinRT(Windows Runtime)API,特别是Windows.Media.Ocr模块,来实现图像到文本的转换。这个功能通常被称为光学字符...
ComponentOne Studio for WinRT XAML2012 v3 Powerful WinRT XAML controls for building better Windows Store apps. Powerful UI and Data Visualization Controls for the Windows Store Get UI controls ...