Introduction
The purpose of this tip is to demonstrate using multiple animations in a WPF XAML.
Background
The idea behind this animation came from this post. So after a few tries and mistakes, the resulting loading screen was created.
Using the Code
All code is in LoadingCircle.xaml.
From the original .gif, we were able to find that there are 24 dots rotating around the circle. Meaning that each dot is 15o from each other, 360o / 24 = 15o, which was translated to radians, xo * PI / 180o. Knowing the location of each dot starting location, we had to translate to a canvas' left and top location. Knowing we want a dot to be an ellipse with width and height of unit size 2 on a circle with a diameter of 22 units, we can calculate the left of the canvas to be center
x
+ (radius * cos (radian value))
and the top of the canvas to be centery + (radius * sin (radian value))
for each dot location.
It is noted from the original .gif that each dot rotates with the 3rd dot down. That is dot 1 rotates with dot 4, dot 3 with dot 6, etc., and ending with dot 23 with dot 2. Knowing this, we see that we need to use multiple canvas' on the main canvas. To find the center point at which the two dots rotate, we use the following formula for the Left dot1x + (dot4x + 1 - dot1x + 1) / 2
, note the '+1's are due to the ellipse size, and top dot1y + (dot4y + 1 - dot1y + 1) / 2
this is the location of the canvas for the two points. We take the desired location of the dot minus the canvas location to get the location of the dot in relation to the sub-canvas. Please see the Calculations.xlsx spreadsheet for reference.
Now that we have the locations of the dots, I cheated to get the colors of the dots. I took a screen shot of the .gif and put it in paint and used color picker to get the RGB values.
With the dots located and colored, we start on the animation. You will notice that the animation is linked through the names of Controls, so always name your controls so if you need to add something, it is easier to link them.
So if we look at the main canvas and the first sub-canvas containing dots 1 & 4, we see that the whole canvas uses Storyboard SpinnerRotate
to control the animation with dots 1 & 4 using Point1
and Point4
, respectively, to control color changes, Group1
to control rotation, and the circle, connecting dots 1 & 4, using Group1Circle
to fade in and out.
<Canvas RenderTransformOrigin="0.5,0.5"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="24" Height="24" >
-->
<Canvas Canvas.Left="15.8890873" Canvas.Top="2.610912703"
RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Center"
VerticalAlignment="Center">
-->
<Ellipse Width="8.419035512" Height="8.419035512" x:Name="Group1Circle"
Canvas.Left="-4.209517756" Canvas.Top="-4.209517756"
StrokeThickness="0.125">
<Ellipse.Stroke>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color A="255" R="253" G="95" B="1" />
</SolidColorBrush.Color>
</SolidColorBrush>
</Ellipse.Stroke>
</Ellipse>
-->
<Ellipse Width="2" Height="2"
x:Name="Point1" Canvas.Left="-4.889087297"
Canvas.Top="-2.610912703"
Stretch="Fill" Fill="Red" Opacity="1.0"/>
-->
<Ellipse Width="2" Height="2"
x:Name="Point4" Canvas.Left="2.889087297"
Canvas.Top="0.610912703" Opacity="1.0" >
<Ellipse.Fill>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color A="255" R="251" G="190" B="3" />
</SolidColorBrush.Color>
</SolidColorBrush>
</Ellipse.Fill>
</Ellipse>
<Canvas.RenderTransform>
<RotateTransform x:Name="Group1" Angle="135" />
</Canvas.RenderTransform>
</Canvas>
<Canvas.Triggers>
<EventTrigger RoutedEvent="ContentControl.Loaded">
<BeginStoryboard Storyboard="{StaticResource SpinnerRotate}" />
</EventTrigger>
</Canvas.Triggers>
</Canvas>
These connect to animations:
<Storyboard x:Key="SpinnerRotate" RepeatBehavior="Forever">
-->
<DoubleAnimation BeginTime="00:00:00.00" Storyboard.TargetName="Group1Circle"
Storyboard.TargetProperty="Opacity" From="0" To="0"
Duration="0:0:00.00" />
-->
<DoubleAnimation BeginTime="00:00:00.00" Storyboard.TargetName="Group1"
Storyboard.TargetProperty="(RotateTransform.Angle)" From="135"
To="180" Duration="0:0:00.25" />
-->
<DoubleAnimation BeginTime="00:00:02.50" Storyboard.TargetName="Group1"
Storyboard.TargetProperty="(RotateTransform.Angle)" From="180"
To="360" Duration="0:0:01.00" />
<DoubleAnimation BeginTime="00:00:02.6666" Storyboard.TargetName="Group1Circle"
Storyboard.TargetProperty="Opacity" From="0" To="0.88"
Duration="0:0:00.333" AutoReverse="True" />
-->
<DoubleAnimation BeginTime="00:00:05.50" Storyboard.TargetName="Group1"
Storyboard.TargetProperty="(RotateTransform.Angle)" From="0"
To="135" Duration="0:0:00.75" />
<DoubleAnimation BeginTime="00:00:05.6665" Storyboard.TargetName="Group1Circle"
Storyboard.TargetProperty="Opacity" From="0" To="0.88"
Duration="0:0:00.333" AutoReverse="True" />
-->
<ColorAnimation BeginTime="00:00:00.00" Storyboard.TargetName="Point1"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
From="Red" Duration="0:0:00.25">
<ColorAnimation.To>
<Color A="255" R="251" G="190" B="3" />
</ColorAnimation.To>
</ColorAnimation>
<ColorAnimation BeginTime="00:00:02.50" Storyboard.TargetName="Point1"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
To="Red" Duration="0:0:01">
<ColorAnimation.From>
<Color A="255" R="251" G="190" B="3" />
</ColorAnimation.From>
</ColorAnimation>
<ColorAnimation BeginTime="00:00:00.00" Storyboard.TargetName="Point4"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
To="Red" Duration="0:0:00.25">
<ColorAnimation.From>
<Color A="255" R="251" G="190" B="3" />
</ColorAnimation.From>
</ColorAnimation>
<ColorAnimation BeginTime="00:00:02.50" Storyboard.TargetName="Point4"
Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)"
From="Red" Duration="0:0:01">
<ColorAnimation.To>
<Color A="255" R="251" G="190" B="3" />
</ColorAnimation.To>
</ColorAnimation>
</Storyboard>
So one can see that each animation is linked by their name and each name can have multiple animations. In this case, different animations at different times.
Points of Interest
It is to be noted that the first couple of dots are started through most of their rotations to give a seamless transition from the last animation to the first.
If you want to make this a splash screen, here is a great article that shows how to override the Startup
function in App.xaml.cs.
History