I have been following this question to make a color animation.
I can create the animation and it runs fine until the animation gets started again. There is a moment where it feels that the animation has been started/stopped there for a moment. I want the animation to run smoothly so that it doesn't show any restarting effects in the colors. Is it possible to do that?
I am using the following code:
<Storyboard x:Key="GradientAnimation"
RepeatBehavior="Forever"
Storyboard.TargetName="BackgroundBrush"
SpeedRatio="0.3">
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Background).(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)"
EnableDependentAnimation="True"
BeginTime="-0:0:0.5">
<LinearColorKeyFrame KeyTime="0:0:0" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:1" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:2" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:3" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:4" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:5" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:6" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:7" Value="Red"/>
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Background).(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)"
EnableDependentAnimation="True">
<LinearColorKeyFrame KeyTime="0:0:0" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:1" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:2" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:3" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:4" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:5" Value="Black"/>
<LinearColorKeyFrame KeyTime="0:0:6" Value="Red"/>
<LinearColorKeyFrame KeyTime="0:0:7" Value="Black"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
And I start this animation in code-behind:
((Storyboard)Resources["GradientAnimation"]).Begin();
Maybe there is some kind of 'easing' method that mixes up the start and stop of the animation so it appears as smooth as a long running animation. Any suggestions?
You can try EasingColorKeyFrame
but the animation will never be smooth because it's running on the UI thread (i.e. EnableDependentAnimation="True"
).
Here's some good and bad news. The good is that we now have new gradient brush API at the Composition layer level, which means it's fully animatable and running off the UI thread. So performance wise it's going to be much better than the current Storyboard
solution. But it's only available in Windows 10 Insider Preview 16225 onward, which means it's not going to work for most of your current users except insiders.
However, I am still going to post this new solution here for future reference since there's no sample available on this topic yet.
Note I have added some extra sauce to the animation to make it more interesting. If you only want colors to be animated, feel free to move the EndPoint
and RotationAngleInDegrees
animations (I use the EndPoint
animation to animate from a solid background color to a gradient at the startup of the app and RotationAngleInDegrees
to rotate the gradient brush forever).
var compositor = Window.Current.Compositor;
// Initially, we set the end point to be (0,0) 'cause we want to animate it at start.
// If you don't want this behavior, simply set it to a different value within (1,1).
_gradientBrush = compositor.CreateLinearGradientBrush();
_gradientBrush.EndPoint = Vector2.Zero;
// Create gradient initial colors.
var gradientStop1 = compositor.CreateColorGradientStop();
gradientStop1.Offset = 0.0f;
gradientStop1.Color = GradientStop1StartColor;
var gradientStop2 = compositor.CreateColorGradientStop();
gradientStop2.Offset = 1.0f;
gradientStop2.Color = GradientStop2StartColor;
_gradientBrush.ColorStops.Add(gradientStop1);
_gradientBrush.ColorStops.Add(gradientStop2);
// Assign the gradient brush to the Root element's Visual.
_backgroundVisual = compositor.CreateSpriteVisual();
_backgroundVisual.Brush = _gradientBrush;
ElementCompositionPreview.SetElementChildVisual(Root, _backgroundVisual);
// There are 3 animations going on here.
// First, we kick off an EndPoint offset animation to create an special entrance scene.
// Once it's finished, we then kick off TWO other animations simultaneously.
// These TWO animations include a set of gradient stop color animations and
// a rotation animation that rotates the gradient brush.
var linearEase = compositor.CreateLinearEasingFunction();
var batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
batch.Completed += (s, e) =>
{
StartGradientColorAnimations();
StartGradientRotationAnimation();
};
var endPointOffsetAnimation = compositor.CreateVector2KeyFrameAnimation();
endPointOffsetAnimation.Duration = TimeSpan.FromSeconds(3);
endPointOffsetAnimation.InsertKeyFrame(1.0f, Vector2.One);
_gradientBrush.StartAnimation(nameof(_gradientBrush.EndPoint), endPointOffsetAnimation);
batch.End();
void StartGradientColorAnimations()
{
var color1Animation = compositor.CreateColorKeyFrameAnimation();
color1Animation.Duration = TimeSpan.FromSeconds(10);
color1Animation.IterationBehavior = AnimationIterationBehavior.Forever;
color1Animation.Direction = AnimationDirection.Alternate;
color1Animation.InsertKeyFrame(0.0f, GradientStop1StartColor, linearEase);
color1Animation.InsertKeyFrame(0.5f, Color.FromArgb(255, 65, 88, 208), linearEase);
color1Animation.InsertKeyFrame(1.0f, Color.FromArgb(255, 43, 210, 255), linearEase);
gradientStop1.StartAnimation(nameof(gradientStop1.Color), color1Animation);
var color2Animation = compositor.CreateColorKeyFrameAnimation();
color2Animation.Duration = TimeSpan.FromSeconds(10);
color2Animation.IterationBehavior = AnimationIterationBehavior.Forever;
color2Animation.Direction = AnimationDirection.Alternate;
color2Animation.InsertKeyFrame(0.0f, GradientStop2StartColor, linearEase);
color1Animation.InsertKeyFrame(0.5f, Color.FromArgb(255, 200, 80, 192), linearEase);
color2Animation.InsertKeyFrame(1.0f, Color.FromArgb(255, 43, 255, 136), linearEase);
gradientStop2.StartAnimation(nameof(gradientStop2.Color), color2Animation);
}
void StartGradientRotationAnimation()
{
var rotationAnimation = compositor.CreateScalarKeyFrameAnimation();
rotationAnimation.Duration = TimeSpan.FromSeconds(15);
rotationAnimation.IterationBehavior = AnimationIterationBehavior.Forever;
rotationAnimation.InsertKeyFrame(1.0f, 360.0f, linearEase);
_gradientBrush.StartAnimation(nameof(_gradientBrush.RotationAngleInDegrees), rotationAnimation);
}
Here's a working sample and the following is how it looks like. :)