建立动画面临的第一个挑战是为动画选择正确的属性。指望的结果(例如,在窗口中移动元素)与须要使用的属性(在这种状况下是Canvas.Left和Canvas.Top属性)之间的关系并不老是很直观。下面是一些指导原则:html
接下来的示例演示了如何动态改变变换和画刷,以及如何使用更多的一些动画类型。还将讨论如何使用关键帧建立多段动画、如何建立基于路径的动画和基于帧的动画。session
1、动态变换ide
变换提供了自定义元素的最强大方式之一。当使用变换时,不仅是改变元素的边界,并且会移动、翻转、扭曲、拉伸、放大、缩小或旋转元素的整个可视化外观。例如,可经过ScaleTransform动态改变按钮的尺寸,这会改变整个按钮的尺寸,包括按钮的边框及其内部的内容。这种效果比动态改变Width和Height属性或改变文本的Fontsize属性给人的印象更深入。工具
前面章节了解到,每一个元素都能以两种不一样的方式使用变换:RenderTransform属性和LayoutTransform属性。RenderTransform效率更高,由于是在布局以后应用变换而且敢于变换最终的渲染输出。LayoutTransform在布局前应用,从而其余控件须要从新排列以适应变换。改变LayoutTransform属性会引起新的布局操做(除非在Canvas面板上使用元素,在这种状况下,RenderTransform和LayoutTransform的效果相同)。布局
为在动画中使用变换,第一步是定义变换(动画可改变已经存在的变换,但不能建立新的变换)。例如,假设但愿使按钮旋转,此时须要使用RotateTransform对象:性能
<Button Content="A Button"> <RenderTransform> <RotateTransform></RotateTransform> </RenderTransform> </Button>
如今当将鼠标移动到按钮上时,下面的事件触发器就会旋转按钮。使用的目标属性是RenderTransform.Angle——换句话说,读取按钮的RenderTransform属性并修改其中定义的RotateTransform对象的Angle属性。事实上,RenderTransform属性可包含各类不一样的变换对象,每种变换对象的属性各不相同,这不会引发问题。只要使用的变换具备Angle属性,这个触发器就能工做:学习
<EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="rotateStoryboardBegin"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" To="360" Duration="0:0:0.8" RepeatBehavior="Forever"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger>
按钮在0.8秒得时间内旋转一周而且持续旋转。当按钮旋转时仍彻底可用——例如,可单击按钮并处理Click事件。动画
为保证按钮绕其中心旋转(而不是绕左上角旋转),须要按以下方式设置RenderTransformOrigin属性:网站
<Button RenderTransformOrigin="0.5,0.5"/>
请记住,RenderTransformOrigin属性使用0~1的相对单位,因此0.5表示中点。编码
为中止旋转,可以使用第二个触发器响应MouseLeave事件。这是,可删除执行旋转的故事板,但这会致使按钮一步调回到它原来的位置。更好的方法是开始第二个动画,用它替代第一个动画。这个动画忽略To和From属性,这意味着它无缝地再0.2秒得时间内将按钮旋转回原始方向:
<EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.Angle" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger>
为建立旋转的按钮,须要为Button.Triggers集合添加这两个触发器。或将它们(以及变换)放到一个样式中,并根据须要为多个按钮应用这个样式。例如,下面的窗口标记充满了下图中显示的“能旋转的”按钮:
<Window x:Class="Animation.RotateButton" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="RotateButton" 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 Name="rotateStoryboardBegin"> <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.Click="cmd_Clicked"> <Button>One</Button> <Button>Two</Button> <Button>Three</Button> <Button>Four</Button> <TextBlock Name="lbl" Margin="5"></TextBlock> </StackPanel> </Window>
在单击任何按钮时,都会在TextBlock元素中显示一条信息。
这个示例还未分析渲染变换和布局变换之间的区别提供了绝佳的机会。若是修改代码可以使用LayoutTransform属性,那么会发现当旋转其中一个按钮时,其余按钮会被推离原来的位置。例如,若是旋转最上面的按钮,下面的按钮会上下跳动以避开顶部的按钮。
<Window x:Class="Animation.RotateButtonWithLayout" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="RotateButtonWithLayout" 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="LayoutTransform"> <Setter.Value> <RotateTransform></RotateTransform> </Setter.Value> </Setter> <Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="rotateStoryboardBegin"> <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> <!-- <RemoveStoryboard BeginStoryboardName="rotateStoryboardBegin"></RemoveStoryboard> --> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="LayoutTransform.Angle" Duration="0:0:0.2"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel Margin="5" Button.Click="cmd_Clicked"> <Button>One</Button> <Button>Two</Button> <Button>Three</Button> <Button>Four</Button> <TextBlock Name="lbl" Margin="5"></TextBlock> </StackPanel> </Window>
动态改变多个变换
可很容易地组合使用变换。实际上这是很容易——只须要使用TransformGroup对象设置LayoutTransform或RenderTransform属性便可。可根据须要在TransformGroup对象中嵌套任意多个变换。
下图显示了一个使用两个变换建立的有趣效果。文档窗口刚开始做为主窗口左上角的小缩略图。当文档窗口显示时,内容旋转、扩展并快速淡入到试图中,从概念上讲,这与最大化窗口时Windows使用的效果相似。在WPF中,可经过变换为全部的元素应用这种技巧。
为建立这种效果,在以下TransformGroup对象中定义了两个变换,并使用TransformGroup对象设置包含全部内容的Board对象的RenderTransform属性:
<Border.RenderTransform> <TransformGroup> <ScaleTransform></ScaleTransform> <RotateTransform></RotateTransform> </TransformGroup> </Border.RenderTransform>
经过指定数字偏移值(0用于首先显示的ScaleTransform对象,1用于接下来显示的RotateTransform对象),动画可与这两个变换对象进行交互。例如,下面的动画放大内容:
<DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX" From="0" To="1" Duration="0:0:2" AccelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY" From="0" To="1" Duration="0:0:2" AccelerationRatio="1"></DoubleAnimation>
下面的动画位于相同的故事板中,用于旋转内容:
<DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[1].Angle" From="70" To="0" Duration="0:0:2" ></DoubleAnimation>
这个动画中的内容比此处显示的内容还多。例如,还有一个同事增长Opacity属性的动画,而且当Borad元素达到最大尺寸时,它短暂地向后"反弹"一下,建立一种更趋天然的效果。为这个动画建立时间线并修改各个动画对象属性须要耗费时间——理想状况下,可以使用诸如Expression Blend的设计工具执行这些任务,而不是经过手动编写代码来完成这些任务。甚至更好的状况下,只要有第三方开发者将这一逻辑分组到自定义动画中,就能够重用并根据须要将其应用到对象上(根据目前的状况,可经过将Storyboard对象存储为应用程序级的资源,重用这个动画)。
下面是完整的XAML标记:
<Window x:Class="Animation.ExpandElement" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="ExpandElement" Height="423.2" Width="488.8" WindowStartupLocation="CenterScreen"> <Window.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard SpeedRatio="1.5"> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="Opacity" From="0.2" To="1" Duration="0:0:2.5"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[1].Angle" From="70" To="0" Duration="0:0:2" ></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX" From="0" To="1" Duration="0:0:2" AccelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY" From="0" To="1" Duration="0:0:2" AccelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX" To="0.98" BeginTime="0:0:2" Duration="0:0:0.05" DecelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY" To="0.98" BeginTime="0:0:2" Duration="0:0:0.05" DecelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX" To="1" BeginTime="0:0:2.05" Duration="0:0:0.2" AccelerationRatio="1"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="element" Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY" To="1" BeginTime="0:0:2.05" Duration="0:0:0.2" AccelerationRatio="1"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Window.Triggers> <Grid> <!--<Button Name="element"> <Button.Content>Text</Button.Content> <Button.RenderTransform> <TransformGroup> <ScaleTransform ScaleX="0" ScaleY="0"></ScaleTransform> <TranslateTransform></TranslateTransform> <RotateTransform Angle="90"></RotateTransform> </TransformGroup> </Button.RenderTransform> </Button>--> <Border Name="element" Margin="3" Background="LightGoldenrodYellow" BorderBrush="DarkBlue" BorderThickness="2" CornerRadius="5" > <Border.RenderTransform> <TransformGroup> <ScaleTransform></ScaleTransform> <RotateTransform></RotateTransform> </TransformGroup> </Border.RenderTransform> <FlowDocumentScrollViewer IsToolBarVisible="True"> <FlowDocument> <Paragraph xml:space="preserve">The <Italic>foof</Italic> feature is indispensable. You can configure the foof feature using the Foof Options dialog box.</Paragraph> <BlockUIContainer> <Button HorizontalAlignment="Left" Padding="5">Open Foof Options</Button> </BlockUIContainer> <Paragraph FontSize="20pt">Largest Cities in the Year 100</Paragraph> <Table> <Table.Columns> <TableColumn Width="*"></TableColumn> <TableColumn Width="3*"></TableColumn> <TableColumn Width="*"></TableColumn> </Table.Columns> <TableRowGroup > <TableRow FontWeight="Bold" > <TableCell > <Paragraph>Rank</Paragraph> </TableCell> <TableCell> <Paragraph>Name</Paragraph> </TableCell> <TableCell> <Paragraph>Population</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>1</Paragraph> </TableCell> <TableCell> <Paragraph>Rome</Paragraph> </TableCell> <TableCell> <Paragraph>450,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>2</Paragraph> </TableCell> <TableCell> <Paragraph>Luoyang (Honan), China</Paragraph> </TableCell> <TableCell> <Paragraph>420,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>3</Paragraph> </TableCell> <TableCell> <Paragraph>Seleucia (on the Tigris), Iraq</Paragraph> </TableCell> <TableCell> <Paragraph>250,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>4</Paragraph> </TableCell> <TableCell> <Paragraph>Alexandria, Egypt</Paragraph> </TableCell> <TableCell> <Paragraph>250,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>5</Paragraph> </TableCell> <TableCell> <Paragraph>Antioch, Turkey</Paragraph> </TableCell> <TableCell> <Paragraph>150,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>6</Paragraph> </TableCell> <TableCell> <Paragraph>Anuradhapura, Sri Lanka</Paragraph> </TableCell> <TableCell> <Paragraph>130,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>7</Paragraph> </TableCell> <TableCell> <Paragraph>Peshawar, Pakistan</Paragraph> </TableCell> <TableCell> <Paragraph>120,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>8</Paragraph> </TableCell> <TableCell> <Paragraph>Carthage, Tunisia</Paragraph> </TableCell> <TableCell> <Paragraph>100,000</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>9</Paragraph> </TableCell> <TableCell> <Paragraph>Suzhou, China</Paragraph> </TableCell> <TableCell> <Paragraph>n/a</Paragraph> </TableCell> </TableRow> <TableRow> <TableCell> <Paragraph>10</Paragraph> </TableCell> <TableCell> <Paragraph>Smyrna, Turkey</Paragraph> </TableCell> <TableCell> <Paragraph>90,000</Paragraph> </TableCell> </TableRow> </TableRowGroup> </Table> </FlowDocument> </FlowDocumentScrollViewer> </Border> </Grid> </Window>
这种效果很是有用。例如,可以使用该效果将注意力吸引到新的内容——例如用户刚刚打开的文件。这种效果可能的变化是无穷无尽的。例如,建立产品目录时,当用户将鼠标悬停在相应的产品名称上时,滑入包含产品细节的面板或将产品图像滚入试图。
2、动态改变画刷
动态改变画刷是WPF动画中的另外一种经常使用技术,和动态变换一样容易。一样,这种技术使用恰当的动画类型,深刻到但愿改变的特定子属性。
下图显示了一个修改RadialGradientBrush画刷的示例。当动画运行时,径向渐变的中心点沿椭圆漂移,从而实现了一种三维效果。同时,外侧的渐变颜色从蓝色变成黑色。
为实现这个效果,须要使用两种还没有分析过的动画类型。ColorAnimation动画在两个颜色之间逐渐混合,建立一种微妙的颜色转移效果。PointAnimation动画可将点从一个位置移到另外一个位置(本质上与使用独立DoubleAnimation,经过线性插值同时修改X坐标和Y坐标是相同的)。可以使用PointAnimation动画改变使用点构造的图形,或者就像这个示例中那样,改变径向渐变中心点的位置。
下面是标记定义了椭圆及其画刷:
<Ellipse Name="ellipse" Margin="5" Grid.Row="1" Stretch="Uniform"> <Ellipse.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" GradientOrigin="0.7,0.3"> <GradientStop Color="White" Offset="0" ></GradientStop> <GradientStop Color="Blue" Offset="1" ></GradientStop> </RadialGradientBrush> </Ellipse.Fill> </Ellipse>
下面是移动中心点以及改变第二种颜色的两个动画:
<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> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:20"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.3,0.7" KeyTime="0:0:25"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.5,0.9" KeyTime="0:0:28"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.9,0.6" KeyTime="0:0:20"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.8,0.2" KeyTime="0:0:22"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:24"></DiscretePointKeyFrame> </PointAnimationUsingKeyFrames> <ColorAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientStops[1].Color" To="Black" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever"></ColorAnimation>
本例网站XAML标记:
<Window x:Class="Animation.AnimateRadialGradient" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="AnimateRadialGradient" Height="300" Width="300" WindowStartupLocation="CenterScreen"> <Window.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <BeginStoryboard> <Storyboard> <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> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:20"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.3,0.7" KeyTime="0:0:25"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.5,0.9" KeyTime="0:0:28"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.9,0.6" KeyTime="0:0:20"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.8,0.2" KeyTime="0:0:22"></DiscretePointKeyFrame> <DiscretePointKeyFrame Value="0.7,0.3" KeyTime="0:0:24"></DiscretePointKeyFrame> </PointAnimationUsingKeyFrames> <ColorAnimation Storyboard.TargetName="ellipse" Storyboard.TargetProperty="Fill.GradientStops[1].Color" To="Black" Duration="0:0:10" AutoReverse="True" RepeatBehavior="Forever"></ColorAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Window.Triggers> <Grid> <Ellipse Name="ellipse" Margin="5" Grid.Row="1" Stretch="Uniform"> <Ellipse.Fill> <RadialGradientBrush RadiusX="1" RadiusY="1" GradientOrigin="0.7,0.3"> <GradientStop Color="White" Offset="0" ></GradientStop> <GradientStop Color="Blue" Offset="1" ></GradientStop> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> </Grid> </Window>
经过修改LinearGradientBrush和RadialGradientBrush画刷的颜色和偏移值可建立许多精彩效果。若是还不够,渐变画刷还有本身的RelativeTransform属性,可以使用该属性旋转、缩放、拉伸以及扭曲画刷。WPF团队有一个有趣的称为Gradient Obsession的工具,该工具用于构建基于渐变的动画。
VisualBrush画刷
VisualBrush画刷可获取任意元素的外观,使用该外观可填充另外一个表面。其余表面能够是任何内容,从广泛的矩形到文本框中的字母。
下图显示了一个基本示例。顶部是一个真实的活动按钮。下面经过VisualBrush画刷使用按钮图片填充一个矩形,并经过各类变换效果拉伸并旋转按钮图片。
VisualBrush画刷还为实现一些有趣的动画效果的动画效果提供了可能。例如,不只可动态显示活动的真实元素,还可动态显示具备相同填充内容的简单矩形。
为理解这种方法的工做原理,分析这个示例,该例将一个元素放入试图中。当这个动画运行时,处理具备动画的元素的方法和处理其余任意WPF元素的方式相同,这意味着可单击它内部的按钮,或使用键盘滚动内容(若是用户的操做足够迅速的话)。在一些状况下,这可能会令用户感到困惑。在由写状况下,这可能致使性能降低,由于须要额外的开销来变换输入(如鼠标单击),而且和原始元素一块儿传递输入。
使用VisualBrush画刷可轻易地代替这种效果。首先,须要建立另外一个元素,使用VisualBrush画刷填充该元素。VisualBrush画刷必须根据但愿包含动画的元素(在这个示例中,是名为visual的边框)绘制可视化内容。
<Rectangle Grid.Row="1" Name="rectangle" Width="100" Stretch="Uniform" ClipToBounds="False" RenderTransformOrigin="0.5,0.5"> <Rectangle.Fill> <VisualBrush Visual="{Binding ElementName=visual}"> </VisualBrush> </Rectangle.Fill> <Rectangle.RenderTransform> <TransformGroup> <SkewTransform CenterX="0.5"></SkewTransform> <RotateTransform CenterX="0.5" CenterY="0.5"></RotateTransform> </TransformGroup> </Rectangle.RenderTransform> </Rectangle>
示例完整XAML标记:
<Window x:Class="Animation.AnimateVisual" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="AnimateVisual" Height="300" Width="300" Background="LightGoldenrodYellow" WindowStartupLocation="CenterScreen"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Button Name="visual" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button.Content>Test</Button.Content> <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard RepeatBehavior="Forever"> <DoubleAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="RenderTransform.Children[0].AngleY" To="180" Duration="0:0:15" AutoReverse="True"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="RenderTransform.Children[1].Angle" To="180" Duration="0:0:20" AutoReverse="True"></DoubleAnimation> <DoubleAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="Opacity" To="0.1" Duration="0:0:4" AutoReverse="True"></DoubleAnimation> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> </Button> <Rectangle Grid.Row="1" Name="rectangle" Width="100" Stretch="Uniform" ClipToBounds="False" RenderTransformOrigin="0.5,0.5"> <Rectangle.Fill> <VisualBrush Visual="{Binding ElementName=visual}"> </VisualBrush> </Rectangle.Fill> <Rectangle.RenderTransform> <TransformGroup> <SkewTransform CenterX="0.5"></SkewTransform> <RotateTransform CenterX="0.5" CenterY="0.5"></RotateTransform> </TransformGroup> </Rectangle.RenderTransform> </Rectangle> </Grid> </Window>
为将矩形放到与原始元素相同的位置,可将它们同时放到Grid面板的同一个单元格中。改变单元格的尺寸,使其适合原始元素(边框),并拉伸矩形使其相匹配。另外一个选择是在真实实用程序上覆盖Canvas面板(而后可将动画属性绑定到下面真实元素的ActualWidth和ActualHeight属性,从而确保对齐)。
添加矩形后,只须要调整动画来执行动态变化。最后,当动画完成时隐藏矩形:
private void storyboardCompleted(object sender,EventArgs e) { rectangle.Visibility=Visibility.Collapsed; }
3、动态改变像素着色器
经过“【WPF学习】第四十六章 效果 ”的学习,了解了像素着色器(可为任意元素应用位图风格效果的低级例程,如模糊、辉光以及弯曲效果)的相关内容。就自身而言,像素着色器是一些有趣而且偶尔有用的工具。但经过结合使用动画,他们可变的更通用。可以使用它们设计吸引眼球的过渡效果(例如,经过模糊控件使其淡出、隐藏,而后模糊另外一个控件使其淡入)。也可以使用像素着色器建立给人留下深入印象的用户交互效果(例如,当用户将鼠标移动到按钮上时增长按钮上的辉光)。最后为像素着色器的属性应用动画,就像为其余内容应用动画同样容易。
下图显示的页面是基于在前面给出的旋转按钮示例构建的。该例包含一系列按钮,而且当用户将鼠标移动到其中某个按钮上时,关联并开始动画。区别在于这个示例中的动画不是旋转按钮,而将模糊半径减小至0。结果是移动鼠标时,最近的按钮骤然轻快地变得清晰。
该例的代码和旋转按钮示例中的代码相同。须要为每一个按钮提供BlurEffect对象而不是RotateTransform对象:
<Setter Property="Effect"> <Setter.Value> <BlurEffect Radius="10"></BlurEffect> </Setter.Value> </Setter>
还须要相应地修改动画:
<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>
本实例完整XAML标记:
<Window x:Class="Animation.BlurringButtons" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="BlurringButtons" Height="300" Width="300" WindowStartupLocation="CenterScreen"> <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="Effect"> <Setter.Value> <BlurEffect Radius="10"></BlurEffect> </Setter.Value> </Setter> <Style.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> </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>
为反向使用相同的方法来突出显示按钮。例如,可以使用应用辉光效果的像素着色器突出显示鼠标在其上悬停的按钮。