当前位置: 首页>编程笔记>正文

WPF學習(12)動畫

WPF學習(12)動畫

本篇來學習WPF的動畫。什么是動畫?動畫就是一系列幀。在WPF中,動畫就是在一段時間內修改依賴屬性值的行為,它是基于時間線Timeline的。有人會說,要動畫干嘛,華而不實,而且添加了額外的資源消耗而影響性能。盡管如此,適當的使用動畫卻可以使你的程序富有更好的表現力和交互性。更加可喜的是,WPF提供了豐富的動畫支持,大部分的動畫都可以直接通過XAML來呈現而不用去寫繁瑣的cs代碼。在System.Windows.Media.Animation命名空間中,我們發現了許多的類,大體可歸類為這么三種:基于線性內插算法動畫(簡單動畫)、基于KeyFrame動畫和基于路徑動畫。下面來分別介紹這三種動畫:

首先以圖來說明類的繼承層次:

這張粗糙的圖只是以Double類型的動畫為例,因為它是比較全的,全面提到的三種動畫類它都具備。支持KeyFrame的類型最多,其次是線性內插,最后是路徑,只有三種,除了Double外,還有Matrix和Point。

1.基于線性內插算法動畫(簡單動畫)

基于線性內插動畫是在一個開始值到一個結束值之間以逐步增加的方式來改變屬性值。有這么幾個重要的屬性:

From:開始值,當忽略該屬性時,動畫默認從其使用者屬性值開始,所以其使用者屬性在執行動畫前要被賦值

To:結束值,當忽略該屬性時,動畫默認從其使用者屬性值結束,所以其使用者屬性在執行動畫前要被賦值

By:遞增值,和To一樣和From搭配使用,當然可以用To來代替它

Duration:時間間隔,動畫的執行時間,可以將一個TimeSpan值直接給它賦值,會自動隱式轉換

AutoReverse:在到達結束值后,是否以相反的順序回到From值

RepeatBehavior:重復行為,默認不重復,可設置其為Forever來重復動畫,也可以通過構造器來設置次數和Timespan

FillBehavior:在活動周期結束后的行為方式,有HoldEnd(默認值)和Stop兩個值,HoldEnd表示在動畫結束后保持該屬性值,而Stop則表示在動畫結束后回到原來值(不是From值)

例子如下:

private void Button_Click(object sender, RoutedEventArgs e){Button button = (Button)sender;DoubleAnimation widthAnimation = new DoubleAnimation();widthAnimation.From = 200;widthAnimation.To = 100;//widthAnimation.AutoReverse = true;widthAnimation.Duration = TimeSpan.FromSeconds(5);widthAnimation.FillBehavior = FillBehavior.Stop;//RepeatBehavior = RepeatBehavior.Forever;widthAnimation.Completed += widthAnimation_Completed;button.BeginAnimation(Button.WidthProperty, widthAnimation);}void widthAnimation_Completed(object sender, EventArgs e){//to do ...}
View Code

我們再來通過Trigger在Xaml頁面舉個例子:

<Window x:Class="AnimationDemo.Window1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window1" Height="300" Width="300"><Window.Resources><Style TargetType="Grid" x:Key="gridKey"><Style.Triggers><EventTrigger RoutedEvent="Grid.MouseEnter"><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX"From="0" To="1" Duration="0:0:2" AccelerationRatio="1" /><DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY"From="0" To="1" Duration="0:0:2" AccelerationRatio="1" /><DoubleAnimation Storyboard.TargetProperty="RenderTransform.Children[1].Angle"From="70" To="0" Duration="0:0:2" /></Storyboard></BeginStoryboard></EventTrigger></Style.Triggers></Style></Window.Resources><Grid><Grid x:Name="grid" Background="Coral" Style="{StaticResource gridKey}"><Grid.RenderTransform><TransformGroup><ScaleTransform /><RotateTransform /></TransformGroup></Grid.RenderTransform><TextBlock TextWrapping="Wrap">烽火綿延,戰場泣殺,誰留鋒刃惹人嘲。弒紅雙眸,血刃亂劃,拼得素衣染淚頰。陰風低吼,暴雨怒下,徒留艷紅干不涸。只盼茍活,還能歸家,再不問誓死拼殺。此生惟愿與你共活,難得執手畫青絲滄桑,不管紅塵凡事俗爭,只此一生為你描眉點唇。任蒼天笑我懦弱,我依舊充耳不顧,為你甘愿卸甲耕種,只盼安穩走完一生。如若我此去再無音信,愿你重披嫁衣,讓他代我此生疼你若寶。如若我此去再無歸期,愿你沖洗記憶,讓他重新入你心底。我只不甘,再不見你如斯容顏。我只不愿,再見你眼眸帶淚顏。我只不嘆,再不見你懷中展顏。我只不再,再見盼來世重逢顏。風煙輕擦,我已杳無牽掛,獨余你,是我心底最不愿觸及的殤。</TextBlock></Grid></Grid>
</Window>
View Code

?效果如下:

2.基于KeyFrame動畫

基于KeyFrame動畫可以很方便地實現多個分段和不規則移動的動畫。雖然前面的線性內插動畫也可通過BeginTime構建多個連續動畫來實現,但是顯得很復雜。基于KeyFrame動畫是由多個段構成的,每一段表示動畫的開始值和最終值或者中間值。主要有一個×××KeyFrameCollection類型的KeyFrames屬性,它是一個×××KeyFrame的集合。×××KeyFrame有一個KeyTime類型的KeyTime屬性和一個Object類型的Value屬性,后者表示關鍵幀的目標值,前者是到達關鍵幀的目標值的時間。拿Double類型來說吧,DoubleAnimationUsingKeyFrames有個DoubleKeyFrameCollection類型的KeyFrames屬性,是DoubleKeyFrame

抽象類型的集合,DoubleKeyFrame類型又有這么幾個子類:

LinearDoubleKeyFrame:通過使用線性內插(線性關鍵幀),可以在前一個關鍵幀的Double值及其自己的Value值之間進行動畫處理

DiscreteDoubleKeyFrame:通過使用離散內插(離散關鍵幀),可以在前一個關鍵幀的Double值及其自己的Value值之間進行動畫處理

SplineDoubleKeyFrame:通過使用樣條內插(樣條關鍵幀),有KeySpline類型的KeySpline屬性,它表示關鍵幀進度的三次方貝塞爾曲線

EasingDoubleKeyFrame:通過使用緩動關鍵幀,將EasingFunction和關鍵幀動畫關聯,有IEasingFunction類型的EasingFunction屬性

例子如下:

<Window x:Class="AnimationDemo.Window2"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window2" Height="300" Width="300"><Window.Resources><Style TargetType="Grid" x:Key="gridKey"><Style.Triggers><EventTrigger RoutedEvent="Grid.MouseEnter"><BeginStoryboard><Storyboard><DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="RenderTransform.Children[0].ScaleX"AccelerationRatio="1"><DoubleAnimationUsingKeyFrames.KeyFrames><DoubleKeyFrameCollection><LinearDoubleKeyFrame KeyTime="0:0:0" Value="0" /><LinearDoubleKeyFrame KeyTime="0:0:2" Value="1" /></DoubleKeyFrameCollection></DoubleAnimationUsingKeyFrames.KeyFrames></DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="RenderTransform.Children[0].ScaleY"AccelerationRatio="1"><DoubleKeyFrameCollection><LinearDoubleKeyFrame KeyTime="0:0:0" Value="0" /><LinearDoubleKeyFrame KeyTime="0:0:2" Value="1" /></DoubleKeyFrameCollection></DoubleAnimationUsingKeyFrames><DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="RenderTransform.Children[1].Angle"AccelerationRatio="1"><DoubleKeyFrameCollection><LinearDoubleKeyFrame KeyTime="0:0:0" Value="70" /><LinearDoubleKeyFrame KeyTime="0:0:2" Value="50" /><SplineDoubleKeyFrame KeyTime="0:0:4" Value="30"><SplineDoubleKeyFrame.KeySpline><KeySpline ControlPoint1="0.3,0.7" ControlPoint2="0.7,0.3" /></SplineDoubleKeyFrame.KeySpline></SplineDoubleKeyFrame><EasingDoubleKeyFrame KeyTime="0:0:6" Value="0"><EasingDoubleKeyFrame.EasingFunction><BounceEase /></EasingDoubleKeyFrame.EasingFunction></EasingDoubleKeyFrame></DoubleKeyFrameCollection></DoubleAnimationUsingKeyFrames></Storyboard></BeginStoryboard></EventTrigger></Style.Triggers></Style></Window.Resources><Grid><Grid x:Name="grid" Background="Coral" Style="{StaticResource gridKey}"><Grid.RenderTransform><TransformGroup><ScaleTransform /><RotateTransform /></TransformGroup></Grid.RenderTransform><TextBlock TextWrapping="Wrap">烽火綿延,戰場泣殺,誰留鋒刃惹人嘲。弒紅雙眸,血刃亂劃,拼得素衣染淚頰。陰風低吼,暴雨怒下,徒留艷紅干不涸。只盼茍活,還能歸家,再不問誓死拼殺。此生惟愿與你共活,難得執手畫青絲滄桑,不管紅塵凡事俗爭,只此一生為你描眉點唇。任蒼天笑我懦弱,我依舊充耳不顧,為你甘愿卸甲耕種,只盼安穩走完一生。如若我此去再無音信,愿你重披嫁衣,讓他代我此生疼你若寶。如若我此去再無歸期,愿你沖洗記憶,讓他重新入你心底。我只不甘,再不見你如斯容顏。我只不愿,再見你眼眸帶淚顏。我只不嘆,再不見你懷中展顏。我只不再,再見盼來世重逢顏。風煙輕擦,我已杳無牽掛,獨余你,是我心底最不愿觸及的殤。</TextBlock></Grid></Grid>
</Window>
View Code

3.基于Path的動畫

基于Path的動畫是一種很靈活的執行動畫的方式,它一般被用于沿著一條路徑來移動可視對象。它主要有一個PathGeometry類型的PathGeometry屬性和PathAnimationSource類型的Source屬性,前者表示指定用于生成此動畫輸出值的幾何圖形,后者表示指定PathGeometry屬性的方位,默認值為PathAnimationSource.X,表示指定在前進過程中沿動畫序列路徑的 X 坐標偏移量。

<Window x:Class="AnimationDemo.Window3"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window3" Height="500" Width="600"><Window.Resources><PathGeometry x:Key="pathKey"><PathGeometry.Figures><PathFigure IsClosed="True"><PathFigure.Segments><LineSegment Point="300,2" IsStroked="True" IsSmoothJoin="True"/><LineSegment Point="300,80" IsStroked="True" IsSmoothJoin="True"/><LineSegment Point="2,80" IsStroked="True" IsSmoothJoin="True"/></PathFigure.Segments></PathFigure></PathGeometry.Figures></PathGeometry><Style x:Key="ImgKey" TargetType="Image"><Style.Triggers><EventTrigger RoutedEvent="Loaded"><BeginStoryboard><Storyboard><DoubleAnimationUsingPath Storyboard.TargetProperty="(Canvas.Left)"PathGeometry="{StaticResource pathKey}" Duration="0:0:5" RepeatBehavior="Forever" Source="X"></DoubleAnimationUsingPath><DoubleAnimationUsingPath Storyboard.TargetProperty="(Canvas.Top)"PathGeometry="{StaticResource pathKey}" Duration="0:0:5" RepeatBehavior="Forever" Source="Y"></DoubleAnimationUsingPath></Storyboard></BeginStoryboard></EventTrigger></Style.Triggers></Style></Window.Resources><Canvas><Path Stroke="Red" StrokeThickness="1" Data="{StaticResource pathKey}" Canvas.Left="10" Canvas.Top="10" /><Image x:Name="img" Canvas.Left="15" Canvas.Top="15" Style="{StaticResource ImgKey}"><Image.Source><DrawingImage><DrawingImage.Drawing><GeometryDrawing Brush="LightSteelBlue"><GeometryDrawing.Pen><Pen Brush="Black" Thickness="1" /></GeometryDrawing.Pen><GeometryDrawing.Geometry><EllipseGeometry Center="10,10" RadiusX="3" RadiusY="3" /></GeometryDrawing.Geometry></GeometryDrawing></DrawingImage.Drawing></DrawingImage></Image.Source></Image></Canvas>
</Window>
View Code

4.動畫在XAML中的載體--Storyboard

正如開始所說,大部分基于屬性的動畫都是可以直接在XAML中表達的,但是它需要一個載體,或者叫一個容器,它就是Storyboard,它是動畫和希望應用動畫的屬性的橋梁。Storyboard經常被BeginStoryboard包裝作為EventTrigger的TriggerAction,這里BeginStoryboard是一個密封類,而不是FrameworkElement里面的那個方法,它表示一個觸發器操作,該操作可啟動Storyboard 并將其動畫分發給動畫的目標對象和屬性。Storyboard繼承自ParallelTimeline,意味著它可以并行運行多個子時間線,而且它具有控制動畫播放的能力,例如Pause、Resume、Skip和Stop等。它用TargetName附加屬性表示動畫的作用者,用TargetProperty附加屬性來指定TargetName的希望改變的屬性。

<Window x:Class="AnimationDemo.Window4"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window4" Height="300" Width="300"><Window.Resources><Style TargetType="ListBoxItem"><Style.Triggers><EventTrigger RoutedEvent="MouseEnter"><BeginStoryboard HandoffBehavior="Compose"><Storyboard><DoubleAnimation Storyboard.TargetProperty="FontSize" BeginTime="0:0:0.5" Duration="0:0:0.2" To="18"/></Storyboard></BeginStoryboard></EventTrigger><EventTrigger RoutedEvent="MouseLeave"><BeginStoryboard HandoffBehavior="Compose"><Storyboard><DoubleAnimation Storyboard.TargetProperty="FontSize" BeginTime="0:0:0.5" Duration="0:0:0.2" /></Storyboard></BeginStoryboard></EventTrigger></Style.Triggers></Style></Window.Resources><Grid><ListBox><ListBoxItem>Jello</ListBoxItem><ListBoxItem>Taffy</ListBoxItem><ListBoxItem>Jim</ListBoxItem><ListBoxItem>Lily</ListBoxItem></ListBox></Grid>
</Window>
View Code

5.基于Frame的動畫

前面介紹的都是基于屬性的動畫,還有一種比較的動畫,既是基于幀的動畫,只需要訂閱CompositionTarget的靜態Rendering事件即可,它在呈現組合樹中的對象之前發生,從而為每幀獲取內容。

Xaml代碼:

<Window x:Class="AnimationDemo.Window7"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Window7" Height="300" Width="300"><Grid Margin="3"><Grid.RowDefinitions><RowDefinition Height="Auto"></RowDefinition><RowDefinition></RowDefinition></Grid.RowDefinitions><StackPanel Orientation="Horizontal"><Button Margin="3" Padding="3" Click="cmdStart_Clicked">Start</Button><Button Margin="3" Padding="3" Click="cmdStop_Clicked">Stop</Button></StackPanel><Canvas Name="canvas" Grid.Row="1" Margin="3"></Canvas></Grid>
</Window>
View Code

cs代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;namespace AnimationDemo
{/// <summary>/// Window7.xaml 的交互邏輯/// </summary>public partial class Window7 : Window{public Window7(){InitializeComponent();}private bool rendering = false;private void cmdStart_Clicked(object sender, RoutedEventArgs e){if (!rendering){ellipses.Clear();canvas.Children.Clear();CompositionTarget.Rendering += RenderFrame;rendering = true;}}private void cmdStop_Clicked(object sender, RoutedEventArgs e){StopRendering();}private void StopRendering(){CompositionTarget.Rendering -= RenderFrame;rendering = false;}private List<EllipseInfo> ellipses = new List<EllipseInfo>();private double accelerationY = 0.1;private int minStartingSpeed = 1;private int maxStartingSpeed = 50;private double speedRatio = 0.1;private int minEllipses = 20;private int maxEllipses = 100;private int ellipseRadius = 10;private void RenderFrame(object sender, EventArgs e){if (ellipses.Count == 0){// Animation just started. Create the ellipses.int halfCanvasWidth = (int)canvas.ActualWidth / 2;Random rand = new Random();int ellipseCount = rand.Next(minEllipses, maxEllipses + 1);for (int i = 0; i < ellipseCount; i++){Ellipse ellipse = new Ellipse();ellipse.Fill = Brushes.LimeGreen;ellipse.Width = ellipseRadius;ellipse.Height = ellipseRadius;Canvas.SetLeft(ellipse, halfCanvasWidth + rand.Next(-halfCanvasWidth, halfCanvasWidth));Canvas.SetTop(ellipse, 0);canvas.Children.Add(ellipse);EllipseInfo info = new EllipseInfo(ellipse, speedRatio * rand.Next(minStartingSpeed, maxStartingSpeed));ellipses.Add(info);}}else{for (int i = ellipses.Count - 1; i >= 0; i--){EllipseInfo info = ellipses[i];double top = Canvas.GetTop(info.Ellipse);Canvas.SetTop(info.Ellipse, top + 1 * info.VelocityY);if (top >= (canvas.ActualHeight - ellipseRadius * 2 - 10)){// This circle has reached the bottom.// Stop animating it.
                        ellipses.Remove(info);}else{// Increase the velocity.info.VelocityY += accelerationY;}if (ellipses.Count == 0){// End the animation.// There's no reason to keep calling this method// if it has no work to do.
                        StopRendering();}}}}}public class EllipseInfo{public Ellipse Ellipse{get;set;}public double VelocityY{get;set;}public EllipseInfo(Ellipse ellipse, double velocityY){VelocityY = velocityY;Ellipse = ellipse;}}
}
View Code

?

轉載于:https://www.cnblogs.com/jellochen/p/3592963.html

https://www.nshth.com/bcbj/338783.html
>

相关文章:

  • pdf翻譯網站,1 Trillion Dollar Refund – How To Spoof PDF Signatures——欺騙PDF簽名
  • 如何創建一個抽象類,創建具體的產品,并繼承產品抽象類
  • 主從庫理論知識-主從同步如何實現?
  • Tomcat環境變量配置,Mybatis的配置文件參數詳解
  • I Am You,POJ 3130 How I Mathematician Wonder What You Are! 半平面交
  • 要學vue需要學什么基礎知識,第一章 Vue基礎入門
  • win7下安裝win10,win10下安裝Ubuntu18.10雙系統
  • vmplayer怎么使用烏邦圖,烏邦圖環境安裝
  • 計算機專業要不要考研——寫的很棒
  • redisson看門狗原理,記錄一次redis漏洞攻擊
  • 任意波形發生器,基于單片機信號波形發生器系統設計-畢設課設
  • 嵌入式驅動,嵌入式Linux驅動大全問世,十年磨一劍,視頻!服務!新老客戶都有大折扣!
  • socket連接器v2下載,Netty(一)基礎socketchannel,Buffer,selector黏包 半包解決 實戰
  • 大一c語言程序設計筆記,吉林大學2013級大一下學期程序設計作業:同學通訊錄系統
  • 暑期小學生計算機培訓班,青島小學生學習編程暑假
  • 熊貓毛小喵喵去哪里了,小西貝、何小喵看熊貓之觀察者設計模式
  • 如何用c語言比較兩個數的大小,如何用C語言求兩個數的較大值
  • 輾轉相除法求最小公倍數的方法,更相減損術--最大公約數
  • 輾轉相除法求最小公倍數的方法,如何求出兩個整數的最大公約數
  • 李新義的書畫藝術,中國現代書畫家——譚奇中、李義象、高俊鵬等
  • 海底撈張勇名言,致張勇先生一封信:海底撈的“七宗罪”!
  • WPF學習(12)動畫
  • ui自動化測試工具,移動端UI自動化之appium的使用(二)
  • 爬蟲網站,Search For Free —— 新聞爬蟲及爬取結果的查詢網站
  • tenda騰達無線設置,騰達F6路由器無線中繼功能設置
  • 斐波那契數列、小青蛙跳臺階
  • OJ每日一練——小青蛙上臺階
  • 小青蛙貝葉斯
  • 小青蛙走臺階問題
  • MySQL數據庫下載,NAVICAT FOR MYSQL存儲過程