WPF之小动画二
上一篇文章简单介绍了动画的定义方法和一些控制动画的方法,并没有涉及复杂属性的动画处理方式,本文将继续动画的其它方面的使用。
写在前面(对于一些动画操作时候的建议):
1.如果希望某个元素从显示到消失,或者从消失到显示,使用Visibility属性是无效的,因为此属性没有一个动画的过程,所以使用Opacity控制透明度进行操作;
2.如果要修改元素的位置则可以使用Canvas.Left或者Canvas.Top设置是RenderTransform(下文会涉及);
3.可以使用ColorAnimation修改元素的Brush,如果是比较复杂的aBrush,则可以使用DoubleAnimation来修改OffSet偏移量。
动画和Transforms:
Transforms可以对元素进行水平和垂直移动、可以使元素旋转、也可以使元素放大缩小以及进行使元素扭曲,通常在动画中会修改各种Transform的属性以达到动画的效果。
<Window x:Class="WpfApplication3.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <Style TargetType="{x:Type Button}"> <Setter Property="HorizontalAlignment" Value="Center"></Setter> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"></Setter> <Setter Property="Padding" Value="20,15"></Setter> <Setter Property="Margin" Value="2"></Setter> <Setter Property="RenderTransform"> <Setter.Value> <RotateTransform></RotateTransform> </Setter.Value> </Setter> <Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" To="360" Duration="0:0:0.8" RepeatBehavior="Forever"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel Margin="5"> <Button>One</Button> <Button>Two</Button> <Button>Three</Button> <Button>Four</Button> <TextBlock Name="lbl" Margin="5"></TextBlock> </StackPanel> </Window>
效果图为窗体上有四个按钮,鼠标悬浮到按钮上,按钮则进行360度的持续旋转,当鼠标离开,则恢复原状。
代码解析:上述代码的语法在上一篇博客中已经介绍过,通过Style和Trigger以及Animation进行操作,唯一区别就是在上述代码中使用了RenderTransform。可以看到在样式中给Button添加了RenderTransform属性,此处使用的是RotateTransform,也就是旋转变换。在MouseEnter时候开始执行动画,动画的属性为RenderTransform.Angle即旋转的角度,To就是360度,并且动画吃持续性的。在MouseLeave时候开始执行恢复原状的动画,如果From和To都不设置则将To默认为原有的值。(之所以可以直接使用RenderTransform.Angle是因为当前我们的RenderTransform只有一个变换对象,所以可以直接使用的,如果有多个变换对象,则要看下文喽)
Tips:LayoutTransform和RenderTransform的区别
与 RenderTransform相比, LayoutTransform 将影响布局的结果。 设置转换提供缩放和旋转的强大功能。 但是, LayoutTransform 忽略 TranslateTransform 操作。 这是因为, FrameworkElement 的子元素的布局系统行为自动更正对缩放的或旋转的元素的位置的所有偏移量父元素的布局和坐标系中。 LayoutTransform 可能导致应用程序性能变差,如果调用它在不需要布局系统执行全面处理过程的方案。 将 LayoutTransform 于 Panel时的 Children 集合,它将触发布局系统新通过和强制屏幕上的所有对象。重新度量和重新排列。 如果要更新完整的应用程序 用户界面 (UI),此功能可能完全符合您的需要。 但是,因此,如果不需要完整布局处理过程,请使用 RenderTransform 属性,不会调用布局系统,并且,通常是此方案的更好的选择。 |
理论说多了都是泪,看看LayoutTransform的例子:
<Style TargetType="{x:Type Button}"> <Setter Property="HorizontalAlignment" Value="Center"></Setter> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"></Setter> <Setter Property="Padding" Value="20,15"></Setter> <Setter Property="Margin" Value="2"></Setter> <Setter Property="LayoutTransform"> <Setter.Value> <RotateTransform></RotateTransform> </Setter.Value> </Setter> <Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.Angle" To="360" Duration="0:0:0.8" RepeatBehavior="Forever"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.Angle" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style>
如你所见,没有怎么改动,只是把RenderTransform的地方都改为了LayoutTransform(话说楼主也是因为Property写的是LayoutTransform,而在动画中使用的是RenderTransform,却一直报错)。运行程序,你会发现按钮在旋转的时候整个布局都是弹跳(比喻),其实就是动态更改布局。
多个Transform(TransformGroup):
顾名思义,多个Transform,也就意味着可以同时向元素应用多个Transform,也可以在动画中对其,进行修改。下面的例子为,默认情况下Image倾斜角度为50,并且缩放了0.5倍,同时Image的Opacity透明度为0.5,在窗口进行最大化的时候使Image角度为0,然后大小正常,透明度为1。
<Image Height="300" Width="300" Source="Desert.jpg" Opacity="0.5" x:Name="imgTransform"> <Image.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="0.5" ScaleY="0.5"></ScaleTransform> <RotateTransform Angle="50"></RotateTransform> </TransformGroup> </Image.RenderTransform> </Image>
动画资源
<Window.Resources> <Storyboard x:Key="sbTransform"> <DoubleAnimation Storyboard.TargetName="imgTransform" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX" To="1" Duration="0:0:1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="imgTransform" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY" To="1" Duration="0:0:1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="imgTransform" Storyboard.TargetProperty="RenderTransform.Children[1].Angle" To="0" Duration="0:0:1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="imgTransform" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"></DoubleAnimation> </Storyboard> </Window.Resources>
解释:在Image中主要是对其RenderTransform使用了TransformGroup,表示可以放置多个Transform,在此是一个缩放的ScaleTransform和一个旋转的RotateTransform。在动画中使用了四个DoubleAnimation,前三个为Transform。在TargetProperty中使用的属性是不是觉得很奇怪,那你还记不记得我们第一个例子中是直接使用RenderTransform.属性的呢,区别就是第一个例子我们只有一个Transform,而这里我们有两个(是一个集合哦),所以我们需要通过RenderTransform的Children属性根据索引得到对应的Transform。在这里0代表的就是ScaleTransform,1就是RotateTransform,所以可以分别设置它们的属性,最后一个Animation没什么说的,就是修改透明度。
开始动画:
private void Window_SizeChanged_1(object sender, SizeChangedEventArgs e) { if (WindowState== System.Windows.WindowState.Maximized) { Storyboard storyboard = this.Resources["sbTransform"] as Storyboard; storyboard.Begin(); } }
在窗口的SizeChange中,判断如果为最大化,则开始动画。
动画和颜色(Brush):
颜色:
<Rectangle Name="rectTest" Fill="Green" Height="100" Width="100" > <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown"> <BeginStoryboard> <Storyboard> <ColorAnimation To="Red" Storyboard.TargetProperty="Fill.Color" Duration="0:0:1"></ColorAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
一个Rectangle矩形,默认为绿色,点击之后变为红色。在这里使用的是ColorAnimation,表示颜色动画,这个不是重点,重点是Property的设置,由于颜色(不管是Rectangle的Fill还是其它元素的Background类型都是Brush)的类型都是Brush,而Brush是无法进行设置的,所以需要给其Color进行设置,也就看到了上述的语法。
Brush:
上述例子修改的是某一个元素的颜色,现在开始试着修改Brush这样的类型。
动画修改偏移量:
<Rectangle Name="rectTest" Height="100" Width="100" > <Rectangle.Fill> <LinearGradientBrush> <GradientStop Offset="0" Color="Red"></GradientStop> <GradientStop Offset="1" Color="Green"></GradientStop> </LinearGradientBrush> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown"> <BeginStoryboard> <Storyboard> <DoubleAnimation To="0.2" Storyboard.TargetProperty="Fill.GradientStops[0].Offset" Duration="0:0:1"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
此例子关键点在于Property,Fill.GradientStops表示画刷的整个渐变点的集合,然后通过索引确定某一个渐变项,然后修改偏移量。
动画修改渐变项的颜色:
<Rectangle Name="rectTest" Height="100" Width="100" > <Rectangle.Fill> <LinearGradientBrush> <GradientStop Offset="0" Color="Red"></GradientStop> <GradientStop Offset="1" Color="Green"></GradientStop> </LinearGradientBrush> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown"> <BeginStoryboard> <Storyboard> <ColorAnimation To="Yellow" Storyboard.TargetProperty="Fill.GradientStops[0].Color" Duration="0:0:1"></ColorAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
此例子关键点和上述基本一致,唯一区别是使用了ColorAnimation,修改的是Color属性。
动画修改画刷的属性和渐变项的颜色:
<Rectangle Name="rectTest" Height="100" Width="100" > <Rectangle.Fill> <RadialGradientBrush GradientOrigin="0.5,0.5"> <GradientStop Offset="0" Color="Red"></GradientStop> <GradientStop Offset="1" Color="Green"></GradientStop> </RadialGradientBrush> </Rectangle.Fill> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.MouseLeftButtonDown"> <BeginStoryboard> <Storyboard> <PointAnimation To="0.7,0.5" Storyboard.TargetProperty="Fill.GradientOrigin" Duration="0:0:1"></PointAnimation> <ColorAnimation To="Yellow" Storyboard.TargetProperty="Fill.GradientStops[0].Color" Duration="0:0:1"></ColorAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle>
此例子关键点在第一个动画,Property修改的是Brush的Gradientorigin属性而并非见渐变项的,从上述各种代码可以看到,如果元素的颜色属性就是一个Bursh,那么可以使用“颜色属性.Brush的属性"进行访问。
动画和着色器(BlurEffect):
<Button Content="A Button"> <Button.Effect> <BlurEffect Radius="10"></BlurEffect> </Button.Effect> <Button.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Effect.Radius" To="0" Duration="0:0:0.4"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Effect.Radius" To="10" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Button.Triggers> </Button>
一个Button,添加了BlurEffect用于模糊,默认模糊为10像素,当鼠标悬浮时候变为正常,鼠标离开变为10想速度模糊,没有难点,和之前的大致一样,例子旨在说明动画可以应用到大部分你想改变的属性上。
关键帧动画(本来是蛮不错的东西,不过本人之前的Silverlight中已经有提到,大家可以移步到此,戳我):
轨迹动画(Path-Animation):
轨迹动画,顾名思义,根据某一段轨迹进行运动的过程,当然也就少不了三元素,要运动的Element,轨迹Path,动画。
<Window.Resources> <PathGeometry x:Key="path"> <PathFigure IsClosed="True"> <ArcSegment Point="100,200" Size="15,10" SweepDirection="Clockwise"></ArcSegment> <ArcSegment Point="400,50" Size="5,5" ></ArcSegment> </PathFigure> </PathGeometry> <Storyboard x:Key="sbTest"> <DoubleAnimationUsingPath Storyboard.TargetName="image" Storyboard.TargetProperty="(Canvas.Left)" PathGeometry="{StaticResource path}" Duration="0:0:5" RepeatBehavior="Forever" Source="X"/> <DoubleAnimationUsingPath Storyboard.TargetName="image" Storyboard.TargetProperty="(Canvas.Top)" PathGeometry="{StaticResource path}" Duration="0:0:5" RepeatBehavior="Forever" Source="Y"/> </Storyboard> </Window.Resources>
上述代码中放置了一个Path,表示运动的轨迹(轨迹的语法有点DT,所以大家还是去找MSDN吧)。一个动画,动画的类型是前所没有使用过的DoubleAnimationUsingPath,表示使用Path执行的Double动画。TargetName下文会出现就是一图片(其实是使用DrawingImage绘制的),Property又好奇怪,其实这个就是附加属性的语法(关于这个大家再移步一下,戳我)。接下来重要的出现了,PathGeometry和Source,没错前者就是要运动的轨迹,果断把我们的Path丢给它;Source又是嘛意思呢,根据MS的解释就是在运动过程中的输出,说白了就是这个值到底是X还是Y还是Angle,因为Path是一系列de坐标组成的,所以你懂得。在两个动画中分别设置Canvas.Left和Canvas.Top,Left当然应该使用X,而Top也顺其自然的使用Y了。
看到了吧,小图片根据这个轨迹在蹭蹭蹭的跑呢。
好了,此次介绍就到这里了,下一篇会有亮瞎眼睛的东西哦,敬请期待。