From wpf-dev-pack
Creates WPF animations using Storyboard, Timeline, and EasingFunction patterns for UI transitions, state change visualizations, and interactive feedback effects.
How this skill is triggered — by the user, by Claude, or both
Slash command
/wpf-dev-pack:creating-wpf-animationssonnetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
WPF animations create visual effects by changing property values over time.
WPF animations create visual effects by changing property values over time.
Storyboard (container)
├── Timeline (time control)
│ ├── Animation (value change)
│ │ ├── DoubleAnimation
│ │ ├── ColorAnimation
│ │ └── ...
│ └── AnimationUsingKeyFrames (keyframes)
│ ├── DoubleAnimationUsingKeyFrames
│ └── ...
└── EasingFunction (acceleration/deceleration)
<Button Content="Hover Me" Width="100">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<!-- Width animation -->
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="150"
Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="100"
Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Border x:Name="AnimatedBorder" Background="Blue" Width="100" Height="100">
<Border.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<!-- Background color animation -->
<ColorAnimation
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="Red"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="Blue"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
<Border x:Name="SlidingBorder" Margin="0,0,0,0">
<Border.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<!-- Slide-in effect -->
<ThicknessAnimation
Storyboard.TargetProperty="Margin"
From="-100,0,0,0"
To="0,0,0,0"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width" To="200" Duration="0:0:0.5">
<DoubleAnimation.EasingFunction>
<!-- Various easing functions -->
<!-- Smooth deceleration -->
<QuadraticEase EasingMode="EaseOut"/>
<!-- Elastic effect -->
<!--<ElasticEase Oscillations="3" Springiness="5"/>-->
<!-- Bounce effect -->
<!--<BounceEase Bounces="3" Bounciness="2"/>-->
<!-- Back effect (overshoot) -->
<!--<BackEase Amplitude="0.3"/>-->
<!-- Circular easing -->
<!--<CircleEase EasingMode="EaseInOut"/>-->
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
| Mode | Description |
|---|---|
| EaseIn | Slow at start, fast at end |
| EaseOut | Fast at start, slow at end |
| EaseInOut | Slow at both ends, fast in middle |
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<!-- Sequential keyframes -->
<LinearDoubleKeyFrame Value="0.3" KeyTime="0:0:0.2"/>
<LinearDoubleKeyFrame Value="1.0" KeyTime="0:0:0.4"/>
<SplineDoubleKeyFrame Value="0.5" KeyTime="0:0:0.8">
<SplineDoubleKeyFrame.KeySpline>
<KeySpline ControlPoint1="0.5,0" ControlPoint2="0.5,1"/>
</SplineDoubleKeyFrame.KeySpline>
</SplineDoubleKeyFrame>
<EasingDoubleKeyFrame Value="1.0" KeyTime="0:0:1">
<EasingDoubleKeyFrame.EasingFunction>
<BounceEase/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard>
<!-- Visibility toggle (non-continuous values) -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:1" Value="{x:Static Visibility.Hidden}"/>
<DiscreteObjectKeyFrame KeyTime="0:0:2" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Button Content="Rotate" RenderTransformOrigin="0.5,0.5">
<Button.RenderTransform>
<RotateTransform x:Name="RotateTransform" Angle="0"/>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Click">
<BeginStoryboard>
<Storyboard>
<!-- Rotation animation -->
<DoubleAnimation
Storyboard.TargetName="RotateTransform"
Storyboard.TargetProperty="Angle"
By="360"
Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Border Width="100" Height="100" Background="Blue" RenderTransformOrigin="0.5,0.5">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="Scale" ScaleX="1" ScaleY="1"/>
<RotateTransform x:Name="Rotate" Angle="0"/>
<TranslateTransform x:Name="Translate" X="0" Y="0"/>
</TransformGroup>
</Border.RenderTransform>
<Border.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<!-- Simultaneous animations -->
<DoubleAnimation Storyboard.TargetName="Scale"
Storyboard.TargetProperty="ScaleX"
To="1.2" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetName="Scale"
Storyboard.TargetProperty="ScaleY"
To="1.2" Duration="0:0:0.2"/>
<DoubleAnimation Storyboard.TargetName="Rotate"
Storyboard.TargetProperty="Angle"
To="10" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
namespace MyApp.Animations;
using System;
using System.Windows;
using System.Windows.Media.Animation;
public static class AnimationHelper
{
/// <summary>
/// Opacity fade animation
/// </summary>
public static void FadeIn(UIElement element, double durationSeconds = 0.3)
{
var animation = new DoubleAnimation
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(durationSeconds),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
};
element.BeginAnimation(UIElement.OpacityProperty, animation);
}
public static void FadeOut(UIElement element, double durationSeconds = 0.3, Action? onCompleted = null)
{
var animation = new DoubleAnimation
{
From = 1,
To = 0,
Duration = TimeSpan.FromSeconds(durationSeconds),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
if (onCompleted is not null)
{
animation.Completed += (s, e) => onCompleted();
}
element.BeginAnimation(UIElement.OpacityProperty, animation);
}
}
Advanced: See ADVANCED.md for composite Storyboard animation, AnimationController (stop/resume), and VisualStateManager integration.
| Property | Performance | Description |
|---|---|---|
| Opacity | ⭐⭐⭐ | Most efficient |
| RenderTransform | ⭐⭐⭐ | No layout recalculation |
| Clip | ⭐⭐ | Medium performance |
| Width/Height | ⭐ | Causes layout recalculation |
| Margin | ⭐ | Causes layout recalculation |
// Performance optimization hints
RenderOptions.SetBitmapScalingMode(element, BitmapScalingMode.LowQuality);
Timeline.SetDesiredFrameRate(storyboard, 30); // Default 60fps → 30fps
npx claudepluginhub christian289/dotnet-with-claudecode --plugin wpf-dev-packAnimates .NET MAUI views using fade, scale, rotation, translation, easing functions, and chaining. Addresses common pitfalls like uncancelled animations, accessibility with reduced motion, and performance tips.
Implements WPF 2D graphics with Shape, Geometry, Brush, and Pen classes for vector UIs, icons, charts, and diagrams in WPF apps.
Provides reusable animation patterns for UI components—buttons, modals, lists, page transitions, scroll reveals—using motion-foundations tokens. Best when adding entrance/exit animations or scroll-linked effects.