连续背景刷动画

Continuous background brush animation

我想在鼠标悬停在我的用户控件上时制作一个连续的笔刷动画。

我已经有了这个:

    private void MainControl_MouseEnter(object sender, MouseEventArgs e)
    {            
        BeginColorAnimation(Background, BackgroundHover);
    }

    private void BeginColorAnimation(Brush from, Brush to)
    {            
        BrushAnimation anim = new BrushAnimation()
        {
            From = from,
            To = to,
            Duration = AnimationDuration
        };
        
        BackgroundBorder.BeginAnimation(Border.BackgroundProperty, anim);                        
    }        

    private void MainControl_MouseLeave(object sender, MouseEventArgs e)
    {            
        BeginColorAnimation(BackgroundHover, Background);
    }

可以找到 BrushAnimation class here

我现在遇到的问题是,如果我将鼠标快速移到控件上,控件的背景会立即从背景画笔切换到 BackgroundHover 画笔(反之亦然),我不希望这样。

我怎样才能让它看起来像笔刷之间的连续变化?

编辑 1

Here你可以看到我遇到的问题的视频。

让我解释一下:

假设我们有两把刷子,一把黑色,一把蓝色(或任何其他颜色)。当鼠标悬停在控件上时,我想将颜色背景从黑色更改为蓝色,当鼠标离开控件时,我想将背景颜色更改回黑色。 那么如果鼠标悬停在控件上并在动画中途退出会怎样呢?看起来好像是从一个画笔跳到另一个画笔。我想避免这种情况。

我尝试使用:

private void MainControl_MouseEnter(object sender, MouseEventArgs e)
{            
    BeginColorAnimation(BackgroundBorder.Background, BackgroundHover);
}

private void MainControl_MouseLeave(object sender, MouseEventArgs e)
{            
    BeginColorAnimation(BackgroundBorder.Background, Background);
}

但随后我收到:'System.InvalidOperationException'无法冻结此 freezable。

编辑 2

最终使用 SolidColorBrush 而不是 Brush,并使用了 ColorAnimation class。

public bool BackgroundFrozen = true;

        private void MainControl_MouseEnter(object sender, MouseEventArgs e)
        {            
            BeginColorAnimation((SolidColorBrush)BackgroundBorder.Background, BackgroundHover);
        }

        private void BeginColorAnimation(SolidColorBrush from, SolidColorBrush to)
        {
            if(BackgroundFrozen)
            {
                BackgroundBorder.Background = BackgroundBorder.Background.CloneCurrentValue();
                BackgroundFrozen = false;
            }

            ColorAnimation anim = new ColorAnimation()
            {                
                From = from.Color,
                To = to.Color,
                Duration = AnimationDuration
            };
            
            BackgroundBorder.Background.BeginAnimation(SolidColorBrush.ColorProperty, anim);
        }        

        private void MainControl_MouseLeave(object sender, MouseEventArgs e)
        {            
            BeginColorAnimation((SolidColorBrush)BackgroundBorder.Background, Background);
        }

出于某种原因,边框背景 属性 一开始是冻结的,所以当动画第一次运行时,我使用 BackgroundFrozen 布尔值通过 CloneCurrentValue() 来“解冻”它。

结果here

阅读“编辑 2”,因为它是解决方案的一部分

根据某人的建议,我删除了 From = from, 并为动画制作了一个“动态”持续时间,同时考虑了 fromend 值。

结果:

start为原值
end为目标值
actual 介于 startend 之间(可能相同)

private ColorAnimation GetColorAnimation(SolidColorBrush start, SolidColorBrush end, SolidColorBrush actual)
        {
            ColorAnimation anim = new ColorAnimation()
            {                
                To = end.Color,
                Duration = new Duration(TimeSpan.FromMilliseconds(GetAnimationDuration(start, end, actual)))
            };

            return anim;
        }

GetAnimationDuration returns 包含以毫秒为单位的持续时间的 double

private double GetAnimationDuration(SolidColorBrush start, SolidColorBrush end, SolidColorBrush actual)
        {
            Color s = start.Color;
            Color e = end.Color;
            Color a = actual.Color;

            double Rdif = Math.Abs(s.R - e.R);
            double Gdif = Math.Abs(s.G - e.G);
            double Bdif = Math.Abs(s.B - e.B);            

            //Console.WriteLine("Rdif: {0}, Gdif: {1}, Bdif: {2}", Rdif, Gdif, Bdif);

            double Rp = Math.Abs(a.R - e.R) / Rdif;
            double Gp = Math.Abs(a.G - e.G) / Gdif;
            double Bp = Math.Abs(a.B - e.B) / Bdif;

            double p = 0;
            int c = 0;

            if(!double.IsNaN(Rp))
            {
                p += Rp;
                c += 1;
            }

            if (!double.IsNaN(Gp))
            {
                p += Gp;
                c += 1;
            }

            if (!double.IsNaN(Bp))
            {
                p += Bp;
                c += 1;
            }

            p /= c;

            //Console.WriteLine("Rp: {0}, Gp: {1}, Bp: {2}", Rp, Gp, Bp);

            double r = p * AnimationDuration;

            //Console.WriteLine("Result: {0}", r);

            return r;
        }

它的名字是这样的:

BackgroundBorder.Background.BeginAnimation(SolidColorBrush.ColorProperty, GetColorAnimation(startColor, endColor, (SolidColorBrush)BackgroundBorder.Background));

来自 GetAnimationDuration() 的持续时间为 500 毫秒的测试结果

---------------------------
Mouse enter
---------------------------
R: 0, G: 0, B: 255
Rdif: 255, Gdif: 0, Bdif: 255
Rp: 1, Gp: NaN, Bp: 1
Result: 500
---------------------------
Mouse leave (after having passed half of the animation (approximately))
---------------------------
R: 193, G: 0, B: 182
Rdif: 255, Gdif: 0, Bdif: 255
Rp: 0,756862745098039, Gp: NaN, Bp: 0,286274509803922
Result: 260,78431372549