次の方法で共有


Xamarin.Forms のカスタム アニメーション

Animation クラスは、すべての Xamarin.Forms アニメーションの構成要素であり、ViewExtensions クラスの拡張メソッドによって 1 つ以上の Animation オブジェクトが作成されます。 この記事では、Animation クラスを使用してアニメーションを作成およびキャンセルする方法、複数のアニメーションを同期する方法、既存のアニメーション メソッドでアニメーション化されていないプロパティをアニメーション化するカスタム アニメーションを作成する方法について説明します。

Animation オブジェクトを作成するときは、アニメーション化するプロパティの開始値と終了値、プロパティの値を変更するコールバックなど、多数のパラメーターを指定する必要があります。 Animation オブジェクトは、実行および同期できる子アニメーションのコレクションを維持することもできます。 詳細については、「子アニメーション (Child Animations)」を参照してください。

Animation クラスで作成されたアニメーション(子アニメーションが含まれる場合と含まれない場合があります)を実行するには、Commit メソッドを呼び出します。 このメソッドはアニメーションの継続時間を指定し、特にアニメーションを繰り返すかどうかを制御するコールバックを指定します。

さらに、Animation クラスには、省電力モードがアクティブになっている場合など、オペレーティング システムによってアニメーションが無効になっているかどうかを調べることができる IsEnabled プロパティがあります。

アニメーションを作成する

Animation オブジェクトを作成する場合、次のコード例に示すように、通常は少なくとも 3 つのパラメーターが必要です。

var animation = new Animation (v => image.Scale = v, 1, 2);

このコードでは、Image インスタンスの Scale プロパティを値 1 から値 2 に変更するアニメーションを定義します。 アニメーションの値は Xamarin.Forms によって導出され、最初の引数として指定されたコールバックに渡され、Scale プロパティの値を変更するために使用されます。

アニメーションは、次のコード例で説明するとおり、Commit メソッドの呼び出しで開始されます。

animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);

Commit メソッドは Task オブジェクトを返さないことに注意してください。 代わりに、通知はコールバック メソッドを介して提供されます。

次の引数は Commit メソッドで指定されます。

  • 最初の引数 (owner) は、アニメーションの所有者を特定します。 これは、アニメーションが適用される視覚要素、またはページなどの別の視覚要素にすることができます。
  • 2 番目の引数 (name) は、名前を含むアニメーションを特定します。 名前は所有者と組み合わせて、アニメーションを一意に識別します。 その後、この一意の ID を使用して、アニメーションが実行中かどうかを判断したり (AnimationIsRunning)、アニメーションをキャンセルしたり (AbortAnimation) できます。
  • 3 番目の引数 (rate) は、Animation コンストラクターで定義されたコールバック メソッドの各呼び出し間のミリ秒数を示します。
  • 4 番目の引数 (length) はアニメーションの継続時間をミリ秒単位で示します。
  • 5 番目の引数 (easing) は、アニメーションで使用されるイージング関数を定義します。 あるいは、イージング関数を Animation コンストラクターへの引数として指定することもできます。 イージング関数の詳細については、「イージング関数」を参照してください。
  • 6 番目の引数 (finished) は、アニメーションが完了したときに実行されるコールバックです。 このコールバックは 2 つの引数を取ります。最初の引数は最終値を示し、2 番目の引数はアニメーションがキャンセルされた場合に true に設定される bool です。 あるいは、finished コールバックを Animation コンストラクターへの引数として指定することもできます。 ただし、単一のアニメーションでは、finished コールバックが Animation コンストラクターと Commit メソッドの両方で指定されている場合、Commit メソッドで指定されているコールバックのみが実行されます。
  • 7 番目の引数 (repeat) は、アニメーションを繰り返すためのコールバックです。 これはアニメーションの最後に呼び出され、true を返すとアニメーションを繰り返す必要があることを示します。

全体的な効果は、Linear イージング関数を使用して、2 秒 (2000 ミリ秒) かけて ImageScale プロパティを 1 から 2 に増やすアニメーションを作成することです。 アニメーションが完了するたびに、その Scale プロパティは 1 にリセットされ、アニメーションが繰り返されます。

Note

相互に独立して実行される同時アニメーションは、アニメーションごとに Animation オブジェクトを作成し、各アニメーションで Commit メソッドを呼び出すことで構築できます。

子アニメーション

Animation クラスは子アニメーションもサポートしており、Animation オブジェクトを作成し、そこに他の Animation オブジェクトを追加します。 これにより、一連のアニメーションを実行して同期できるようになります。 次のコード例は、子アニメーションの作成と実行を示しています。

var parentAnimation = new Animation ();
var scaleUpAnimation = new Animation (v => image.Scale = v, 1, 2, Easing.SpringIn);
var rotateAnimation = new Animation (v => image.Rotation = v, 0, 360);
var scaleDownAnimation = new Animation (v => image.Scale = v, 2, 1, Easing.SpringOut);

parentAnimation.Add (0, 0.5, scaleUpAnimation);
parentAnimation.Add (0, 1, rotateAnimation);
parentAnimation.Add (0.5, 1, scaleDownAnimation);

parentAnimation.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));

または、次のコード例に示すように、コード例をより簡潔に記述することもできます。

new Animation {
    { 0, 0.5, new Animation (v => image.Scale = v, 1, 2) },
    { 0, 1, new Animation (v => image.Rotation = v, 0, 360) },
    { 0.5, 1, new Animation (v => image.Scale = v, 2, 1) }
    }.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));

どちらのコード例でも、親 Animation オブジェクトが作成され、そこに Animation オブジェクトが追加されます。 Add メソッドの最初の 2 つの引数は、子アニメーションを開始および終了するタイミングを指定します。 引数の値は 0 から 1 の間である必要があり、指定された子アニメーションがアクティブになる親アニメーション内の相対的な期間を表します。 したがって、この例では、scaleUpAnimation はアニメーションの前半でアクティブになり、scaleDownAnimation はアニメーションの後半でアクティブになり、rotateAnimation は継続時間全体でアクティブになります。

全体的な効果として、アニメーションは 4 秒 (4000 ミリ秒) かけて行われます。 scaleUpAnimation は、Scale プロパティを 2 秒かけて 1 から 2 までアニメーション化します。 次に、scaleDownAnimation は、Scale プロパティを 2 から 1 まで 2 秒かけてアニメーション化します。 両方のスケール アニメーションが発生している間、rotateAnimationRotation プロパティを 0 から 360 まで 4 秒間アニメーション化します。 スケーリング アニメーションにもイージング関数が使用されていることに注意してください。 SpringIn イージング関数により、Image は最初に縮小してから大きくなり、SpringOut イージング関数により、Image は完全なアニメーションの終わりに向かって実際のサイズよりも小さくなります。

子アニメーションを使用する Animation オブジェクトと、子アニメーションを使用しないオブジェクトの間には、多くの違いがあります。

  • 子アニメーションを使用する場合、子アニメーションの finished コールバックは子アニメーションが完了した時間を示し、Commit メソッドに渡された finished コールバックはアニメーション全体が完了した時間を示します。
  • 子アニメーションを使用する場合、Commit メソッドの repeat コールバックから true を返してもアニメーションは繰り返されませんが、アニメーションは新しい値なしで引き続き実行されます。
  • Commit メソッドにイージング関数が含まれており、イージング関数が 1 より大きい値を返した場合、アニメーションは終了します。 イージング関数が 0 未満の値を返す場合、値は 0 に固定されます。 0 未満の値や 1 より大きい値を返すイージング関数を使用するには、Commit メソッドではなく、子アニメーションのいずれかで指定する必要があります。

Animation クラスには、親 Animation オブジェクトに子アニメーションを追加するために使用できる WithConcurrent メソッドも含まれています。 ただし、beginfinishの引数値は 0 から 1 に制限されず、0 から 1 の範囲に対応する子アニメーションの部分のみがアクティブになります。 たとえば、WithConcurrent メソッド呼び出しが、Scale プロパティの 1 から 6 をターゲットとする子アニメーションを定義し、beginfinish の値が -2 と 3 の場合、begin の値である -2 は Scale の値である 1 に対応し、finish の値である 3 は Scale の値である 6 に対応します。 0 と 1 の範囲外の値はアニメーションに関与しないため、Scale プロパティは 3 から 6 までのみアニメーション化されます。

アニメーションを取り消す

アプリケーションは、次のコード例に示すように、AbortAnimation 拡張メソッドへの呼び出しを使用してアニメーションを取り消すことができます。

this.AbortAnimation ("SimpleAnimation");

アニメーションは、アニメーション所有者とアニメーション名の組み合わせによって一意に識別されることに注意してください。 そのため、アニメーションを取り消すには、アニメーションの実行時に指定された所有者と名前を指定する必要があります。 したがって、コード例は、ページで所有される SimpleAnimation という名前のアニメーションを即座にキャンセルします。

カスタム アニメーションを作成する

これまでにここで示した例は、ViewExtensions クラスのメソッドでも同様に実現できるアニメーションを示しています。 ただし、Animation クラスの利点は、アニメーション化された値が変更されたときに実行されるコールバック メソッドにアクセスできることです。 これにより、コールバックは任意のアニメーションを実装できます。 たとえば、次のコード例では、ページの BackgroundColor プロパティを、Color.FromHsla メソッドで作成された Color 値に設定し、色相の値を 0 から 1 に設定してアニメーション化します。

new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5),
  start: 0,
  end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Color.Default);

結果として得られるアニメーションでは、ページの背景が虹の色で進んでいくように見えます。

ベジエ曲線アニメーションを含む複雑なアニメーションを作成する例については、Xamarin.Forms を使用したモバイル アプリの作成の第 22 章を参照してください。

カスタム アニメーション拡張メソッドを作成する

ViewExtensions クラスの拡張メソッドは、プロパティを現在の値から指定された値にアニメーション化します。 このため、ある値から別の値に色をアニメーション化するために使用できる ColorTo アニメーション メソッドなどを作成することは困難です:

  • VisualElement クラスによって定義された唯一の Color プロパティは BackgroundColor で、これは常にアニメーション化することが望ましい Color プロパティではありません。
  • Color プロパティの現在の値が Color.Default である場合がよくありますが、これは実際の色ではなく、補間計算では使用できません。

この問題の解決策は、ColorTo メソッドで特定の Color プロパティを対象にしないことです。 代わりに、補間された Color 値を呼び出し元に渡すコールバック メソッドを使用して記述することができます。 さらに、このメソッドは開始と終了の Color 引数を受け取ります。

ColorTo メソッドは、AnimationExtensions クラスの Animate メソッドを使用してその機能を提供する拡張メソッドとして実装できます。 これは、次のコード例に示すように、Animate メソッドを使用して、型 double 以外のプロパティを対象にできるためです。

public static class ViewExtensions
{
  public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
  {
    Func<double, Color> transform = (t) =>
      Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R),
                     fromColor.G + t * (toColor.G - fromColor.G),
                     fromColor.B + t * (toColor.B - fromColor.B),
                     fromColor.A + t * (toColor.A - fromColor.A));
    return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
  }

  public static void CancelAnimation(this VisualElement self)
  {
    self.AbortAnimation("ColorTo");
  }

  static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
  {
    easing = easing ?? Easing.Linear;
    var taskCompletionSource = new TaskCompletionSource<bool>();

    element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
    return taskCompletionSource.Task;
  }
}

Animate メソッドには、コールバック メソッドである transform 引数が必要です。 このコールバックへの入力は常に 0 から 1 の範囲の double です。 したがって、ColorTo メソッドは、0 から 1 までの範囲の double を受け入れ、その値に対応する Color 値を返す独自の変換 Func を定義します。 Color 値は、指定された 2 つの Color 引数の RGBA の値を補間することによって計算されます。 その後、Color 値は、特定のプロパティに適用するためにコールバック メソッドに渡されます。

このアプローチにより、ColorTo メソッドは、次のコード例に示すように、任意の Color プロパティをアニメーション化できます。

await Task.WhenAll(
  label.ColorTo(Color.Red, Color.Blue, c => label.TextColor = c, 5000),
  label.ColorTo(Color.Blue, Color.Red, c => label.BackgroundColor = c, 5000));
await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
await boxView.ColorTo(Color.Blue, Color.Red, c => boxView.Color = c, 4000);

このコード例では、ColorTo メソッドは、LabelTextColor プロパティと BackgroundColor プロパティ、ページの BackgroundColor プロパティ、BoxViewColor プロパティをアニメーション化します。