8.3 タイムライン
8.3.1 アニメーションと時間の関係
アニメーション処理は、一定の時間をかけて徐々にオブジェクトの状態を遷移させるという性質から、タイムラインを持ちます。タイムラインとは、アニメーション発生時の時間軸のことで、タイムラインを制御することで思い通りのアニメーションを実現することができます。タイムラインと呼ぶことができるのは System.Windows.Media.Animation.Timeline クラス、またはこの派生クラスです。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Animation.Timeline
[RuntimeNamePropertyAttribute("Name")] [LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)] public abstract class Timeline : Animatable
実は、前述した AnimationTimeline クラスは Timeline クラスを継承しているので、AnimationTimeline クラスを実装する DoubleAnimation クラスなどはタイムラインです。前のサンプルでは Timeline クラスの Duration プロパティを利用して、プロパティの変更に費やす時間を設定しました。
Timeline クラスには、タイムラインが開始される時間を設定する BeginTime プロパティを用意しています。このプロパティには、タイムラインを開始するまでの時間を設定することができます。
public Nullable<TimeSpan> BeginTime { get; set; }
BeginTime プロパティによって、タイムラインが開始される時間を制御することができます。既定では 0 が設定されているため即座にアニメーションが開始されますが、他のタイムラインとの組み合わせで使われるアニメーションなどで遅延させたい場合などには BeginTime を設定します。
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 = 100; ellipse.MouseUp += ellipseMouseUp; Content = ellipse; } private void ellipseMouseUp(object sender, MouseEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(100)); DoubleAnimation anime1 = new DoubleAnimation(ellipse.Width + 50, duration); DoubleAnimation anime2 = new DoubleAnimation(ellipse.Height + 50, duration); anime2.BeginTime = TimeSpan.FromMilliseconds(100); ellipse.BeginAnimation(Ellipse.WidthProperty, anime1); ellipse.BeginAnimation(Ellipse.HeightProperty, anime2); } }
コード1は、表示された楕円をクリックすると楕円の幅と高さが大きくなるというプログラムですが、楕円の幅と高さは同時に大きくなるのではなく、最初に幅を 100 ミリ秒で大きくし、その次の 100 ミリ秒で高さを大きくしています。2 つのアニメーションを同時に起動していますが、BeginTime の設定で実行のタイミングが遅れていることを確認できます。
8.3.2 元に戻す
演出的なアニメーションでは、何らかのアニメーションを行った後で、オブジェクトの状態を元に戻したいと考えることも多いでしょう。例えば、強調表示のためにオブジェクトのサイズを一瞬だけ大きくし、すぐに元に戻したい場合や、オブジェクトの色を点滅させたい場合などです。「8.2 アニメーション コード2」では、2 つのタイムラインを作成して、マウスが楕円に入ったときに赤色に変更し、外に出たときにもとの色に戻しました。これらはイベントが分離されているため個別に行う必要がありますが、1 つのアニメーション処理の中でアニメーションの繰返しを行いたい場合は不便です。
何らかのアニメーションを行った後、逆再生するようにプロパティの値をアニメーション開始前の状態の戻したい場合、AutoReverse プロパティを設定します。
public bool AutoReverse { get; set; }
タイムラインが処理を終了した後に復元処理を行う場合は true、そうでなければ false を指定します。既定では false が設定されているため、アニメーションを実行した結果、オブジェクトには To プロパティなどで設定した移行した結果の値が格納されることになります。しかし、AutoReverse を true にすることで、自動的にアニメーションを逆再生させてオブジェクトの値をアニメーション開始前の状態に戻すことができます。
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; private const int ORIGINAL_SIZE = 100, EXPANDED_SIZE = 150; public Test() { ellipse = new Ellipse(); ellipse.Fill = Brushes.Black; ellipse.Width = ORIGINAL_SIZE; ellipse.Height = ORIGINAL_SIZE; ellipse.MouseEnter += ellipseMouseEnter; Content = ellipse; } private void ellipseMouseEnter(object sender, MouseEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(100)); DoubleAnimation anime1 = new DoubleAnimation( ORIGINAL_SIZE, EXPANDED_SIZE, duration); DoubleAnimation anime2 = new DoubleAnimation( ORIGINAL_SIZE, EXPANDED_SIZE, duration); anime1.AutoReverse = true; anime2.AutoReverse = true; anime2.BeginTime = TimeSpan.FromMilliseconds(100); ellipse.BeginAnimation(Ellipse.WidthProperty, anime1); ellipse.BeginAnimation(Ellipse.HeightProperty, anime2); } }
コード2は、黒い楕円の上にマウスカーソルが入ると、一瞬だけ楕円が震えるように大きく強調され、すぐに元のサイズに戻るというプログラムです。幅と高さを変更する 2 つの DoubleAnimation オブジェクトの AutoReverse プロパティに、それぞれ true を設定しているため、アニメーション処理でプロパティが目的の値にまで到達すると、同じ時間をかけてプロパティを初期の値に戻してくれます。
このプロパティは、特定のアニメーションを繰り返し実行し続ける繰り返し処理にも応用できます。
8.3.3 繰り返し
これまでにタイムラインは、BeginTime プロパティで指定している時間から開始して、Duration プロパティに設定している時間までの間で処理を終了します。必要に応じて AutoReverse で元の値に戻すことができましたが、どちらにしても処理は 1 度だけしか行われません。
アニメーションを繰り返し行うには、タイムラインの RepeatBehavior プロパティを使います。演出的なアニメーション処理の場合は、何度も繰り返し行いたい場合があることでしょう。
public RepeatBehavior RepeatBehavior { get; set; }
RepeatBehavior プロパティには System.Windows.Media.Animation.RepeatBehavior 構造体の値を設定します。
[TypeConverterAttribute(typeof(RepeatBehaviorConverter))] public struct RepeatBehavior : IFormattable
RepeatBehavior 構造体のコンストラクタには、繰り返す回数か、または繰り返す時間を指定します。
public RepeatBehavior (double count)
public RepeatBehavior (TimeSpan duration)
double 型を渡す count パラメータのコンストラクタを使った場合、指定した値は Count プロパティに設定されます。Count プロパティに値が設定されている場合、指定した回数だけアニメーションを繰り返します。
TimeSpan 型の duration パラメータを受けるコンストラクタから生成した場合、指定した値は Duration プロパティに設定されます。この場合、指定した時間だけアニメーションを繰り返します。例えば、開始から終了までの時間が 1 秒間のタイムラインを 5 秒間繰り返す場合、結果としてアニメーションは 5 回繰り返されることになります。
public double Count { get; }
public TimeSpan Duration { get; }
例えば、オブジェクトの前景色や背景色を点滅させて強調させいたい場合、何度か点滅させるには ColorAnimation オブジェクトの AutoReverse プロパティを true にした状態で数回繰り返せばよいでしょう。
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() { Color color1 = Color.FromRgb(0, 0, 0); Color color2 = Color.FromRgb(0xFF, 0, 0); SolidColorBrush brush = new SolidColorBrush(color1); ellipse = new Ellipse(); ellipse.Fill = brush; ellipse.Width = 200; ellipse.Height = 200; Duration duration = new Duration(TimeSpan.FromMilliseconds(1000)); ColorAnimation anime = new ColorAnimation(color2, duration); anime.AutoReverse = true; anime.RepeatBehavior = new RepeatBehavior(10); brush.BeginAnimation(SolidColorBrush.ColorProperty, anime); Content = ellipse; } }
コード3は、ウィンドウ上に点滅する楕円を表示するプログラムです。楕円形は、黒から赤に、赤から黒に、アニメーションを繰り返すことで点滅してみます。ただし、このアニメーションは繰り返し回数が 10 回と指定されているため、10 回点滅した時点で終了します。
アニメーションを無限に繰り返したい場合は RepeatBehavior クラスの静的な Forever プロパティが返す RepeatBehavior を設定します。
public static RepeatBehavior Forever { get; }
Forever プロパティが返した RepeatBehavior の値をタイムラインに設定することで、アニメーションを停止させることなく、永遠と繰り返させることができます。