作成日 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
本文には、説明が少ないので、サンプルコードとにらめっこする必要があります。