I want to make a continuous brush animation whenever the mouse is over my usercontrol.
I already have this:
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);
}
The BrushAnimation class can be found here
The problem i have right now is that if i move the mouse quickly over the control, the Background of my control shifts from the Background brush to the BackgroundHover brush (or viceversa) instantly and i don't want that.
How can i make this to look like a continuous change between brushes?
EDIT 1
Here you can see a video of the problem i have.
Let me explain myself:
Imagine we have two brushes, one black and one blue (or any other color). When the mouse is over the control i want to change the background of my color from black to blue, and when the mouse exits the control i want to change the background back to black. So what happens if the mouse is over the control and exits halfway the animation? It looks like if was bouncing from one brush to another. I want to avoid that.
I tried using:
private void MainControl_MouseEnter(object sender, MouseEventArgs e)
{
BeginColorAnimation(BackgroundBorder.Background, BackgroundHover);
}
private void MainControl_MouseLeave(object sender, MouseEventArgs e)
{
BeginColorAnimation(BackgroundBorder.Background, Background);
}
But then i receive: 'System.InvalidOperationException' This freezable cannot be frozen.
EDIT 2
Ended up using a SolidColorBrush instead of a Brush, and using the 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);
}
For some reason the Border Background property is frozen at first, so i used a BackgroundFrozen bool to "Unfreeze" it with CloneCurrentValue() when the animation runs for first time.
Result here
READ "EDIT 2" AS IT IS PART OF THE SOLUTION
As suggested by someone, i removed From = from,
and make a "dynamic" duration for the animation taking into account the from
and the end
values.
Result:
start
is the original value
end
is the target value
actual
is something between start
and end
(could be the same)
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 a double
that contains the duration in milliseconds
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;
}
And it's called like this:
BackgroundBorder.Background.BeginAnimation(SolidColorBrush.ColorProperty, GetColorAnimation(startColor, endColor, (SolidColorBrush)BackgroundBorder.Background));
Test results from GetAnimationDuration()
with a duration of 500 ms
---------------------------
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