doc/articles/guides/xf-migration/animations.md
This guide explores how to migrate animations from Xamarin.Forms to Uno Platform. While both frameworks support rich animation capabilities, they use different APIs and approaches. This article will help you understand the differences and successfully migrate your animations.
Xamarin.Forms provides a simple, code-based animation API through extension methods on the View class:
RotateToScaleTo, ScaleXTo, ScaleYToTranslateToFadeToThese methods are called directly on view instances and allow you to animate properties over time with optional easing functions.
Uno Platform, following the WinUI model, uses XAML-based animations through the Storyboard class. Animations are typically defined in XAML and controlled from code. This approach provides more flexibility and better separation of animation definitions from business logic.
Transforms allow you to rotate, scale, translate, and skew visual elements without affecting their layout position.
Xamarin.Forms:
await myElement.RotateTo(360, 1000);
Uno Platform:
<Storyboard x:Name="RotateStoryboard">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
To="360"
Duration="0:0:1"/>
</Storyboard>
To use transforms in Uno Platform, you must first apply the transform to the element:
<Border x:Name="myElement">
<Border.RenderTransform>
<RotateTransform/>
</Border.RenderTransform>
</Border>
Xamarin.Forms:
await myElement.ScaleTo(2.0, 1000);
await myElement.ScaleXTo(2.0, 1000);
await myElement.ScaleYTo(2.0, 1000);
Uno Platform:
<Storyboard x:Name="ScaleStoryboard">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
To="2.0"
Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
To="2.0"
Duration="0:0:1"/>
</Storyboard>
With the transform applied:
<Border x:Name="myElement">
<Border.RenderTransform>
<ScaleTransform/>
</Border.RenderTransform>
</Border>
Xamarin.Forms:
await myElement.TranslateTo(100, 100, 1000);
Uno Platform:
<Storyboard x:Name="TranslateStoryboard">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
To="100"
Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
To="100"
Duration="0:0:1"/>
</Storyboard>
With the transform applied:
<Border x:Name="myElement">
<Border.RenderTransform>
<TranslateTransform/>
</Border.RenderTransform>
</Border>
Xamarin.Forms:
await myElement.FadeTo(0.5, 1000);
Uno Platform:
<Storyboard x:Name="FadeStoryboard">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0:0:1"/>
</Storyboard>
| Xamarin.Forms Method | WinUI/Uno Storyboard Target |
|---|---|
RotateTo | (UIElement.RenderTransform).(RotateTransform.Angle) |
ScaleTo | (UIElement.RenderTransform).(ScaleTransform.ScaleX) and (UIElement.RenderTransform).(ScaleTransform.ScaleY) |
ScaleXTo | (UIElement.RenderTransform).(ScaleTransform.ScaleX) |
ScaleYTo | (UIElement.RenderTransform).(ScaleTransform.ScaleY) |
TranslateTo | (UIElement.RenderTransform).(TranslateTransform.X) and (UIElement.RenderTransform).(TranslateTransform.Y) |
FadeTo | Opacity |
If you need to apply multiple transforms to a single element (e.g., rotate and scale), use CompositeTransform instead of individual transforms:
<Border x:Name="myElement">
<Border.RenderTransform>
<CompositeTransform/>
</Border.RenderTransform>
</Border>
<Storyboard x:Name="CompositeStoryboard">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
To="360"
Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
To="2.0"
Duration="0:0:1"/>
</Storyboard>
Easing functions change how animations progress over time, enabling elements to speed up and slow down for more natural movements.
Xamarin.Forms uses the Easing class with built-in easing functions:
await myElement.ScaleTo(2.0, 1000, Easing.BounceOut);
WinUI uses EasingFunctionBase-derived classes:
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
To="2.0"
Duration="0:0:1">
<DoubleAnimation.EasingFunction>
<BounceEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
| Xamarin.Forms | WinUI/Uno Platform |
|---|---|
Bounce (BounceIn/BounceOut) | BounceEase |
Cubic (CubicIn/CubicInOut/CubicOut) | CubicEase |
Linear | (specify no EasingFunction) |
Sin (SinIn/SinInOut/SinOut) | SineEase |
Spring (SpringIn/SpringOut) | ElasticEase |
Easing functions in WinUI have an EasingMode property that controls the direction of the easing:
EaseIn: Acceleration at the startEaseOut: Deceleration at the endEaseInOut: Acceleration at start and deceleration at end<BounceEase EasingMode="EaseOut"/>
<CubicEase EasingMode="EaseInOut"/>
<SineEase EasingMode="EaseIn"/>
WinUI provides additional easing functions not available in Xamarin.Forms:
Example using PowerEase:
<PowerEase Power="3" EasingMode="EaseInOut"/>
Example using BackEase:
<BackEase Amplitude="0.5" EasingMode="EaseOut"/>
Xamarin.Forms supports custom easing functions where you define your own progression from 0.0 to 1.0:
var customEasing = new Easing(t => Math.Sin(t * Math.PI * 2));
await myElement.ScaleTo(2.0, 1000, customEasing);
WinUI doesn't currently support custom easing functions directly. You can work around this limitation by:
Keyframe animations allow you to specify exact values at specific points in time:
<Storyboard x:Name="KeyframeStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<LinearDoubleKeyFrame KeyTime="0:0:0" Value="1.0"/>
<LinearDoubleKeyFrame KeyTime="0:0:0.25" Value="1.5"/>
<LinearDoubleKeyFrame KeyTime="0:0:0.5" Value="0.8"/>
<LinearDoubleKeyFrame KeyTime="0:0:0.75" Value="1.8"/>
<LinearDoubleKeyFrame KeyTime="0:0:1" Value="2.0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
Types of keyframes:
Xamarin.Forms:
await myElement.RotateTo(360, 1000);
Uno Platform:
RotateStoryboard.Begin();
Uno Platform:
RotateStoryboard.Stop();
Uno Platform:
RotateStoryboard.Pause();
RotateStoryboard.Resume();
Xamarin.Forms:
await myElement.RotateTo(360, 1000);
// Code here runs after animation completes
Uno Platform:
RotateStoryboard.Completed += (s, e) =>
{
// Code here runs after animation completes
};
RotateStoryboard.Begin();
Uno Platform:
<Storyboard x:Name="RepeatStoryboard" RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="myElement"
Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:2"/>
</Storyboard>
Uno Platform:
<Storyboard x:Name="RepeatStoryboard" RepeatBehavior="3x">
<!-- Animation repeats 3 times -->
</Storyboard>
Uno Platform:
<Storyboard x:Name="ReverseStoryboard" AutoReverse="True">
<!-- Animation plays forward, then backward -->
</Storyboard>
When migrating animations from Xamarin.Forms to Uno Platform:
RotateTo, ScaleTo, TranslateTo, FadeTo, etc.)Storyboard.Begin() callsCompleted event instead of awaitWhile Xamarin.Forms uses code-based animations and Uno Platform uses XAML-based Storyboards, the concepts are similar:
The main difference is the declarative nature of Uno Platform animations, which provides better separation between animation definitions and business logic.