建立動畫面臨的第一個挑戰是爲動畫選擇正確的屬性。指望的結果(例如,在窗口中移動元素)與須要使用的屬性(在這種狀況下是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>
爲反向使用相同的方法來突出顯示按鈕。例如,可以使用應用輝光效果的像素着色器突出顯示鼠標在其上懸停的按鈕。