wpf動畫——緩動動畫Animation Easing(2)


在wpf或者silverlight中,經常用到Storyboard來完成一些動畫的效果,本例將說明使用緩動函數關聯動畫 Animation Easing的方法:


1.新建一個wpf應用程序(silverlight亦可),xaml簡單修改布局如下:

 

<Window x:Class="WpfApplication51.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid Name="root" Loaded="root_Loaded">
      
    </Grid>
</Window>

 

對應我們添加了一個Grid的Loaded事件處理:

 private void root_Loaded(object sender, RoutedEventArgs e)
        {
        }

然后我們准備一個故事布景canvas,一個圓elipse,幾條准線line,用來描述一個緩動動畫過程:

        Canvas canvas = new Canvas()
        {
            Height = 300,
            Width = 400,
            Background = new SolidColorBrush(Colors.Silver),
        };
        Ellipse e1 = new Ellipse()
        {
            Height = 20,
            Width = 20,
            Fill = new SolidColorBrush(Colors.Green),
            RenderTransform = new TranslateTransform(-10, -10)
        };
        Line l1 = new Line()
        {
            Stroke = new SolidColorBrush(Colors.Gray),
            X1 = 0,
            Y1 = 100,
            X2 = 400,
            Y2 = 100
        };
        Line l2 = new Line()
        {
            Stroke = new SolidColorBrush(Colors.Gray),
            X1 = 0,
            Y1 = 200,
            X2 = 400,
            Y2 = 200
        };

    private void root_Loaded(object sender, RoutedEventArgs e)
        {
            this.root.Children.Add(this.canvas);
            this.canvas.Children.Add(this.l1);
            this.canvas.Children.Add(this.l2);
            this.canvas.Children.Add(this.e1);
            this.e1.SetValue(Canvas.TopProperty, 200.0);
            this.e1.SetValue(Canvas.LeftProperty, 50.0);
          }

這里我們讓動畫的主角-圓,Transform變換了一下,縱橫坐標都偏移了一個半徑距離,目的是讓圓的圓心位置在原點位置

F5運行一下,得到一個開幕:


2.接下來我們讓這個圓動起來,

首先說明一下,一個動畫故事StoryBoard可以由很多的關鍵幀集合DoubleAnimationUsingKeyFrames組成,

每個DoubleAnimationUsingKeyFrames關鍵幀集合是由很多KeyFrame幀組成,

每個KeyFrame幀記錄了時間點keytime,目標值 value,以及是否會用到的EasingFunction緩動處理函數,

“工欲善其事,必先利其器”,so,先寫兩個方法用來添加豐富我們的動畫吧:

        /// <summary>
        /// story故事版增加key關鍵幀集
        /// </summary>
        /// <param name="sb">story故事版</param>
        /// <param name="ks">key關鍵幀集</param>
        /// <param name="dobj">動畫目標</param>
        /// <param name="property">動畫屬性</param>
        void StoryAddKey(Storyboard sb, DoubleAnimationUsingKeyFrames ks, DependencyObject dobj, PropertyPath property)
        {
            sb.Children.Add(ks);
            Storyboard.SetTarget(ks, dobj);
            Storyboard.SetTargetProperty(ks, property);
        }

        /// <summary>
        /// key關鍵幀集增加幀frame
        /// </summary>
        /// <param name="kfs">關鍵幀集</param>
        /// <param name="ms">時間點</param>
        /// <param name="value">數值</param>
        /// <param name="efun">緩動處理</param>
        void KeyAddFrame(DoubleAnimationUsingKeyFrames kfs, double ms, double value, EasingFunctionBase efun)
        {
            EasingDoubleKeyFrame kf = new EasingDoubleKeyFrame();
            kf.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(ms));
            kf.Value = value;
            kf.EasingFunction = efun;
            kfs.KeyFrames.Add(kf);
        }

        /// <summary>
        /// property屬性鏈
        /// </summary>
        DependencyProperty[] propertyChain = new DependencyProperty[]
        {
            Canvas.TopProperty,
            Canvas.LeftProperty
        };

這里的屬性鏈,在處理動畫目標屬性的時候,可以很方便的選擇要處理的屬性,

然后用我們建的方法去添加一個動畫吧,

動畫的過程是讓圓在一個二維坐標系中移動:

y縱軸方向:讓圓從下面的准線移動到上面的准線;

x橫軸方向:讓圓水平移動;

       Storyboard sb = new Storyboard()

        {
           
      FillBehavior = FillBehavior.HoldEnd
 
       };


    private void root_Loaded(object sender, RoutedEventArgs e) { this.root.Children.Add(this.canvas); this.canvas.Children.Add(this.l1); this.canvas.Children.Add(this.l2); this.canvas.Children.Add(this.e1); this.e1.SetValue(Canvas.TopProperty, 200.0); this.e1.SetValue(Canvas.LeftProperty, 50.0); //縱坐標動畫 DoubleAnimationUsingKeyFrames ks1e1 = new DoubleAnimationUsingKeyFrames(); this.KeyAddFrame(ks1e1, 0.0, 200.0, null); this.KeyAddFrame(ks1e1, 2.0, 100.0, new BackEase() { EasingMode = EasingMode.EaseOut, Amplitude = 1.0 }); this.StoryAddKey(sb, ks1e1, this.e1, new PropertyPath("(0)", this.propertyChain)); //橫坐標動畫 DoubleAnimationUsingKeyFrames ks2e1 = new DoubleAnimationUsingKeyFrames(); this.KeyAddFrame(ks2e1, 0.0, 50.0, null); this.KeyAddFrame(ks2e1, 2.0, 350.0, null); this.StoryAddKey(sb, ks2e1, this.e1, new PropertyPath("(1)", this.propertyChain)); sb.Begin(); }

這里我們使用了緩動動畫中的一種BackEase,設置模式為EaseOut(動畫末尾處理),當然你也可以去show其他幾種模式:

EaseIn:動畫初時處理;

EaseInOut:動畫初時及末尾均處理;

這里還設置了Amplitude,其默認值就為1.0,代表緩動動畫的幅度,

F5運行一下,發現圓已經按照我們預想的那樣去移動,

接下來我們試着把圓移動的軌跡繪制出來,更加直觀的觀察緩動的處理過程。


3.繪制的方法,添加的代碼如下:

        DrawingVisual drawingVisual = new DrawingVisual();
        DrawingContext drawingContext;
        Point pe1 = new Point(50.0, 200.0);
        Image image = new Image()
        {
            Width = 400,
            Height = 300
        };

        private void root_Loaded(object sender, RoutedEventArgs e)
        {
            this.root.Children.Add(this.canvas);
            this.canvas.Children.Add(this.l1);
            this.canvas.Children.Add(this.l2);
            this.canvas.Children.Add(this.e1);
            this.e1.SetValue(Canvas.TopProperty, 200.0);
            this.e1.SetValue(Canvas.LeftProperty, 50.0);

            //縱坐標動畫
            DoubleAnimationUsingKeyFrames ks1e1 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks1e1, 0.0, 200.0, null);
            this.KeyAddFrame(ks1e1, 2.0, 100.0, new BackEase() { EasingMode = EasingMode.EaseOut, Amplitude = 1.0 });
            this.StoryAddKey(sb, ks1e1, this.e1, new PropertyPath("(0)", this.propertyChain));

            //橫坐標動畫
            DoubleAnimationUsingKeyFrames ks2e1 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks2e1, 0.0, 50.0, null);
            this.KeyAddFrame(ks2e1, 2.0, 350.0, null);
            this.StoryAddKey(sb, ks2e1, this.e1, new PropertyPath("(1)", this.propertyChain));

                     this.canvas.LayoutUpdated += new EventHandler(canvas_LayoutUpdated);
            drawingContext = drawingVisual.RenderOpen();
            sb.Completed += new EventHandler(sb_Completed);
            sb.Begin();
        }

        void sb_Completed(object sender, EventArgs e)
        {
            drawingContext.Close();
            RenderTargetBitmap composeImage = new RenderTargetBitmap(400, 300, 0, 0, PixelFormats.Pbgra32);
            composeImage.Render(drawingVisual);
            this.image.Source = composeImage;
            this.canvas.LayoutUpdated -= canvas_LayoutUpdated;
            this.canvas.Children.Add(image);
        }

        void canvas_LayoutUpdated(object sender, EventArgs e)
        {
            Point p1 = new Point(Convert.ToDouble(this.e1.GetValue(Canvas.LeftProperty)), Convert.ToDouble(this.e1.GetValue(Canvas.TopProperty)));
            drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green), 2.0), pe1, p1);
            pe1 = p1;

          }

這里用到了canvas內部元素布局改變的LayoutUpdated事件,每當圓移動改變自己坐標的時候,就記錄下坐標,移動到下一個坐標時,連接兩個坐標的連線即移動的路徑

這里的繪制使用drawingContext.DrawLine繪制路徑線段,動畫結束時將繪制得到的DrawingVisual結果放到一個與canvas舞台大小相同的image中用以呈現,

F5運行一下,結果如下圖:


4.試着修改緩沖動畫的種類,以及幅度Amplitude的值,亦可多show幾個圓同時移動,來觀察他們的區別,以下給出完整的參考代碼(兩個圓的軌跡比較):

    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        #region
        Canvas canvas = new Canvas()
        {
            Height = 300,
            Width = 400,
            Background = new SolidColorBrush(Colors.Silver),
        };
        Ellipse e1 = new Ellipse()
        {
            Height = 20,
            Width = 20,
            Fill = new SolidColorBrush(Colors.Green),
            RenderTransform = new TranslateTransform(-10, -10)
        };
        Ellipse e2 = new Ellipse()
        {
            Height = 20,
            Width = 20,
            Fill = new SolidColorBrush(Colors.Yellow),
            RenderTransform = new TranslateTransform(-10, -10)
        };
        Line l1 = new Line()
        {
            Stroke = new SolidColorBrush(Colors.Gray),
            X1 = 0,
            Y1 = 100,
            X2 = 400,
            Y2 = 100
        };
        Line l2 = new Line()
        {
            Stroke = new SolidColorBrush(Colors.Gray),
            X1 = 0,
            Y1 = 200,
            X2 = 400,
            Y2 = 200
        };
        Storyboard sb = new Storyboard()
        {
            FillBehavior = FillBehavior.HoldEnd
        };
        DrawingVisual drawingVisual = new DrawingVisual();
        DrawingContext drawingContext;
        Point pe1 = new Point(50.0, 200.0);
        Point pe2 = new Point(50.0, 200.0);
        Image image = new Image()
        {
            Width = 400,
            Height = 300
        };
        #endregion

        #region

        /// <summary>
        /// story故事版增加key關鍵幀集
        /// </summary>
        /// <param name="sb">story故事版</param>
        /// <param name="ks">key關鍵幀集</param>
        /// <param name="dobj">動畫目標</param>
        /// <param name="property">動畫屬性</param>
        void StoryAddKey(Storyboard sb, DoubleAnimationUsingKeyFrames ks, DependencyObject dobj, PropertyPath property)
        {
            sb.Children.Add(ks);
            Storyboard.SetTarget(ks, dobj);
            Storyboard.SetTargetProperty(ks, property);
        }

        /// <summary>
        /// key關鍵幀集增加幀frame
        /// </summary>
        /// <param name="kfs">關鍵幀集</param>
        /// <param name="ms">時間點</param>
        /// <param name="value">數值</param>
        /// <param name="efun">緩動處理</param>
        void KeyAddFrame(DoubleAnimationUsingKeyFrames kfs, double ms, double value, EasingFunctionBase efun)
        {
            EasingDoubleKeyFrame kf = new EasingDoubleKeyFrame();
            kf.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(ms));
            kf.Value = value;
            kf.EasingFunction = efun;
            kfs.KeyFrames.Add(kf);
        }

        /// <summary>
        /// property屬性鏈
        /// </summary>
        DependencyProperty[] propertyChain = new DependencyProperty[]
        {
            Canvas.TopProperty,
            Canvas.LeftProperty
        };

        #endregion

        private void root_Loaded(object sender, RoutedEventArgs e)
        {
            this.root.Children.Add(this.canvas);
            this.canvas.Children.Add(this.l1);
            this.canvas.Children.Add(this.l2);
            this.canvas.Children.Add(this.e1);
            this.e1.SetValue(Canvas.TopProperty, 200.0);
            this.e1.SetValue(Canvas.LeftProperty, 50.0);
            this.canvas.Children.Add(this.e2);
            this.e2.SetValue(Canvas.TopProperty, 200.0);
            this.e2.SetValue(Canvas.LeftProperty, 50.0);

            #region e1

            //縱坐標動畫
            DoubleAnimationUsingKeyFrames ks1e1 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks1e1, 0.0, 200.0, null);
            this.KeyAddFrame(ks1e1, 2.0, 100.0, new BackEase() { EasingMode = EasingMode.EaseOut, Amplitude = 1.0 });
            this.StoryAddKey(sb, ks1e1, this.e1, new PropertyPath("(0)", this.propertyChain));

            //橫坐標動畫
            DoubleAnimationUsingKeyFrames ks2e1 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks2e1, 0.0, 50.0, null);
            this.KeyAddFrame(ks2e1, 2.0, 350.0, null);
            this.StoryAddKey(sb, ks2e1, this.e1, new PropertyPath("(1)", this.propertyChain));

            #endregion

            #region e2

            //縱坐標動畫
            DoubleAnimationUsingKeyFrames ks1e2 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks1e2, 0.0, 200.0, null);
            this.KeyAddFrame(ks1e2, 2.0, 100.0, null);
            this.StoryAddKey(sb, ks1e2, this.e2, new PropertyPath("(0)", this.propertyChain));

            //橫坐標動畫
            DoubleAnimationUsingKeyFrames ks2e2 = new DoubleAnimationUsingKeyFrames();
            this.KeyAddFrame(ks2e2, 0.0, 50.0, null);
            this.KeyAddFrame(ks2e2, 2.0, 350.0, null);
            this.StoryAddKey(sb, ks2e2, this.e2, new PropertyPath("(1)", this.propertyChain));

            #endregion

            this.canvas.LayoutUpdated += new EventHandler(canvas_LayoutUpdated);
            drawingContext = drawingVisual.RenderOpen();
            sb.Completed += new EventHandler(sb_Completed);
            sb.Begin();
        }

        void sb_Completed(object sender, EventArgs e)
        {
            drawingContext.Close();
            RenderTargetBitmap composeImage = new RenderTargetBitmap(400, 300, 0, 0, PixelFormats.Pbgra32);
            composeImage.Render(drawingVisual);
            this.image.Source = composeImage;
            this.canvas.LayoutUpdated -= canvas_LayoutUpdated;
            this.canvas.Children.Add(image);
        }

        void canvas_LayoutUpdated(object sender, EventArgs e)
        {
            #region e1

            Point p1 = new Point(Convert.ToDouble(this.e1.GetValue(Canvas.LeftProperty)), Convert.ToDouble(this.e1.GetValue(Canvas.TopProperty)));
            drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green), 2.0), pe1, p1);
            pe1 = p1;

            #endregion

            #region e2

            Point p2 = new Point(Convert.ToDouble(this.e2.GetValue(Canvas.LeftProperty)), Convert.ToDouble(this.e2.GetValue(Canvas.TopProperty)));
            drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Yellow), 2.0), pe2, p2);
            pe2 = p2;

            #endregion
        }

    }

運行結果如下:


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM