Animation Controls
DrawnUi.Maui provides powerful controls for displaying animations directly on the canvas with high performance. This article covers the animation controls available in the framework.
Animation Basics
All animation controls in DrawnUi.Maui share common functionality through the AnimatedFramesRenderer
base class. This provides consistent playback control and event handling across different animation types.
Common properties and methods include:
Property | Type | Description |
---|---|---|
AutoPlay |
bool | Automatically start animation when loaded |
IsPlaying |
bool | Indicates if animation is currently playing |
Repeat |
int | Number of times to repeat (-1 for infinite looping) |
SpeedRatio |
double | Animation playback speed multiplier |
DefaultFrame |
int | Frame to display when not playing |
Common methods:
Start()
- Begin or resume the animationStop()
- Pause the animationSeek(frame)
- Jump to a specific frame
Common events:
Started
- Fires when animation beginsFinished
- Fires when animation completes
SkiaGif
SkiaGif is a control for displaying animated GIF images with precise frame timing and control.
Basic Usage
<DrawUi:SkiaGif
Source="animated.gif"
WidthRequest="200"
HeightRequest="200"
AutoPlay="True"
Repeat="-1" />
Key Properties
Property | Type | Description |
---|---|---|
Source |
string | Path or URL to the GIF file |
Animation |
GifAnimation | Internal animation data (automatically created) |
Loading Sources
SkiaGif can load animations from various sources:
// From app resources
myGif.Source = "embedded_resource.gif";
// From file system
myGif.Source = "file:///path/to/animation.gif";
// From URL
myGif.Source = "https://example.com/animation.gif";
Controlling Playback
// Start the animation
myGif.Start();
// Stop at current frame
myGif.Stop();
// Jump to specific frame
myGif.Seek(5);
// Get total frames
int total = myGif.Animation?.TotalFrames ?? 0;
SkiaLottie
SkiaLottie is a control for displaying Lottie animations, which are vector-based animations exported from Adobe After Effects. It provides smooth, resolution-independent animations with additional customization options.
Basic Usage
<draw:SkiaLottie
Source="animation.json"
WidthRequest="200"
HeightRequest="200"
AutoPlay="True"
Repeat="-1" />
Real-World Example: Robot Animation
From the demo project, here's a practical implementation showing a robot animation with proper styling:
<draw:SkiaLottie
AutoPlay="True"
HorizontalOptions="Center"
LockRatio="1"
Repeat="-1"
Source="Lottie/robot.json"
VerticalOptions="Start"
WidthRequest="300" />
Key Features:
LockRatio="1"
maintains aspect ratioRepeat="-1"
loops indefinitely- Loads from
Resources/Raw/Lottie/robot.json
Toggle State Support
SkiaLottie includes special support for toggle/switch animations with the IsOn
property:
<DrawUi:SkiaLottie
Source="toggle_animation.json"
IsOn="{Binding IsToggled}"
DefaultFrame="0"
DefaultFrameWhenOn="30"
SpeedRatio="1.5"
AutoPlay="True"
Repeat="0" />
This is perfect for animated toggles, checkboxes, or any other animation with distinct on/off states.
Key Properties
Property | Type | Description |
---|---|---|
Source |
string | Path or URL to the Lottie JSON file |
ColorTint |
Color | Color tint applied to the entire animation |
Colors |
IList |
Collection of replacement colors |
IsOn |
bool | Toggle state property |
DefaultFrameWhenOn |
int | Frame to display when IsOn = true |
ApplyIsOnWhenNotPlaying |
bool | Whether to apply IsOn state when not playing |
Customizing Colors
One of the powerful features of SkiaLottie is color customization without needing external editors:
Single Color Tint
<!-- Apply a global tint with theme color -->
<draw:SkiaLottie
InputTransparent="True"
AutoPlay="True"
ColorTint="{StaticResource ColorPrimary}"
HorizontalOptions="Center"
LockRatio="1"
Opacity="0.85"
Repeat="-1"
Source="Lottie/Loader.json"
VerticalOptions="Center"
WidthRequest="56" />
Multiple Color Replacement
For more granular control, you can replace multiple specific colors:
<draw:SkiaLottie
AutoPlay="True"
HorizontalOptions="Center"
WidthRequest="50"
LockRatio="1"
Repeat="-1"
Source="Lottie/Loader.json"
VerticalOptions="Center">
<draw:SkiaLottie.Colors>
<Color>#FF0000</Color>
<Color>#FFFFFF</Color>
</draw:SkiaLottie.Colors>
</draw:SkiaLottie>
Programmatic Color Changes
// Replace specific colors in the animation
myLottie.Colors.Add(Colors.Blue);
myLottie.Colors.Add(Colors.Green);
// Apply changes
myLottie.ReloadSource();
Color Replacement Logic:
- Colors are replaced in the order they appear in the original animation
- First color in
Colors
collection replaces the first unique color found ColorTint
applies a global tint over the entire animation- Use
Colors
for precise color replacement,ColorTint
for theme integration
SkiaSprite
SkiaSprite is a high-performance control for displaying and animating sprite sheets. It loads sprite sheets (a single image containing multiple animation frames arranged in a grid) and renders individual frames with precise timing for smooth animations.
Basic Usage
<DrawUi:SkiaSprite
Source="sprites/explosion.png"
Columns="8"
Rows="4"
FramesPerSecond="24"
AutoPlay="True"
Repeat="-1"
WidthRequest="128"
HeightRequest="128" />
Key Properties
Property | Type | Description |
---|---|---|
Source |
string | Path or URL of the sprite sheet image |
Columns |
int | Number of columns in the sprite sheet grid |
Rows |
int | Number of rows in the sprite sheet grid |
FramesPerSecond |
int | Animation speed in frames per second (default: 24) |
MaxFrames |
int | Maximum number of frames to use (0 means use all) |
CurrentFrame |
int | Current frame being displayed (0-based index) |
FrameSequence |
int[] | Custom sequence of frames to play |
AnimationName |
string | Name of a predefined animation sequence |
Sprite Sheet Structure
A sprite sheet is a single image containing multiple frames arranged in a grid:
+---+---+---+---+
| 0 | 1 | 2 | 3 |
+---+---+---+---+
| 4 | 5 | 6 | 7 |
+---+---+---+---+
| 8 | 9 | 10| 11|
+---+---+---+---+
The Columns
and Rows
properties define the grid structure:
- In the example above, set
Columns="4"
andRows="3"
- Frames are numbered left-to-right, top-to-bottom (0 to 11)
- Each frame must have the same dimensions
Frame Sequences and Reusing Spritesheets
One of the key features of SkiaSprite is the ability to create multiple animations from a single spritesheet by defining frame sequences:
// Register named animations for a character spritesheet
SkiaSprite.CreateAnimationSequence("Idle", new[] { 0, 1, 2, 1 });
SkiaSprite.CreateAnimationSequence("Walk", new[] { 3, 4, 5, 6, 7, 8 });
SkiaSprite.CreateAnimationSequence("Jump", new[] { 9, 10, 11 });
Then in XAML just reference by name:
<!-- Multiple sprites sharing the same spritesheet with different animations -->
<DrawUi:SkiaSprite Source="character.png" AnimationName="Walk" />
<DrawUi:SkiaSprite Source="character.png" AnimationName="Jump" />
Or use a direct frame sequence:
// Define a specific frame sequence
mySprite.FrameSequence = new[] { 3, 4, 5, 4, 3 }; // Play frames in this exact order
Spritesheet Caching
SkiaSprite includes an intelligent caching system to avoid reloading the same spritesheets multiple times:
// Clear the entire spritesheet cache
SkiaSprite.ClearCache();
// Remove a specific spritesheet from cache
SkiaSprite.RemoveFromCache("character.png");
Performance Considerations
SkiaGif
- GIFs can consume significant memory, especially large ones
- For large animations, verify memory usage
SkiaLottie
- Vector-based animations are more memory efficient than GIFs
- Complex Lottie animations may be CPU-intensive
- Use
ColorTint
for simple color changes rather than individual color replacements when possible
SkiaSprite
- Spritesheets are cached automatically to avoid redundant loading
- For large or numerous sprite sheets, consider monitoring memory usage
- Use
ClearCache()
orRemoveFromCache()
when spritesheets are no longer needed - For complex animations, use frame sequences to avoid redundant frames
Advanced Examples
Code-Behind Lottie Implementation
For complex scenarios, you can create and control SkiaLottie entirely in code:
public partial class AnimationPage : ContentPage
{
private SkiaLottie crashAnimation;
public AnimationPage()
{
InitializeComponent();
CreateLottieAnimation();
}
private void CreateLottieAnimation()
{
crashAnimation = new SkiaLottie()
{
AutoPlay = false,
WidthRequest = 110,
LockRatio = 1,
SpeedRatio = 0.6f,
Repeat = 0,
DefaultFrame = -1, // -1 means "last frame" when not playing
UseCache = SkiaCacheType.ImageDoubleBuffered,
Source = "Space/Lottie/crash.json"
};
// Add to your layout
MainLayout.Children.Add(crashAnimation);
}
// Control animation programmatically
private void PlayCrashAnimation()
{
crashAnimation.Play();
}
private void StopAndResetAnimation()
{
crashAnimation.Stop();
// Will automatically seek to DefaultFrame (-1 = last frame)
}
}
Advanced Properties Explained:
DefaultFrame = -1
- Shows last frame when stopped (useful for "completed" states)SpeedRatio = 0.6f
- Plays at 60% of original speedUseCache = ImageDoubleBuffered
- Optimizes for smooth playbackRepeat = 0
- Plays once (use -1 for infinite loop)
Loading Animation with Theme Integration
<draw:SkiaLottie
Source="loading_spinner.json"
WidthRequest="48"
HeightRequest="48"
AutoPlay="True"
Repeat="-1"
ColorTint="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}"
Opacity="0.8" />
Animated Button with Sprite Sheet
<DrawUi:SkiaButton
WidthRequest="200"
HeightRequest="60"
BackgroundColor="Transparent">
<DrawUi:SkiaSprite
x:Name="buttonAnimation"
Source="button_animation.png"
Columns="5"
Rows="1"
FramesPerSecond="30"
AutoPlay="False"
DefaultFrame="0" />
<DrawUi:SkiaLabel
Text="Animated Button"
TextColor="White"
FontSize="16"
HorizontalOptions="Center"
VerticalOptions="Center" />
</DrawUi:SkiaButton>
In code-behind:
MyButton.Pressed += (s, e) => {
buttonAnimation.Stop();
buttonAnimation.CurrentFrame = 0;
buttonAnimation.Start();
};
Game Character Animation
<DrawUi:SkiaLayout
WidthRequest="200"
HeightRequest="200">
<DrawUi:SkiaSprite
x:Name="CharacterAnimation"
Source="character_sprites.png"
Columns="8"
Rows="4"
FramesPerSecond="12"
AnimationName="Walk"
AutoPlay="True"
WidthRequest="128"
HeightRequest="128"
HorizontalOptions="Center"
VerticalOptions="Center" />
</DrawUi:SkiaLayout>
In code-behind:
// Setup animation sequences
void InitializeAnimations()
{
SkiaSprite.CreateAnimationSequence("Idle", new[] { 0, 1, 2, 1 });
SkiaSprite.CreateAnimationSequence("Walk", new[] { 8, 9, 10, 11, 12, 13, 14, 15 });
SkiaSprite.CreateAnimationSequence("Jump", new[] { 16, 17, 18, 19, 20, 21 });
SkiaSprite.CreateAnimationSequence("Attack", new[] { 24, 25, 26, 27, 28, 29, 30 });
}
// Change animation based on game state
void UpdateCharacterState(PlayerState state)
{
CharacterAnimation.Stop();
switch (state)
{
case PlayerState.Idle:
CharacterAnimation.AnimationName = "Idle";
break;
case PlayerState.Walking:
CharacterAnimation.AnimationName = "Walk";
break;
case PlayerState.Jumping:
CharacterAnimation.AnimationName = "Jump";
break;
case PlayerState.Attacking:
CharacterAnimation.AnimationName = "Attack";
break;
}
CharacterAnimation.Start();
}
GIF Avatar
<draw:SkiaShape
Type="Circle"
WidthRequest="100"
LockRatio="1">
<draw:SkiaGif
Source="avatar.gif"
WidthRequest="100"
LockRatio="1"
AutoPlay="True"
Repeat="-1" />
</draw:SkiaShape>