小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【W(wǎng)PF學(xué)習(xí)】第五十六章 基于幀的動(dòng)畫

 小仙女本仙人 2021-04-12

  除基于屬性的動(dòng)畫系統(tǒng)外,WPF提供了一種創(chuàng)建基于幀的動(dòng)畫的方法,這種方法只使用代碼。需要做的全部工作是響應(yīng)靜態(tài)的CompositionTarge.Rendering事件,觸發(fā)該事件是為了給每幀獲取內(nèi)容。這是一種非常低級(jí)的方法,除非使用標(biāo)準(zhǔn)的基于屬性的動(dòng)畫模型不能滿足需要(例如,構(gòu)建簡(jiǎn)單的側(cè)邊滾動(dòng)游戲、創(chuàng)建基于物理的動(dòng)畫式構(gòu)建粒子效果模型(如火焰、雪花以及氣泡)),否則不會(huì)希望使用這種方法。

  構(gòu)建基于幀的動(dòng)畫的基本技術(shù)很容易。只需要為靜態(tài)的CompositionTarget.Rendering事件關(guān)聯(lián)事件處理程序。一旦關(guān)聯(lián)事件處理程序,WPF就開(kāi)始不斷地調(diào)用這個(gè)事件處理程序(只要渲染代碼的執(zhí)行速度足夠快,WPF每秒將調(diào)用60次)。在渲染事件處理程序中,需要在窗口中相應(yīng)地創(chuàng)建或調(diào)整元素。換句話說(shuō),需要自行管理全部工作。當(dāng)動(dòng)畫結(jié)束時(shí),分離事件處理程序。

  下圖顯示了一個(gè)簡(jiǎn)單示例。在此,隨機(jī)數(shù)量的圓從Canvas面板的頂部向底部下落。它們(根據(jù)隨機(jī)生成的開(kāi)始速度)以不同速度下降,但一相同的速率加速。當(dāng)所有的圓到達(dá)底部時(shí),動(dòng)畫結(jié)束。

  在這個(gè)示例中,每個(gè)下落的圓由Ellipse元素表示。使用自定義的EllipseInfo類保存橢圓的引用,并跟蹤對(duì)于物理模型而言十分重要的一些細(xì)節(jié)。在這個(gè)示例中,只有如下信息很重要——橢圓沿X軸的移動(dòng)速度(可很容易地?cái)U(kuò)張這個(gè)類,使其包含沿著Y軸運(yùn)動(dòng)的速度、額外的加速信息等)。

public class EllipseInfo
    {
        public Ellipse Ellipse
        {
            get;
            set;
        }

        public double VelocityY
        {
            get;
            set;
        }

        public EllipseInfo(Ellipse ellipse, double velocityY)
        {
            VelocityY = velocityY;
            Ellipse = ellipse;
        }
    }

  應(yīng)用程序使用集合跟蹤每個(gè)橢圓的EllipseInfo對(duì)象。還有幾個(gè)窗口級(jí)別的字段,它們記錄計(jì)算橢圓下落時(shí)使用的各種細(xì)節(jié)??珊苋菀椎厥惯@些細(xì)節(jié)變成可配置的。

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;

  當(dāng)單擊其中某個(gè)按鈕時(shí),清空集合,并將事件處理程序關(guān)聯(lián)到CompositionTarget.Rendering事件:

        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;
        }    

  如果橢圓不存在,渲染代碼會(huì)自動(dòng)創(chuàng)建它們。渲染代碼創(chuàng)建隨機(jī)數(shù)量的橢圓(當(dāng)前為20到100個(gè)),并使他們具有相同的尺寸和顏色。橢圓被放在Canvas面板的頂部,但他們沿著X軸隨機(jī)移動(dòng):

 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);
                }
            }
        }    

  如果橢圓已經(jīng)存在,代碼處理更有趣的工作,以便進(jìn)行動(dòng)態(tài)顯示。使用Canvas.SetTop()方法緩慢移動(dòng)每個(gè)橢圓。移動(dòng)距離取決于指定的速度。

            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);
            }

  為提高性能,一旦橢圓到達(dá)Canvas面板的底部,就從跟蹤集合中刪除橢圓。這樣,就不需要再處理它們。當(dāng)遍歷集合時(shí),為了能夠工作而不會(huì)導(dǎo)致丟失位置,需要向后迭代,從集合的末尾向起始位置迭代。

  如果橢圓尚未到達(dá)Canvas面板的底部,代碼會(huì)提高速度(此外,為獲得磁鐵吸引效果,還可以根據(jù)橢圓與Canvas面板底部的距離來(lái)設(shè)置速度):

                    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;
                    }    

  最后,如果所有橢圓都已從集合中刪除,就移除事件處理程序,然后結(jié)束動(dòng)畫:

                    if (ellipses.Count == 0)
                    {
                        // End the animation.
                        // There's no reason to keep calling this method
                        // if it has no work to do.
                        StopRendering();
                    }        

  示例完整XAML標(biāo)記如下所示:

<Window x:Class="Animation.FrameBasedAnimation"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="FrameBasedAnimation" Height="396" Width="463.2">
    <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>
FrameBasedAnimation.xaml
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 Animation
{
    /// <summary>
    /// FrameBasedAnimation.xaml 的交互邏輯
    /// </summary>
    public partial class FrameBasedAnimation : Window
    {
        public FrameBasedAnimation()
        {
            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;
        }
    }
}
FrameBasedAnimation.xaml.cs

  顯然,可擴(kuò)展的這個(gè)動(dòng)畫以使圓跳躍和分散等。使用的技術(shù)是相同的——只需要使用更復(fù)雜的公式計(jì)算速度。

  當(dāng)構(gòu)建基于幀的動(dòng)畫時(shí)需要注意如下問(wèn)題:它們不依賴與時(shí)間。換句話說(shuō),動(dòng)畫可能在性能好的計(jì)算機(jī)上運(yùn)動(dòng)更快,因?yàn)閹蕰?huì)增加,會(huì)更頻繁地調(diào)用CompositionTarget.Rendering事件。為補(bǔ)償這種效果,需要編寫考慮當(dāng)前時(shí)間的代碼。

  開(kāi)始學(xué)習(xí)基于幀的動(dòng)畫的最好方式是查看WPF SDK提供的每一幀動(dòng)畫都非常詳細(xì)的示例。該例演示了幾種粒子系統(tǒng)效果,并且使用自定義的TimeTracker類實(shí)現(xiàn)了依賴與時(shí)間的基于幀的動(dòng)畫。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多