C# 点击按钮形成动画(Flash背景)

C# 点击按钮形成动画(Flash背景),c#,xaml,animation,xamarin.forms,C#,Xaml,Animation,Xamarin.forms,我想在我的表单上实现拨号板。 现在,在我的XAML中,我正在测试一个按钮: XAML 它能工作,但没有我想要的那么平稳。 你能建议我添加动画而不是背景色吗 根据 您的代码如下所示 XAML 还可以使用动画创建通用自定义控件: 在这个示例中,我使用了图像控件,但您可以使用按钮、标签等:) 我想更好的选择是创建一个可重用的特性来实现这一点。您可以利用来自的动画资源 例如,您可以创建一个扩展方法,该方法为您提供了一种在任何地方重用的好方法,如FadeTo或TranslateTo 这是相当容易的,除了在

我想在我的表单上实现拨号板。 现在,在我的XAML中,我正在测试一个按钮: XAML

它能工作,但没有我想要的那么平稳。 你能建议我添加动画而不是背景色吗

根据 您的代码如下所示

XAML


还可以使用动画创建通用自定义控件:

在这个示例中,我使用了图像控件,但您可以使用按钮、标签等:)


我想更好的选择是创建一个可重用的特性来实现这一点。您可以利用来自的动画资源

例如,您可以创建一个扩展方法,该方法为您提供了一种在任何地方重用的好方法,如
FadeTo
TranslateTo

这是相当容易的,除了在这种特殊情况下可以平滑地改变颜色的逻辑。代码如下:

public static class AnimationExtensions
{
    public static Task<bool> ChangeBackgroundColorTo(this Button self, Color newColor, uint length = 250, Easing easing = null)
    {
        Task<bool> ret = new Task<bool>(() => false);

        if (!self.AnimationIsRunning(nameof(ChangeBackgroundColorTo)))
        {
            Color fromColor = self.BackgroundColor;

            try
            {
                Func<double, Color> transform = (t) =>
                  Color.FromRgba(fromColor.R + t * (newColor.R - fromColor.R),
                                 fromColor.G + t * (newColor.G - fromColor.G),
                                 fromColor.B + t * (newColor.B - fromColor.B),
                                 fromColor.A + t * (newColor.A - fromColor.A));

                ret = TransmuteColorAnimation(self, nameof(ChangeBackgroundColorTo), transform, length, easing);
            }
            catch (Exception ex)
            {
                // to supress animation overlapping errors 
                self.BackgroundColor = fromColor;
            }
        }

        return ret;
    }

    private static Task<bool> TransmuteColorAnimation(Button button, string name, Func<double, Color> transform, uint length, Easing easing)
    {
        easing = easing ?? Easing.Linear;
        var taskCompletionSource = new TaskCompletionSource<bool>();

        button.Animate(name, transform, (color) => { button.BackgroundColor = color; }, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
        return taskCompletionSource.Task;
    }
}
处理单击代码隐藏:

private async void btnTest_Click(object sender, EventArgs args)
{
    #region You will not need this block, it is just to choose a random color for change to
    var colors = new[] { Color.Red, Color.Pink, Color.Silver, Color.Yellow, Color.Black, Color.Green };
    var rnd = new Random();

    var actualColor = btnTeste.BackgroundColor;
    var randomColor = colors.Where(c => c != actualColor).ToArray()[rnd.Next(0, colors.Length - 2)];
    #endregion

    // Here is the effective use of the smooth background color change animation
    await btnTeste.ChangeBackgroundColorTo(randomColor, 150, Easing.CubicOut);
    await btnTeste.ChangeBackgroundColorTo(actualColor, 100, Easing.SinOut);
}
编辑:

以下是结果(gif显示单击和双击,因此您可以看到许多平滑的更改):


@Johannes谢谢,我已经看过这篇文章了。问题-在哪种情况下,我应该根据您提供给我的此页面上的已接受答案绑定此方法“StartAnimation”?此外,在我的CodeBehind中,我无法从您的
按钮中“看到”名为x:Name=“whiteLabel”?的标签。谢谢您单击的
按钮选择联系人。您必须添加两个具有两种不同背景颜色(一个为0080ff,另一个为22ac38)的按钮(到网格中,以覆盖它们),可能有一个按钮没有单击事件(取决于您希望功能的方式…),并给出一个x:Name=“buttonSelectContactStandardColor”和另一个x:Name=“buttonSelectContactAnimationColor”,然后在正常显示的那一个上调用
FadeTo
。@Johannes我明白你说的。如果你想用示例代码回答我的问题,我可以接受你的答案吗?谢谢你指出:)@stefan0309通常这是个好主意,但对于这个特定的例子,你需要两个按钮/标签/任何一个在另一个之上的按钮/标签,只有一个按钮/标签具有淡出动画,以实现背景的闪烁效果。您必须了解,FadeTo只是不透明值的淡入淡出动画,因此您只需缓慢隐藏按钮,然后让它再次出现。我不知道这是否可能与自定义渲染器,但这可能是最好的方法,类似的东西…真棒!我喜欢这种方法伟大的解决方案,这应该是公认的答案!做得漂亮,我的朋友!更简单,没有自定义渲染器!
<StackLayout Spacing="20" Padding="15">
    <Grid>
        <Button
        x:Name="buttonSelectContactSecond"
        Text="CLICK" BackgroundColor="#22ac38" />
        <Button
            x:Name="buttonSelectContact"
            Clicked="buttonSelectContact_Clicked"
            Text="CLICK" BackgroundColor="#0080ff" />
    </Grid>
</StackLayout>
private async void buttonSelectContact_Clicked(object sender, EventArgs e) 
{
    StartAnimation();
}

private async void StartAnimation()
{
    await Task.Delay(200);
    await buttonSelectContact.FadeTo(0, 250);
    await Task.Delay(200);
    await buttonSelectContact.FadeTo(1, 250);
}
public class NativeImage : Image
{
    public static readonly BindableProperty CommandProperty =
        BindableProperty.Create(
            nameof(Command),
            typeof(ICommand),
            typeof(NativeImage),
            default(ICommand));

    public ICommand Command
    {
        get => (ICommand) GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    public static readonly BindableProperty CommandParameterProperty =
        BindableProperty.Create(
            nameof(Command),
            typeof(object),
            typeof(NativeImage));

    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    private ICommand TransitionCommand
    {
        get
        {
            return new Command(async () =>
            {
                AnchorX = 0.48;
                AnchorY = 0.48;
                await this.ScaleTo(0.8, 50, Easing.Linear);
                await Task.Delay(100);
                await this.ScaleTo(1, 50, Easing.Linear);
                Command?.Execute(CommandParameter);
            });
        }
    }

    public NativeImage()
    {
        Initialize();
    }

    public void Initialize()
    {
        GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = TransitionCommand
        });
    }
}
public static class AnimationExtensions
{
    public static Task<bool> ChangeBackgroundColorTo(this Button self, Color newColor, uint length = 250, Easing easing = null)
    {
        Task<bool> ret = new Task<bool>(() => false);

        if (!self.AnimationIsRunning(nameof(ChangeBackgroundColorTo)))
        {
            Color fromColor = self.BackgroundColor;

            try
            {
                Func<double, Color> transform = (t) =>
                  Color.FromRgba(fromColor.R + t * (newColor.R - fromColor.R),
                                 fromColor.G + t * (newColor.G - fromColor.G),
                                 fromColor.B + t * (newColor.B - fromColor.B),
                                 fromColor.A + t * (newColor.A - fromColor.A));

                ret = TransmuteColorAnimation(self, nameof(ChangeBackgroundColorTo), transform, length, easing);
            }
            catch (Exception ex)
            {
                // to supress animation overlapping errors 
                self.BackgroundColor = fromColor;
            }
        }

        return ret;
    }

    private static Task<bool> TransmuteColorAnimation(Button button, string name, Func<double, Color> transform, uint length, Easing easing)
    {
        easing = easing ?? Easing.Linear;
        var taskCompletionSource = new TaskCompletionSource<bool>();

        button.Animate(name, transform, (color) => { button.BackgroundColor = color; }, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
        return taskCompletionSource.Task;
    }
}
<Button Text="Nice button ;)"
        BackgroundColor="Gray"
        x:Name="btnTeste"
        Clicked="btnTest_Click"/>
private async void btnTest_Click(object sender, EventArgs args)
{
    #region You will not need this block, it is just to choose a random color for change to
    var colors = new[] { Color.Red, Color.Pink, Color.Silver, Color.Yellow, Color.Black, Color.Green };
    var rnd = new Random();

    var actualColor = btnTeste.BackgroundColor;
    var randomColor = colors.Where(c => c != actualColor).ToArray()[rnd.Next(0, colors.Length - 2)];
    #endregion

    // Here is the effective use of the smooth background color change animation
    await btnTeste.ChangeBackgroundColorTo(randomColor, 150, Easing.CubicOut);
    await btnTeste.ChangeBackgroundColorTo(actualColor, 100, Easing.SinOut);
}