【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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...