WPF 動畫:同為控制元件不同命
1. 及格與優秀
讀大學的時候,有一門課的作業是用 PPT 展示。
但是我們很多同學都把 PPT 當做 Word 來用,就單純地往裡面堆文字。
大家都單純地從一頁堆積的文字翻到另一頁堆積的文字,以致於臺下的同學都聽不下去,包括那些以同樣的方式彙報的同學。
本來以為會在枯燥中期待下課的到來,直到有個叫幽靈東的同學彙報,他驚豔到了我們。
相比別人單純地堆積文字,他更多的採用圖片+較少的文字的方式。
同時,那些圖片和文字的出現、出現順序、消失,都採用了動畫。這些經過設計的動畫,串聯起來之後,竟讓我們觀眾像是在看一部小動漫。
我們不禁都盯著螢幕,讚歎著正在呈現的動畫,同時又期待著下一個動畫。
2. 動起來的軟體
而對於我們的 Windows 軟體或 網站,在實現基礎的業務功能後,做一些"增值"的動畫效果,會讓我們的系統看起來,更有趣。
所以,有時候,我們會在滑鼠移入按鈕時,讓按鈕緩慢變大;
又或者開啟一個子視窗的時候,讓這個子視窗以動畫的方式出現在我們面前,例如:像一幅畫一樣,從左到右展開的方式。
這兩種動畫都是在控制元件的尺寸上做手腳。
然而,今天我們要講的重點是:
雖然按鈕(System.Windows.Controls.Button 類)和視窗(System.Windows.Window 類) 都繼承自 System.Window.Controls.ContentControl。
它們實現縮放動畫的方式,卻有些出入。
3. Button 的縮放實現
當你想對 Button 進行縮放時,你可以通過對不同的屬性分發動畫,來實現該效果。
3.1 基於 Width 和 Height 的動畫
即:將動畫分發給 Button 的 Width 和 Height 屬性,從而實現 Button 的縮放。
3.1.1 程式碼
<Style TargetType="{x:Type Button}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="180" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="180" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever"/> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> </Style.Triggers> </Style>
3.1.2 效果

image
3.2 基於 RenderTransform 的動畫
跟 Width 一樣,RenderTransform 也是 Button 的依賴項屬性。
它的值型別為 System.Windows.Media.Transform,這個類是定義在二維平面上啟用變換的功能,包括旋轉、縮放、扭曲、平移。
System.Windows.Media.Transform 的子類 System.Windows.Media.ScaleTransform ,就是實現在二維 x-y 座標系內縮放物件。
所以下面我們會用到它。
3.2.1 程式碼
<Style TargetType="{x:Type Button}" x:Key="zoomByScale"> <!--設定元素的重點為轉換的中心點--> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/> <!--設定轉換資訊為(或往轉換資訊裡新增)縮放--> <Setter Property="RenderTransform"> <Setter.Value> <ScaleTransform></ScaleTransform> </Setter.Value> </Setter> <!--滑鼠移入時,將動畫分發給 ScaleTransform 的 ScaleX、ScaleY 屬性--> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" From="1" To="1.2" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="0:0:6"/> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="1.2" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="0:0:6"/> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> </Style.Triggers> </Style>
3.2.2 效果

image
3.2.3 附件
屬性 | 摘要 | 備註 |
---|---|---|
RenderTransform | 獲取或設定影響此元素的呈現位置的轉換資訊 | 依賴項屬性,值為 System.Windows.Media.Transform 類的物件 |
ScaleTransform | 在二維 x-y 座標系內縮放物件 | 繼承自 Transform 類 |
ScaleX | 獲取或設定 x 軸的縮放比例 | ScaleTransform 的成員。預設值為 1 |
RotateTransform | 在 二維 x-y 座標系內圍繞指定點按照順時針方向旋轉物件。 | 繼承自 Transform 類 |
Angle | 獲取或設定順時針旋轉角度(以度為單位) | RotateTransform 的成員。預設值為 0 |
3.3 Viewbox 的一次性縮放
3.3.1 程式碼
<Grid Width="300" Height="300"> <Viewbox Width="150" Height="150" x:Name="vb_ChangeSizeButton"> <Button Width="150" Height="150" Content="Viewbox 帶我飛"/> </Viewbox> <StackPanel Margin="15" HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal"> <Button Margin="10" Click="Zoom_Big" Cursor="Hand" Content="+"/> <Button Margin="10" Click="Zoom_Small" Cursor="Hand" Content="——" /> </StackPanel> </Grid>
// 將 viewBox 的尺寸放大到 1.2 倍 private void Zoom_Big(object sender, RoutedEventArgs e) { vb_ChangeSizeButton.Width = vb_ChangeSizeButton.Width * 1.2; vb_ChangeSizeButton.Height = vb_ChangeSizeButton.Height * 1.2; } // 將 viewBox 的尺寸除以 1.2 private void Zoom_Small(object sender, RoutedEventArgs e) { vb_ChangeSizeButton.Width = vb_ChangeSizeButton.Width / 1.2; vb_ChangeSizeButton.Height = vb_ChangeSizeButton.Height / 1.2; }
3.3.2 解釋
當 viewbox 的尺寸改變時,子元素大小縮放的倍數等同於 viewbox 的縮放倍數。
3.3.3 效果

image
4. Window 的縮放
對於 Window 的動畫,我們一般是分發給它的 RenderTransform 屬性。
Width 或 Height 雖然也可以,但是無法實現同時縮放寬高。
既然提到了,我們就先來看看。
4.1 Window 基於 Width 和 Height 的動畫
4.1.1 動畫只分發給 Width 屬性
4.1.1.1 程式碼
<Window.Style> <Style TargetType="Window" BasedOn="{StaticResource {x:Type Window}}"> <Setter Property="AllowsTransparency" Value="True"/> <Setter Property="WindowStyle" Value="None"/> <Setter Property="ResizeMode" Value="CanResizeWithGrip" /> <Style.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" From="0" To="300" Duration="0:0:0.6"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Style>
4.1.1.2 效果

image
這看起來還挺正常。但如果你想要寬高同時放大呢?
4.1.2 Window 基於 Width 和 Height 的蹩腳動畫
4.1.2.1 程式碼
<Window.Style> <Style TargetType="Window" BasedOn="{StaticResource {x:Type Window}}"> ... <Style.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" From="0" To="300" Duration="0:0:0.6"/> <DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="300" Duration="0:0:0.6"/> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Style>
4.1.2.2 效果

image
這種並不是我們想要的結果。
把兩個動畫的開始時間錯開,也不是使用者想要的。
所以,我們更喜歡將動畫分發給 RenderTransform 屬性。
4.2 window 基於 RenderTransform 的動畫
這裡將僅對 ScaleX 放大的情況一筆帶過了..

image
4.2.1 將動畫分發給 ScaleX 和 ScaleY,實現寬高同時放大的程式碼
<Window.Style> <Style TargetType="Window" BasedOn="{StaticResource {x:Type Window}}"> ... <Setter Property="RenderTransform"> <Setter.Value> <ScaleTransform></ScaleTransform> </Setter.Value> </Setter> <Style.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX" From="0" To="1" Duration="0:0:0.6" AccelerationRatio="0.7"/> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1" Duration="0:0:0.6" AccelerationRatio="0.7"/> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Style.Triggers> </Style> </Window.Style>
4.2.2 效果

image