Home > C# > WPF > WPFアニメーション

C#でWPFアニメーション その2 アニメーションを実装する

作成日 2015-06-18
最終更新日
右向きの▷をクリックすると円が右向きに移動し、下向きの▽をクリックすると円が下向きに移動します。

開始時の画像です。右向きの三角をクリックすると円が右向きに移動します。下向きの三角をクリックすると、円が下向きに移動します。 Canvasウィンドウの端に到達すると停止します。

アニメーションを実装する

xaml

<Window x:Class="Drawing001.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>
        
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        
        <ToolBarTray Orientation="Vertical">
            <ToolBar>
                <Button Margin="0,3" Name="cmdPlay" Click="cmdPlay_Click">
                    <StackPanel>
                        <Polygon Stroke="SteelBlue" StrokeThickness="3" Fill="SteelBlue" Points="0,0 0,18 18,9" />
                        <TextBlock>Play→</TextBlock>
                    </StackPanel>
                </Button>
                <Button Margin="0,3" Name="cmdPlayDown" Click="cmdPlayDown_Click">
                    <StackPanel>
                        <Polygon Stroke="SteelBlue" StrokeThickness="3" Fill="SteelBlue" Points="0,0 18,0 9,18" />
                        <TextBlock>Play↓</TextBlock>
                    </StackPanel>
                </Button>
                <Button Margin="0,3" Name="cmdStop" Click="cmdStop_Click">
                    <StackPanel>
                        <Rectangle Width="30" Height="30" Stroke="SteelBlue" StrokeThickness="3" Fill="SteelBlue" />
                        <TextBlock>Stop</TextBlock>
                    </StackPanel>
                </Button>
                <TextBlock Name="textPosX" Text="x:0" />
                <TextBlock Name="textPosY" Text="y:0" />
                <TextBlock Name="fps" Text="fps:000" />
             </ToolBar>
        </ToolBarTray>

        <Border Grid.Column="1" Margin="3" BorderBrush="SteelBlue" BorderThickness="1">
            <Canvas Name="myCanvas">
            </Canvas>
        </Border>
    </Grid>

</Window>

xaml.cs

フレームに基づくアニメーションの本体は、CompositionTarget.Renderingイベントです。 下記のコードでは、RenderFrameが、1/60秒ごとに呼び出されます。要素を動かすコードをRenderFrameに記述しておけば、要素が1/60秒ごとに動き、その結果、アニメーションになります。 ちらつきは発生しません。

CompositionTarget.Rendering += RenderFrame;

+=の形式で、イベントを追加しているので、デリゲートイベントだと思います。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;


namespace Drawing001
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {

        private int ellipseRadius = 10;     // 円の直径(楕円の高さと幅が同じ場合は円)
        private double accelerationX = 0;   // 移動速度を初期化
        private double accelerationY = 0;
 
        private bool rendering = false;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void printPos(UIElement el)
        {
            // elで指定した要素の座標を表示します。
            int x = (int)Canvas.GetLeft(el);
            int y = (int)Canvas.GetTop(el);

            // ツールバーに座標を表示します。
            textPosX.Text = string.Format("x:{0}",x);
            textPosY.Text = string.Format("y:{0}",y);
        }

        private void printPos(int x, int y)
        {
            // ツールバーに座標を表示します。
            textPosX.Text = string.Format("x:{0}", x);
            textPosY.Text = string.Format("y:{0}", y);
        }

        private void cmdPlay_Click(object sender, RoutedEventArgs e)
        {

            if (!rendering)
            {
                ellipses.Clear();
                myCanvas.Children.Clear();

            // 移動速度を指定
                accelerationX = 1;
                accelerationY = 0;
            // アニメーションを実行します
                CompositionTarget.Rendering += RenderFrame;
                rendering = true;
            }
        }
        private void cmdPlayDown_Click(object sender, RoutedEventArgs e)
        {
            if (!rendering)
            {
                ellipses.Clear();
                myCanvas.Children.Clear();

                // 移動速度を指定
                accelerationX = 0;
                accelerationY = 1;
                // アニメーションを実行します
                CompositionTarget.Rendering += RenderFrame;
                rendering = true;
            }

        }

        private void RenderFrame(object sender, EventArgs e)
        {
            if (ellipses.Count == 0)
            {
                // 初期化:楕円(円)を作成します。

                // キャンパスサイズを確認し、円の大きさを引いておきます。
                int maxWidth = (int)myCanvas.ActualWidth-ellipseRadius;
                int maxHight = (int)myCanvas.ActualHeight-ellipseRadius;
 
                // Create the ellipse. 楕円(円)を作成します。
                Ellipse ellipse = new Ellipse();
                ellipse.Fill = Brushes.LimeGreen;
                ellipse.Width = ellipseRadius;
                ellipse.Height = ellipseRadius;

                // Place the ellipse. 楕円(円)を座標(0,0)に配置します。
                Canvas.SetLeft(ellipse, 0);
                Canvas.SetTop(ellipse, 0);
                myCanvas.Children.Add(ellipse);

                // Track the ellipse. 楕円を追跡します。
                EllipseInfo info = new EllipseInfo(ellipse, 0,0);
                ellipses.Add(info);

                // ツールバーに座標を表示します。
                printPos((int)Canvas.GetLeft(ellipse), (int)Canvas.GetTop(ellipse));
            }

            else
            {
                // それぞれの楕円を移動します。
                
                for (int i = ellipses.Count - 1; i >= 0; i--)
                {
                    EllipseInfo info = ellipses[i];
                    // y軸方向の位置を移動します。
                    double top = Canvas.GetTop(info.Ellipse);
                    Canvas.SetTop(info.Ellipse, top + 1 * info.VelocityY);
                    // x軸方向の位置を移動します。
                    double left = Canvas.GetLeft(info.Ellipse);
                    Canvas.SetLeft(info.Ellipse, left + 1 * info.VelocityX);

                    // ツールバーに座標を表示します。
                    printPos((int)Canvas.GetLeft(info.Ellipse), (int)Canvas.GetTop(info.Ellipse));

                    if (top >= (myCanvas.ActualHeight - ellipseRadius))
                    {
                        
                        // y軸方向の終了処理、Canvasの端で終了します。
                        // 少しはみ出るのは、端数のためだと思います。
                        ellipses.Remove(info);
                    }
                    else
                    {
                        // y軸方向の移動処理
                        info.VelocityY = accelerationY; // 等速移動
                    }

                   if (left >= (myCanvas.ActualWidth - ellipseRadius))
                    {
                        // x軸方向の終了処理、Canvasサイズの端で終了します。
                        // 少しはみ出るのは、端数のためだと思います。
                        ellipses.Remove(info);
                    }  
                    else
                    {
                        // x軸方向の移動処理
                        info.VelocityX = accelerationX;
                    }

                    if (ellipses.Count == 0)
                    {
                        // 全ての楕円(円)に処理が終われば終了します。
                        // アニメーションを終了します。
                        StopRendering();
                    }
                }


            }
        }

        private void cmdStop_Click(object sender, RoutedEventArgs e)
        {
            StopRendering();
        }

        private void StopRendering()
        {
            CompositionTarget.Rendering -= RenderFrame;
            rendering = false;
        }

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

            public Ellipse Ellipse
            {
                // 楕円の参照
                get;
                set;
            }

            public double VelocityY
            {
                // 楕円のY方向速度
                get;
                set;
            }

            public double VelocityX
            {
                // 楕円のX方向速度
                get;
                set;
            }

         }
        private List<EllipseInfo> ellipses = new List<EllipseInfo>();


    }
}


参考文献

参考文献: [Matthew MacDonald]Pro WPF 4.5 in C# p0499-0452, 0372-0374

本文には、説明が少ないので、サンプルコードとにらめっこする必要があります。

このエントリーをはてなブックマークに追加

Home PC C# Illustration

Copyright (C) 2011 Horio Kazuhiko(kukekko) All Rights Reserved.
kukekko@gmail.com
ご連絡の際は、お問い合わせページのURLの明記をお願いします。
「掲載内容は私自身の見解であり、所属する組織を代表するものではありません。」