Wpf Design And Animation Lab
This is a WPF project for creating and collecting fun designs and animations. It currently includes dozens of demos, some of which have corresponding blog posts detailing implementation steps and principles:
Design and Animation - dino.c - Blog Garden
Through these blog posts, you will learn how to implement some cool WPF animations and designs, as well as various WPF technical details.
1. Implemented Designs and Animations
1.1 Three Ways to Implement an Arc Progress Bar

There are many ways to implement an arc progress bar. By using the three approaches – Path with ArcSegment, Arc, and Ellipse – you can understand the basic usage of various Shape types.
1.2 Implementing a Cylindrical Progress Bar Using Only Rectangle

A cylindrical progress bar is not difficult to implement, but it's interesting that the image above is entirely composed of Rectangle elements, which is a bit counterintuitive.
First, we need to revisit some basics: Rectangle displays rectangles with rounded corners. RadiusX and RadiusY specify the X-axis and Y-axis radii of the ellipse used to round the rectangle's corners.
In the following example, you can see that the rounded corners of a Rectangle with RadiusX="50" RadiusY="20" exactly overlap an Ellipse with Width="100" Height="40" (X-axis radius 50, Y-axis radius 20).
<Rectangle Height="100"
Width="100"
Fill="#FF7E9EC0"
Stroke="#FFFF0EC4"
StrokeThickness="5"
RadiusX="50"
RadiusY="20" />
<Ellipse HorizontalAlignment="Left"
VerticalAlignment="Top"
StrokeThickness="5"
Stroke="Yellow"
Fill="Red"
Width="100"
Height="40"
Opacity="0.5" />

Now if we stretch the Rectangle above, it becomes the basic shape of a cylinder; conversely, squashing it gives the cross-section of the cylinder. Making them semi-transparent yields the background of a cylindrical progress bar:

<Grid.Resources>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="#36a8e2" />
<Setter Property="RadiusX" Value="25" />
<Setter Property="RadiusY" Value="5" />
</Style>
</Grid.Resources>
<Rectangle Opacity="0.2" />
<Rectangle Height="10"
VerticalAlignment="Top"
Opacity="0.1" />
Then add a semi‑transparent gradient layer and another cross-section to complete the cylindrical progress bar.
1.3 Playing with Rainbow Text and Animation
Splitting text with ItemsControl to achieve rainbow text is a fun approach because you can apply different transformations and animations to each character, enabling many variations. First, since a string is a collection, it can actually be used as the ItemsSource of an ItemsControl. However, writing ItemsSource="somestring" directly in Xaml causes an error. You can wrap it with a ContentControl like this:
<ContentControl Content="ItemsControl" >
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<ItemsControl ItemsSource="{TemplateBinding Content}" >
</ItemsControl>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
Then set the ItemsPanel of the ItemsControl to arrange the content horizontally, and set the DataTemplate to display each character in a TextBlock:
<ItemsControl ItemsSource="{TemplateBinding Content}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Next, to give each character a different color, implement a collection class and instantiate it in Xaml, placing the colors to use:
<common:RepeatCollection x:Key="RepeatCollection">
<SolidColorBrush>#4a0e68</SolidColorBrush>
<SolidColorBrush>#b62223</SolidColorBrush>
<SolidColorBrush>#fdd70c</SolidColorBrush>
<SolidColorBrush>#f16704</SolidColorBrush>
<SolidColorBrush>#69982d</SolidColorBrush>
<SolidColorBrush>#0075a5</SolidColorBrush>
<SolidColorBrush>#0b0045</SolidColorBrush>
</common:RepeatCollection>
The code for this RepeatCollection is as follows. It's essentially a circular queue; each call to the Next getter retrieves the next element (maybe CircleCollection would be a better name?):
public class RepeatCollection : Collection<object>
{
private int _offset;
public object Next
{
get
{
if (this.Count == 0)
return null;
var result = this[_offset];
_offset++;
if (_offset > this.Count - 1)
_offset = 0;
return result;
}
}
}
Finally, bind the TextBlock's Foreground to the Next property of the collection, so each TextBlock uses a different color:
<TextBlock Foreground="{Binding Next, Source={StaticResource RepeatCollection}}" Text="{Binding}" />

By modifying the code above, we can animate the rainbow text:

1.4 Making a Rainbow Button
Applying a LinearGradientBrush to text turns it rainbow-colored. If two GradientStops have the same Color, no gradient occurs; if their Offset values are set to transition instantly, the color jumps. Using this technique, combined with a monospaced font, we can create rainbow text where each character has a different color:
<LinearGradientBrush x:Name="RainbowBrush" StartPoint="0,0.5" EndPoint="1,.5">
<GradientStop x:Name="G1" Offset="0" Color="#65b849" />
<GradientStop x:Name="G2" Offset=".166" Color="#65b849" />
<GradientStop x:Name="G3" Offset=".166" Color="#f7b423" />
<GradientStop x:Name="G4" Offset=".3333" Color="#f7b423" />
<GradientStop x:Name="G5" Offset="0.3333" Color="#f58122" />
<GradientStop x:Name="G6" Offset="0.5" Color="#f58122" />
<GradientStop x:Name="G7" Offset="0.5" Color=" #f8f8f8" />
<GradientStop x:Name="G8" Offset="0.5" Color=" #f8f8f8" />
<GradientStop x:Name="G9" Offset="0.50" Color="#de3a3c" />
<GradientStop x:Name="G10" Offset="0.666" Color="#de3a3c" />
<GradientStop x:Name="G11" Offset="0.666" Color="#943f96" />
<GradientStop x:Name="G12" Offset="0.8633" Color="#943f96" />
<GradientStop x:Name="G13" Offset="0.8633" Color="#009fd9" />
<GradientStop x:Name="G14" Offset="01" Color="#009fd9" />
</LinearGradientBrush>

In the MouseOver Storyboard, we control the LinearGradientBrush to change its direction. There are two ways to do this: using PointAnimation to change StartPoint and EndPoint, or using DoubleAnimation to directly change the LinearGradientBrush.RelativeTransform. The latter is written as:
<Storyboard>
<DoubleAnimation Storyboard.TargetName="textBlock"
Storyboard.TargetProperty="(TextBlock.Foreground).(Brush.RelativeTransform).(RotateTransform.Angle)"
To="90"
Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<QuarticEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<LinearGradientBrush x:Name="RainbowBrush" StartPoint="0,0.5" EndPoint="1,.5">
<LinearGradientBrush.RelativeTransform>
<RotateTransform Angle="0" CenterX="0.5" CenterY="0.5" />
</LinearGradientBrush.RelativeTransform>
When run, the effect rotates all colors by 90 degrees, resembling the old Apple logo color scheme. In the LinearGradientBrush above, I hid two white GradientStops (named G6 and G7), both with Offset 0.5, placed right in the middle. In the button's Pressed state, using DoubleAnimation, we set the Offset of all GradientStops before and after them to 0 or 1, pushing all colors to the sides. Since it's now rotated 90 degrees, they are pushed up and down:

1.5 Two Loading Animations from Nintendo Switch


Using character splitting and a TimeSpanIncreaser pattern, we implemented two of the most common animations seen on the Nintendo Switch.
1.6 Writing a Lighten Effect Using Shazzam Shader Editor
In the animation above, to achieve different brightness levels for Grids, we used a LightenConverter class, but it only works with SolidColorBrush. To make it more versatile, we'll write our own Effect to achieve the same Lighten effect.

1.7 Implementing Inner Shadow in WPF
In WPF, we usually use DropShadow for shadow effects, but those are outer shadows. Inner shadows can be achieved, albeit with some workarounds. There are several approaches; my favorite is using another element's VisualBrush as an OpacityMask.
<Grid Width="100"
Height="100"
Margin="10">
<Rectangle x:Name="Rectangle2"
Fill="White"
RadiusX="8"
RadiusY="8" />
<Border Margin="0">
<Border.Effect>
<DropShadowEffect BlurRadius="8" ShadowDepth="0" />
</Border.Effect>
<ContentControl HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="OpacityMask" />
</Border>
<Grid.OpacityMask>
<VisualBrush Stretch="None" Visual="{Binding ElementName=Rectangle2}" />
</Grid.OpacityMask>
</Grid>
However, the resulting shadow is not very thick. For larger, thicker inner shadows, you can use a negative Margin combined with an equally thick BorderThickness. Using the OpacityMask approach, the following code creates a thick and large inner shadow:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
ShadowElement.Margin = new Thickness(-e.NewValue);
ShadowElement.BorderThickness = new Thickness(e.NewValue);
(ShadowElement.Effect as DropShadowEffect).BlurRadius = e.NewValue * 2;
}

1.8 Mimicking UWP's Text Shimmer Animation with OpacityMask

In the UWP Windows Composition Samples, there is a Text Shimmer animation demonstrating how to use Composition Light. The animation is simple: a PointLight sweeps across a line of text from left to right. Although WPF doesn't have Composition Light, we can still simulate this simple animation using OpacityMask.
RadialGradientBrush represents a circular gradient brush. Here we care about three properties:
RadiusX/RadiusY: The horizontal/vertical radius of the circle. Center: The center of the outermost circle. GradientOrigin: The location of the two‑dimensional focal point where the gradient begins.
The role of these properties is illustrated below:

Using a RadialGradientBrush as the OpacityMask causes the TextBlock to gradually become transparent from the center outward:
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="SegoeUI"
FontSize="100"
FontWeight="Thin"
Foreground="DimGray"
Text="Text Shimmer">
<TextBlock.OpacityMask>
<RadialGradientBrush x:Name="Brush" Center=".5,.5" GradientOrigin=".5,.5" RadiusX=".43" RadiusY="2">
<GradientStop Color="Black" />
<GradientStop Offset=".5" Color="#6000" />
<GradientStop Offset="1" Color="#2000" />
</RadialGradientBrush>
</TextBlock.OpacityMask>
</TextBlock>
Then animate Center and GradientOrigin with PointAnimation to move the OpacityMask horizontally, simulating the sweep of a PointLight:
<PointAnimation RepeatBehavior="Forever"
Storyboard.TargetName="Brush"
Storyboard.TargetProperty="Center"
From="-2,.5"
To="3,.5"
Duration="0:0:3.3" />
<PointAnimation RepeatBehavior="Forever"
Storyboard.TargetName="Brush"
Storyboard.TargetProperty="GradientOrigin"
From="-2,.5"
To="3,.5"
Duration="0:0:3.3" />
1.9 Recreating a CSS3 Button

Recreate a button implemented with CSS3, while getting familiar with CSS3 in the process.
1.10 Line Light and Shadow Effect Using an Effect

To achieve this effect, I used the following knowledge and techniques:
- Segoe Fluent icon font
- Creating a Path in Blend
- Calculating the length of a Path
- Path border animation
- Design-time data support in Visual Studio
- Custom Effect
2. License
The project is released under MIT License.
3. UWP Version
Additionally, I have another project for playing with UWP animations:
https://github.com/DinoChan/uwp_design_and_animation_lab

Reprinted from GitHub
Author: dino.c
Repository: https://github.com/DinoChan/wpf_design_and_animation_lab