【WPF學習】第五十三章 動畫類型回顧

来源:https://www.cnblogs.com/Peter-Luo/archive/2020/03/03/12399126.html
-Advertisement-
Play Games

創建動畫面臨的第一個挑戰是為動畫選擇正確的屬性。期望的結果(例如,在視窗中移動元素)與需要使用的屬性(在這種情況下是Canvas.Left和Canvas.Top屬性)之間的關係並不總是很直觀。下麵是一些指導原則: 如果希望使用動畫來使元素顯示和消失,不要使用Visibility屬性(該屬性只能在完全 ...


  創建動畫面臨的第一個挑戰是為動畫選擇正確的屬性。期望的結果(例如,在視窗中移動元素)與需要使用的屬性(在這種情況下是Canvas.Left和Canvas.Top屬性)之間的關係並不總是很直觀。下麵是一些指導原則:

  •   如果希望使用動畫來使元素顯示和消失,不要使用Visibility屬性(該屬性只能在完全可見和完全不可見之間進行切換)。應改用Opacity屬性淡入或淡出元素。
  •   如果希望動態改變元素的位置,可考慮使用Canvas面板。它提供了最直接的屬性(Canvas.Left及Canvas.Top),而且開銷最小。此外,也可使用動畫屬性在其他佈局容器中獲得類似效果。例如,可通過使用ThicknessAnimation類動態改變Margin和Padding等屬性,還可動態改變Grid控制項中的MinWidth或MinHeight屬性、一列或一行。
  •   動畫最常用的屬性是渲染變換。可使用變換移動或翻轉元素(TranslateTransform)、旋轉元素(RotateTransform)、縮放或扭曲元素(ScaleTransform)等。通過仔細地使用變換,有時可避免在動畫中硬編碼尺寸和位置。它們也繞過了WPF佈局系統,比直接作用於元素大小或位置的其他方法速度更快。
  •   動態改變元素錶面的較好方法是修改畫刷屬性。可使用ColorAnimation改變顏色或其他動畫對象來變換更複雜畫刷的屬性,如漸變中的偏移。

  接下來的示例演示瞭如何動態改變變換和畫刷,以及如何使用更多的一些動畫類型。還將討論如何使用關鍵幀創建多段動畫、如何創建基於路徑的動畫和基於幀的動畫。

一、動態變換

  變換提供了自定義元素的最強大方式之一。當使用變換時,不只是改變元素的邊界,而且會移動、翻轉、扭曲、拉伸、放大、縮小或旋轉元素的整個可視化外觀。例如,可通過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>
RotateButton

  在單擊任何按鈕時,都會在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>
RotateButtonWithLayout

  動態改變多個變換

  可很容易地組合使用變換。實際上這是很容易——只需要使用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>
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、編程語言簡介 機器語言 電腦能直接理解的就是二進位指令,所以機器語言就是直接用二進位編程,這意味著機器語言是直接操作硬體的,因此機器語言屬於低級語言, 此處的低級指的是底層、貼近電腦硬體(貼近代指需要詳細瞭解電腦硬體細節、直接控制硬體) 彙編語言 是一種用於電子電腦、微處理器、微控制器或 ...
  • 首先提供兩個XML文件 XML代碼如下: 1、Example.xml <?xml version="1.0" encoding="ISO-8859-1"?> <chart> <series title="Series1" type="Point" color="#FF0000"> <points c ...
  • 一、編程語言介紹 編程語言的分類: 機器語言(奴隸的母語):直接用二進位數0,1構成的指令去編寫程式,即用電腦能夠直接理解的二進位指令編寫程式,電腦可以無障礙理解。 優點:執行效率最高 缺點:開發效率最低、跨平臺性差 彙編語言:用英文標簽取代二進位去編寫程式 優點:執行效率高 缺點:開發效率低、 ...
  • Bottle是一個快速、簡潔、輕量級的基於WSIG的微型Web框架,此框架只由一個 .py 文件,除了Python的標準庫外,其不依賴任何其他模塊。 1 pip install bottle 2 easy_install bottle 3 apt-get install python-bottle ...
  • [TOC] std::copy是C++標準庫中的演算法介面,主要用於兩個容器間的複製,據說其效率要優於自己用for迴圈逐個複製。之前一直非常混淆其中的用法,這裡總結了幾個例子如下: cpp include include include include using namespace std; int ...
  • Python裡面如何拷貝一個對象? http://blog.csdn.net/sharkw/article/details/1934090 標準庫中的copy模塊提供了兩個方法來實現拷貝.一個方法是copy,它返回和參數包含內容一樣的對象. 使用deepcopy方法,對象中的屬性也被覆制 Pytho ...
  • 使用 resx 文件,可以動態切換語言,使用如下類: public class LanguageManager : INotifyPropertyChanged { private readonly ResourceManager _resourceManager; private static r ...
  • 1、簡述 private、 protected、 public、 internal 修飾符的訪問許可權。private : 私有成員, 在類的內部才可以訪問。protected : 保護成員,該類內部和繼承類中可以訪問。public : 公共成員,完全公開,沒有訪問限制。internal: 當前程式集 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...