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 ミリ秒) かけて Image
の Scale
プロパティを 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 秒かけてアニメーション化します。 両方のスケール アニメーションが発生している間、rotateAnimation
は Rotation
プロパティを 0 から 360 まで 4 秒間アニメーション化します。 スケーリング アニメーションにもイージング関数が使用されていることに注意してください。 SpringIn
イージング関数により、Image
は最初に縮小してから大きくなり、SpringOut
イージング関数により、Image
は完全なアニメーションの終わりに向かって実際のサイズよりも小さくなります。
子アニメーションを使用する Animation
オブジェクトと、子アニメーションを使用しないオブジェクトの間には、多くの違いがあります。
- 子アニメーションを使用する場合、子アニメーションの finished コールバックは子アニメーションが完了した時間を示し、
Commit
メソッドに渡された finished コールバックはアニメーション全体が完了した時間を示します。 - 子アニメーションを使用する場合、
Commit
メソッドの repeat コールバックからtrue
を返してもアニメーションは繰り返されませんが、アニメーションは新しい値なしで引き続き実行されます。 Commit
メソッドにイージング関数が含まれており、イージング関数が 1 より大きい値を返した場合、アニメーションは終了します。 イージング関数が 0 未満の値を返す場合、値は 0 に固定されます。 0 未満の値や 1 より大きい値を返すイージング関数を使用するには、Commit
メソッドではなく、子アニメーションのいずれかで指定する必要があります。
Animation
クラスには、親 Animation
オブジェクトに子アニメーションを追加するために使用できる WithConcurrent
メソッドも含まれています。 ただし、begin と finishの引数値は 0 から 1 に制限されず、0 から 1 の範囲に対応する子アニメーションの部分のみがアクティブになります。 たとえば、WithConcurrent
メソッド呼び出しが、Scale
プロパティの 1 から 6 をターゲットとする子アニメーションを定義し、begin と finish の値が -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
引数の R
、G
、B
、A
の値を補間することによって計算されます。 その後、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
メソッドは、Label
の TextColor
プロパティと BackgroundColor
プロパティ、ページの BackgroundColor
プロパティ、BoxView
の Color
プロパティをアニメーション化します。