8.2 アニメーション
8.2.1 オブジェクトを動かす
動画再生は、すでに完成されたメディアファイルを読み込んでプレイヤーとして再生するものですが、WPF では、WPF の描画要素を実行時に動かすアニメーション処理のインフラストラクチャが提供されています。この機能を使うことで、高度なスレッドと処理時間の管理をせずに、簡単なプロパティの設定のみでアニメーションを実現できます。
アニメーションは、表示されている図形やコントロールの状態を、一定時間の間に徐々に変化させることで実現します。これまでは、オブジェクトの状態を徐々に変化させるスレッドやタイマーを別に用意して、時間管理を行いながらユーザーインターフェイスを少しずつ変化させる処理を記述する必要がありました。しかし、WPF ではこの作業を代わって行ってくれるオブジェクトが用意されています。
WPF が提供するアニメーションのインフラストラクチャは、依存プロパティの値を指定した時間をかけて徐々に変更することで実現しています。例えば、オブジェクトをある座標から、別の座標まで徐々に変更することで、オブジェクトが滑らかに移動するようなアニメーションを実行することは可能です。しかし、新しいインスタンスを生成したり、削除するような、オブジェクトの構造そのものを変更するという形でのアニメーションはできません。
アニメーション処理を実行するには UIElement オブジェクトの BeginAnimation() メソッドを呼び出します。このメソッドは System.Windows.Media.Animation.IAnimatable インターフェイスで宣言されているもので UIElement クラスは IAnimatable インターフェイスを実装しています。UIElement クラスに限らず IAnimatable インターフェイスを実装しているオブジェクトであれば、WPF が提供するアニメーション処理を適用することができます。
public interface IAnimatable
public void BeginAnimation ( DependencyProperty dp, AnimationTimeline animation )
dp パラメータには、アニメーション処理の対象となる依存プロパティを指定します。
animation パラメータには、対象の依存プロパティを開始値から終了値まで、指定した時間をかけて変更させるための時間軸を提供する System.Windows.Media.Animation.AnimationTimeline クラスのオブジェクトを指定します。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Animation.Timeline System.Windows.Media.Animation.AnimationTimeline
public abstract class AnimationTimeline : Timeline
AnimationTimeline クラス自身も、IAnimetable インターフェイスを実装するアニメーション可能なクラスを表す System.Windows.Media.Animation.Animatable を継承しています。しかし、AnimationTimeline は、IAnimetable を実装するオブジェクトに対して、アニメーションのための情報を提供するのが役割で、このオブジェクト自身をアニメーションさせることが主たる目的ではありません。
AnimationTimeline クラス自身は abstract なので、インスタンス化にはこのクラスから派生している実装クラスを用います。アニメーションの対象となる依存プロパティには様々な型が存在するため、プロパティの範囲を設定する値は固定化できません。そこで、対象のプロパティ型に合わせて適切な AnimationTimeline の実装が用意されています。
例えば、座標などの多くの依存プロパティは double 型で動作します。double 型をアニメーションさせるには System.Windows.Media.Animation.DoubleAnimation クラスを使います。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Animation.Timeline System.Windows.Media.Animation.AnimationTimeline System.Windows.Media.Animation.DoubleAnimationBase System.Windows.Media.Animation.DoubleAnimation
public class DoubleAnimation : DoubleAnimationBase
DoubleAnimation クラスのコンストラクタはオーバーロードされています。代表的なコンストラクタは、開始値、終了値、継続時間を設定する次のようなコンストラクタです。
public DoubleAnimation ()
public DoubleAnimation ( double toValue, Duration duration )
public DoubleAnimation ( double fromValue, double toValue, Duration duration )
fromValue パラメータにはアニメーション開始時に設定するプロパティの値、toValue パラメータには終了時のプロパティの値を指定します。アニメーションは、対象のプロパティを fromValue パラメータに指定した値から toValue パラメータに指定した値まで、duration パラメータに指定した継続時間をかけて徐々に変更します。
duration パラメータには、アニメーションの継続時間を表す System.Windows.Duration 構造体型のオブジェクトを設定します。
[TypeConverterAttribute(typeof(DurationConverter))] public struct Duration
この構造体のコンストラクタには、アニメーションの継続時間を表す TimeSpan オブジェクトを渡します。
public Duration (TimeSpan timeSpan)
DoubleAnimation クラスのコンストラクタに設定した開始値、終了値、継続時間は、それぞれ From プロパティ、To プロパティ、Duration プロパティで設定・取得することができます。このうち、継続時間を表す Duration プロパティは基底クラスの Timeline のプロパティです。From と To プロパティは、特定の型に依存するため DoubleAnimation 固有のプロパティです。
public Nullable<double> From { get; set; }
public Nullable<double> To { get; set; }
public Duration Duration { get; set; }
これらの値の範囲と継続時間で変更できるのは IAnimatable を実装するオブジェクトの依存プロパティだけですが、プロパティの値を少しずつ変更することで画面が連続的に更新され、人間の目にはオブジェクトが動いているように見えます。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Controls; using System.Windows.Shapes; class Test { [STAThread] public static void Main() { Ellipse ellipse = new Ellipse(); ellipse.Fill = Brushes.Black; ellipse.Height = 200; Canvas canvas = new Canvas(); canvas.Children.Add(ellipse); Window wnd = new Window(); wnd.Content = canvas; Duration duration = new Duration(TimeSpan.FromMilliseconds(5000)); DoubleAnimation anime = new DoubleAnimation(100, 400, duration); ellipse.BeginAnimation(Ellipse.WidthProperty, anime); Application app = new Application(); app.Run(wnd); } }
コード1は、アプリケージョンを実行すると、ウィンドウ上に表示された楕円の幅が徐々に大きくなるアニメーションが実行されます。この処理は 5 秒ほどの時間をかけて、幅 100 ピクセルの楕円から、幅 400 ピクセルの楕円になるまで実行されます。実行すると、オブジェクトの幅がスムーズに大きくなる様子を見ることができます。
このプログラムでは double 型のプロパティをアニメーションさせるために DoubleAnimation オブジェクトを使いましたが、プロパティの型が異なれば、必然と AnimationTimeline の実装型も変更しなければなりません。例えば int 型のプロパティに対しては Int32Animation クラスが用意されています。プロパティ型に応じて、適切な AnimationTimeline 型を検索してください。
8.2.2 色の変更
もう 1 つ、プロパティの変更によるアニメーションの例を見てみましょう。数値型であれば DoubleAnimation や Int32Animation があるように、より複雑な型に対しても専用のクラスが用意されています。例えば、背景色や前景色などを少しずつ変更すると言うアニメーションは一般的に使われるものでしょう。アルファ値をアニメーションさせるフェード処理なども頻繁に見られます。
色の変更処理は System.Windows.Media.Animation.ColorAnimation クラスを使います。アニメーションの原理は DoubleAnimation と同じです。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Animation.Timeline System.Windows.Media.Animation.AnimationTimeline System.Windows.Media.Animation.ColorAnimationBase System.Windows.Media.Animation.ColorAnimation
public class ColorAnimation : ColorAnimationBase
このクラスのコンストラクタはオーバーロードされています。代表的なコンストラクタは以下のようなものがあります。
public ColorAnimation ()
public ColorAnimation ( Color toValue, Duration duration )
public ColorAnimation ( Color fromValue, Color toValue, Duration duration )
型は異なりますが、パラメータの意味は DoubleAnimation クラスと同じです。fromValue パラメータにはアニメーション開始時の色、toValue パラメータには終了時の色、duration パラメータには継続時間を指定します。これらの値は、From プロパティと To プロパティから設定・取得できます。
public Nullable<Color> From { get; set; }
public Nullable<Color> To { get; set; }
これで、From に指定されている色から To に指定されている色まで変更することが可能です。ブラシに設定されている SolidColorBrush に対してこのアニメーションを実行すれば、オブジェクトの前景色や背景色をフェードさせる処理を容易に実現できます。
using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Controls; using System.Windows.Shapes; class Test : Window { [STAThread] public static void Main() { Window wnd = new Test(); Application app = new Application(); app.Run(wnd); } private Ellipse ellipse; private SolidColorBrush brush; private Color normal, highlight; public Test() { normal = Color.FromRgb(0, 0, 0); highlight = Color.FromRgb(0xFF, 0, 0); brush = new SolidColorBrush(normal); ellipse = new Ellipse(); ellipse.Fill = brush; ellipse.Width = 200; ellipse.Height = 200; ellipse.MouseEnter += ellipseMouseEnter; ellipse.MouseLeave += ellipseMouseLeave; Content = ellipse; } private void ellipseMouseEnter(object sender, MouseEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(500)); ColorAnimation anime = new ColorAnimation(highlight, duration); brush.BeginAnimation(SolidColorBrush.ColorProperty, anime); } private void ellipseMouseLeave(object sender, MouseEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(500)); ColorAnimation anime = new ColorAnimation(normal, duration); brush.BeginAnimation(SolidColorBrush.ColorProperty, anime); } }
コード2は、ウィンドウ上に表示されている黒い楕円形の Ellipse オブジェクトの上にマウスカーソルを乗せると、楕円形が赤色に遷移するプログラムです。楕円形からカーソルを外すと、色は再び黒に戻ります。黒から赤へ、赤から黒への色の変化は、500 ミリ秒ほどの時間をかけてアニメーションします。
8.2.3 相対的な変更
例えば、DoubleAnimation クラスの From と To プロパティから、オブジェクトの幅や高さを変更するアニメーションを作ろうと思った場合、アニメーション開始時のオブジェクトのプロパティの値を From に設定しなければなりません。もし、現在の値から指定した値まで変更したい場合に、現在の値を得ることができないときに、これは問題となります。そこで、このような場合に、現在のプロパティの値から指定した値までを変更する By プロパティが用意されています。
public Nullable<double> By { get; set; }
From プロパティや To プロパティの代わりに By プロパティを使えば、現在のプロパティの値から相対的に指定した値まで変更することができます。By プロパティは、既定で null が設定されています。
From プロパティや To プロパティ、または By プロパティは、AnimationTimeline を実装するクラスであれば、一部のクラスを除いて、そのクラスが担当する型に応じて用意されています。
using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; class Test : Window { [STAThread] public static void Main() { Window wnd = new Test(); Application app = new Application(); app.Run(wnd); } private Ellipse ellipse; public Test() { ellipse = new Ellipse(); ellipse.Fill = Brushes.Black; ellipse.Width = 100; ellipse.Height = 200; ellipse.MouseUp += ellipseMouseUp; Content = ellipse; } private void ellipseMouseUp(object sender, MouseEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(500)); DoubleAnimation anime = new DoubleAnimation(); anime.Duration = duration; anime.By = 50; ellipse.BeginAnimation(Ellipse.WidthProperty, anime); } }
コード3は、楕円をクリックすると、楕円の幅が、現在の幅から 50 ピクセル加算されるアニメーションが実行されます。このアニメーションは By プロパティによって実現されています。From プロパティに ellipse.Width を、To プロパティに ellipse.Width + 50 を設定することでも同じ処理ができますが、プロパティの現在の値から指定した値に変更したい場合は By を使ったほうが便利です。