使用AnimationExtensions
为什么ViewExtensions不包含ColorTo动画? 这种方法没有你最初假设的那么明显有三个可能的原因:
首先,VisualElement定义的唯一Color属性是BackgroundColor,但通常不是要设置动画的Color属性。 您更有可能要为Label的TextColor属性或BoxView的Color属性设置动画。
其次,ViewExtensions中的所有方法都会将属性从其当前值设置为指定值。 但是,Color类型属性的当前值通常是Color.Default,它不是真彩色,不能用于插值计算。
第三,两个颜色值之间的插值可以用各种不同的方式计算,但最有可能是两个:您可能想要插入红绿蓝值或者
色调饱和度 - 光度值。在这两种情况下,中间值将不同。
让我们用三种不同的解决方案来处理这三个问题:
首先,让我们不要将color-animation方法定位到特定属性。让我们用一个回调方法编写方法,该方法将插值的Color值传递给调用者。
其次,让我们要求将起始颜色值和结束颜色值都提供给动画方法。
第三,让我们编写两种不同的方法,RgbColorAnimation和HslColorAnimation。
您当然可以使用Animation类和Commit来完成这项工作,但让我们深入研究Xamarin.Forms动画系统并使用AnimationExtensions类中的方法。
AnimationExtensions有四种不同的方法,名为Animate,以及AnimateKinetic方法。 AnimateKinetic方法旨在将“拖动”值应用于动画,使其像摩擦一样减速。但是,它尚未以允许轻松预测结果的方式工作,本章未对此进行演示。
在四种Animate方法中,通用形式是最通用的:
public static void Animate<T>(this IAnimatable self,
string name,
Func<double, T> transform,
Action<T> callback,
uint rate = 16,
uint length = 250,
Easing easing = null,
Action<T, bool> finished = null,
Func<bool> repeat = null);
泛型类型是要设置动画的属性的类型 - 例如,Color。 此时,您应该识别除名为transform的回调方法之外的所有这些参数。 该回调的输入始终是t或进度值,范围从0到1.输出是泛型类型的值 - 例如,Color。 然后将该值传递给回调方法以应用于特定属性。
以下是Xamarin.FormsBook.Toolkit库的MoreViewExtensions类中的RgbColorAnimation和HslColorAnimation:
namespace Xamarin.FormsBook.Toolkit
{
public static class MoreViewExtensions
{
__
public static Task<bool> RgbColorAnimation(this VisualElement view,
Color fromColor, Color toColor,
Action<Color> callback,
uint length = 250,
Easing easing = null)
{
Func<double, Color> transform = (t) =>
{
return 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(view, "RgbColorAnimation", transform,
callback, length, easing);
}
public static void CancelRgbColorAnimation(VisualElement view)
{
view.AbortAnimation("RgbColorAnimation");
}
public static Task<bool> HslColorAnimation(this VisualElement view,
Color fromColor, Color toColor,
Action<Color> callback,
uint length = 250,
Easing easing = null)
{
Func<double, Color> transform = (t) =>
{
return Color.FromHsla(
fromColor.Hue + t * (toColor.Hue - fromColor.Hue),
fromColor.Saturation + t * (toColor.Saturation - fromColor.Saturation),
fromColor.Luminosity + t * (toColor.Luminosity - fromColor.Luminosity),
fromColor.A + t * (toColor.A - fromColor.A));
};
return ColorAnimation(view, "HslColorAnimation", transform,
callback, length, easing);
}
public static void CancelHslColorAnimation(VisualElement view)
{
view.AbortAnimation("HslColorAnimation");
}
static Task<bool> ColorAnimation(VisualElement view,
string name,
Func<double, Color> transform,
Action<Color> callback,
uint length,
Easing easing)
{
easing = easing ?? Easing.Linear;
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
view.Animate<Color>(name, transform, callback, 16,
length, easing, (value, canceled) =>
{
taskCompletionSource.SetResult(canceled);
});
return taskCompletionSource.Task;
}
}
}
这两个方法定义了自己的转换函数,然后利用私有ColorAnimation方法实际调用AnimationExtensions中的Animate方法。 因为这些方法没有明确地定位特定的可视元素,所以不需要WeakReference类。
ColorAnimations程序演示了以各种方式为各种颜色属性设置动画的方法。 作为Label的XAML文件,两个Button元素和两个BoxView元素:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ColorAnimations.ColorAnimationsPage">
<StackLayout>
<Label x:Name="label"
Text="TEXT"
FontSize="48"
FontAttributes="Bold"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="Rainbow Background"
Clicked="OnRainbowBackgroundButtonClicked"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="BoxView Color"
Clicked="OnBoxViewColorButtonClicked"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<StackLayout Orientation="Horizontal">
<BoxView x:Name="boxView1"
Color="Blue"
HeightRequest="100"
HorizontalOptions="FillAndExpand" />
<BoxView x:Name="boxView2"
Color="Blue"
HeightRequest="100"
HorizontalOptions="FillAndExpand" />
</StackLayout>
</StackLayout>
</ContentPage>
代码隐藏文件使用RgbColorAnimation和HslColorAnimation的混合来为Label文本及其背景,页面背景和两个BoxView元素的颜色设置动画。
标签文本及其背景在黑色和白色之间相反地连续动画。 只有动画的中间 - 当文本和背景都是中灰色时 - 文本是不可见的:
public partial class ColorAnimationsPage : ContentPage
{
public ColorAnimationsPage()
{
InitializeComponent();
AnimationLoop();
}
async void AnimationLoop()
{
while (true)
{
Action<Color> textCallback = color => label.TextColor = color;
Action<Color> backCallback = color => label.BackgroundColor = color;
await Task.WhenAll(
label.RgbColorAnimation(Color.White, Color.Black, textCallback, 1000),
label.HslColorAnimation(Color.Black, Color.White, backCallback, 1000));
await Task.WhenAll(
label.RgbColorAnimation(Color.Black, Color.White, textCallback, 1000),
label.HslColorAnimation(Color.White, Color.Black, backCallback, 1000));
}
}
__
}
在Color.Black和Color.White之间进行动画制作时,无论使用RgbColorAnimation还是HslColorAnimation都无关紧要。结果是一样的。黑色在RGB中表示为(0,0,0),在HSL中表示为(0,0,0)。白色是RGB中的(1,1,1),HSL中是(0,0,1)。在中间点,RGB颜色(0.5,0.5,0.5)与HSL颜色(0,0,0.5)相同。
HslColorAnimation非常适合通过所有色调进行动画制作,这些色调大致对应于彩虹的颜色,传统上是红色,橙色,黄色,绿色,蓝色,靛蓝色和紫罗兰色。在彩色动画中,最终动画返回红色通常在最后发生。通过此序列动画RGB颜色需要首先动画从Color.Red到Color.Yellow,然后Color.Yellow到Color.Green,然后Color.Green到Color.Aqua,然后Color.Aqua到Color.Blue,然后Color.Blue到Color.Fuchsia,最后Color.Fuchsia到Color.Red。
使用HslColorAnimation,所有必要的是在两个红色表示之间设置动画,一个将Hue设置为0,另一个设置为Hue设置为1:
public partial class ColorAnimationsPage : ContentPage
{
__
async void OnRainbowBackgroundButtonClicked(object sender, EventArgs args)
{
// Animate from Red to Red.
await this.HslColorAnimation(Color.FromHsla(0, 1, 0.5),
Color.FromHsla(1, 1, 0.5),
color => BackgroundColor = color,
10000);
BackgroundColor = Color.Default;
}
__
}
即使在两种原色之间使用简单的动画,RgbColorAnimation和HslColorAnimation也可以产生不同的结果。 考虑从蓝色到红色的动画。 ColorAnimations程序通过使用两种动画方法设置两个BoxView元素的颜色动画来演示差异:
public partial class ColorAnimationsPage : ContentPage
{
__
async void OnBoxViewColorButtonClicked(object sender, EventArgs args)
{
Action<Color> callback1 = color => boxView1.Color = color;
Action<Color> callback2 = color => boxView2.Color = color;
await Task.WhenAll(boxView1.RgbColorAnimation(Color.Blue, Color.Red, callback1, 2000),
boxView2.HslColorAnimation(Color.Blue, Color.Red, callback2, 2000));
await Task.WhenAll(boxView1.RgbColorAnimation(Color.Red, Color.Blue, callback1, 2000),
boxView2.HslColorAnimation(Color.Red, Color.Blue, callback2, 2000));
}
}
蓝色具有(0,0,1)的RGB表示和(0.67,1,0.5)的HSL表示。 Red的RGB表示为(1,0,0),HSL的表示为(1,0,0.5)。 在RGB动画的中途,插值颜色为(0.5,0,0.5),在Xamarin.Forms中称为Color.Magenta。 然而,在HslColorAnimation的中间,插值颜色为(0.83,1,0.5),这是较浅的Color.Fuchsia,其RGB表示为(1,0,1)。
此屏幕截图显示了两个BoxView元素的动画从蓝色到红色的进度(从左到右):
两者都不是“正确”或“错误”。这只是两种颜色之间插值的两种不同方式,以及简单的ColorAnimation方法不足的原因。
构建动画
没有动画的XAML表示,因此本章的重点一定是代码而不是标记。
但是,当您将动画与样式结合使用,以及使用MVVM和数据绑定时,您可能想要一种方法来引用XAML中的动画。 这是可能的,您将在下一章中看到如何将动画封装在称为触发器操作和行为的类中,然后使它们成为应用程序视觉效果的样式和数据绑定的一部分。