中的自訂動畫 Xamarin.Forms
Animation 類別是所有 Xamarin.Forms 動畫的建置組塊,而 ViewExtensions 類別中的擴充方法會建立一或多個 Animation 物件。 本文示範如何使用 Animation 類別來建立和取消動畫、同步處理多個動畫,以及建立自定義動畫,以動畫顯示現有動畫方法未產生動畫的屬性。
建立 Animation
物件時必須指定一些參數,包括要產生動畫之屬性的開始和結束值,以及變更屬性值的回呼。 Animation
物件也可以維護可執行和同步處理的子動畫集合。 如需詳細資訊,請參閱 子動畫。
藉由呼叫 Commit
方法,執行以 Animation
類別建立的動畫,其中不一定包含子動畫。 這個方法會指定動畫的持續時間,以及其他專案,可控制是否要重複動畫的回呼。
此外,類別 Animation
具有 IsEnabled
可檢查的屬性,以判斷操作系統是否已停用動畫,例如啟用省電模式時。
建立動畫
建立 Animation
物件時,通常至少需要三個參數,如下列程式代碼範例所示:
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) 會識別動畫的擁有者。 這可以是套用動畫的視覺專案,或是另一個視覺元素,例如頁面。
- 第二個自變數 (name) 會以名稱識別動畫。 名稱會與擁有者結合,以唯一識別動畫。 然後,這個唯一的識別可用來判斷動畫是否正在執行或
AnimationIsRunning
取消它 (AbortAnimation
)。 - 第三個自變數 (rate) 表示建構函式中所
Animation
定義回呼方法的每個呼叫之間的毫秒數。 - 第四個自變數 (length) 表示動畫的持續時間,以毫秒為單位。
- 第五個自變數 (easing) 定義要用於動畫中的 easing 函式。 或者,可以將 easing 函式指定為建構函式的
Animation
自變數。 如需 Easing 函式的詳細資訊,請參閱 Easing 函式。 - 第六個自變數(已完成)是一個回呼,會在動畫完成時執行。 此回呼會採用兩個自變數,第一個自變數表示最終值,而第二個
bool
自變數則為已設定為 ,如果取消動畫,則為true
。 或者,完成的回呼也可以指定為建構函式的Animation
自變數。 不過,使用單一動畫時,如果在建構函式和Commit
方法中Animation
指定完成的回呼,則只會執行 方法中指定的Commit
回呼。 - 第七個自變數 (repeat) 是可讓動畫重複的回呼。 它會在動畫結尾呼叫,並傳
true
回表示應該重複動畫。
整體效果是建立動畫,使用 Linear
easing 函式將 的 屬性Image
從 1 增加到 Scale
2,超過 2 秒(2000 毫秒)。 每次動畫完成時,其 Scale
屬性都會重設為 1,而動畫會重複。
注意
並行動畫可以藉由為每個動畫建立 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
會指定何時開始和完成子動畫。 自變數值必須介於 0 到 1 之間,並代表指定子動畫將使用中之父動畫內的相對期間。 因此,在此範例中, scaleUpAnimation
會針對動畫的前半部使用中、 scaleDownAnimation
將作用中動畫的下半部,而且 rotateAnimation
會在整個期間內為使用中。
整體效果是動畫發生在 4 秒 (4000 毫秒) 以上。 將 scaleUpAnimation
屬性從 1 到 2 產生動畫 Scale
效果,超過 2 秒。 接著,Scale
將 scaleDownAnimation
屬性從 2 到 1 產生動畫效果,超過 2 秒。 雖然發生這兩個縮放動畫,但 rotateAnimation
屬性會從 0 到 360 產生動畫 Rotation
效果,超過 4 秒。 請注意,縮放動畫也會使用 Easing 函式。 Easing 函 SpringIn
式會在 Image
開始縮小之前先縮小,而 SpringOut
Easing 函式會使 Image
變成小於其完整動畫結尾的實際大小。
使用子動畫的物件與不使用子動畫的物件之間 Animation
有許多差異:
- 使用子動畫時,子動畫的完成回呼會指出子系完成的時間,而傳遞至
Commit
方法的完成回呼會指出整個動畫完成的時間。 - 使用子動畫時,從方法上的
Commit
重複回呼傳回true
不會造成動畫重複,但動畫會繼續執行,而不會有新的值。 - 在方法中包含
Commit
easing 函式,而 easing 函式傳回大於 1 的值時,動畫將會終止。 如果 easing 函式傳回小於 0 的值,該值會限製為 0。 若要使用傳回小於 0 或大於 1 值的 Easing 函式,它必須在其中一個子動畫中指定,而不是在 方法中Commit
指定。
類別 Animation
也包含 WithConcurrent
可用來將子動畫新增至父 Animation
物件的方法。 不過,其 開始 和 完成 自變數值不限於 0 到 1,但只有對應至 0 到 1 範圍的子動畫部分才會作用中。 例如,如果WithConcurrent
方法呼叫定義以 1 到 6 之屬性為目標Scale
的子動畫,但以 -2 和 3 的開頭和完成值為目標,則 -2 的開始值會對應至 Scale
1 的值,而完成值為 3 會對應至 Scale
6 的值。 因為介於 0 和 1 以外的值在動畫中沒有作用, Scale
因此屬性只會以動畫顯示從 3 到 6。
取消動畫
應用程式可以使用對擴充方法的呼叫 AbortAnimation
來取消動畫,如下列程式代碼範例所示:
this.AbortAnimation ("SimpleAnimation");
請注意,動畫是由動畫擁有者與動畫名稱的組合唯一識別。 因此,必須指定執行動畫時所指定的擁有者和名稱,才能取消動畫。 因此,程式代碼範例會立即取消頁面所擁有的動畫 SimpleAnimation
。
建立自定義動畫
到目前為止所示範的範例示範了可以同樣透過 類別中 ViewExtensions
方法達成之動畫。 不過,類別的優點 Animation
是它可以存取回呼方法,而這個方法會在動畫值變更時執行。 這可讓回呼實作任何所需的動畫。 例如,下列程式代碼範例會將頁面的 屬性設定為 方法所Color.FromHsla
建立的值,其色調值範圍從 0 到 Color
1,以動畫BackgroundColor
顯示頁面的 屬性:
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);
產生的動畫提供透過彩虹色彩來推進頁面背景的外觀。
如需建立複雜動畫的更多範例,包括 Bezier 曲線動畫,請參閱使用 Xamarin.Forms建立 Mobile Apps 第 22 章。
建立自定義動畫擴充方法
類別中的 ViewExtensions
擴充方法會將屬性從其目前值動畫到指定的值。 例如, ColorTo
這可讓您難以建立動畫方法,可用來建立一個值到另一個值之色彩的動畫方法,因為:
- 類別所定義的唯
Color
一屬性是BackgroundColor
,這不一定是動畫所需的Color
VisualElement
屬性。 - 屬性的
Color
目前值通常是Color.Default
,這不是真正的色彩,而且不能用於插補計算。
此問題的解決方案是沒有 ColorTo
方法以特定 Color
屬性為目標。 相反地,可以使用回呼方法撰寫,將插補 Color
值傳回給呼叫端。 此外,方法會採用 start 和 end Color
自變數。
方法 ColorTo
可以實作為擴充方法,該方法會使用 Animate
類別中的 AnimationExtensions
方法來提供其功能。 這是因為 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
需要 轉換 自變數,這是回呼方法。 這個回呼的輸入一律 double
介於 0 到 1。 因此, ColorTo
方法會定義它自己的轉換 Func
,接受 double
範圍從 0 到 1 的 ,並傳 Color
回對應至該值的值。 值Color
是藉由插補兩個提供的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
,方法會以動畫顯示 TextColor
的和 BackgroundColor
屬性 Label
、 BackgroundColor
頁面的 屬性,以及 Color
的 BoxView
屬性。