Building Custom Animations in Xamarin.Forms

Avatar

David

Xamarin.Forms includes its own animation infrastructure that allows for easy creation of simple animations, while also being versatile enough to create complex animations. Previously, we explored creating and cancelling animations using the ViewExtensions class, and saw it in action in an app.

In this blog post, I’ll explore using the Animation class to create and cancel animations, synchronize multiple animations, and create custom animations that animate properties that aren’t animated by the existing animation methods. The Animation class is the building block of all Xamarin.Forms animations, with the animation methods in the ViewExtensions class, creating one or more Animation objects.

Introduction to the Animations Class

A number of parameters must be specified when creating an Animation object, including start and end values of the property being animated, and a callback that changes the value of the property. The following code example demonstrates creating an Animation object:

var animation = new Animation (v => image.Scale = v, 1, 2);

This code defines an animation of the Scale property of an Image instance from a value of 1 to a value of 2. The animated value, which is derived by Xamarin.Forms, is passed to the callback specified as the first argument, where it’s used to change the value of the Scale property.

Running an animation created with the Animation class is achieved by calling the Commit method, as demonstrated in the following code example:

animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);

This method specifies the duration of the animation, and amongst other items, a callback that controls whether to repeat the animation. Note that the Commit method does not return a Task object. Instead, notifications are provided through callback methods.

The overall effect is to create an animation that increases the Scale property of an Image from 1 to 2, over 2 seconds (2000 milliseconds), using the Linear easing function. Each time the animation completes, its Scale property is reset to 1 and the animation repeats.

Cancelling an Animation

An application can cancel an animation with a call to the AbortAnimation extension method, as demonstrated in the following code example:

this.AbortAnimation ("SimpleAnimation");

Note that animations are uniquely identified by a combination of the animation owner and the animation name. Therefore, the owner and name specified when running the animation must be used to cancel the animation. Thehe code example will then immediately cancel the animation named SimpleAnimation that’s owned by the page.

Synchronizing Multiple Animations

The Animation class also supports child animations, which involves creating an Animation object to which other Animation objects are added. This enables a series of animations to be run and synchronized. The following code example from the sample application demonstrates creating and running child animations:

var parentAnimation = new Animation ();
var scaleUpAnimation = new Animation (v => image.Scale = v, 1, 2, Easing.SpringIn);
var rotateAnimation = new Animation (v => image.Rotation = v, 0, 360);
var scaleDownAnimation = new Animation (v => image.Scale = v, 2, 1, Easing.SpringOut);


parentAnimation.Add (0, 0.5, scaleUpAnimation);
parentAnimation.Add (0, 1, rotateAnimation);
parentAnimation.Add (0.5, 1, scaleDownAnimation);

parentAnimation.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));

This code creates a parent Animation object to which additional Animation objects are then added. The first two arguments to the Add method specify when to begin and finish the child animation. The argument values must be between 0 and 1 and represent the relative period within the parent animation that the specified child animation will be active. In this example the scaleUpAnimation will be active for the first half of the animation, the scaleDownAnimation will be active for the second half of the animation, and the rotateAnimation will be active for the entire duration.

The overall effect is that the animation occurs over 4 seconds (4000 milliseconds). The scaleUpAnimation animates the Scale property from 1 to 2 over 2 seconds. The scaleDownAnimation then animates the Scale property from 2 to 1 over 2 seconds. While both scale animations are occurring, the rotateAnimation animates the Rotation property from 0 to 360 over 4 seconds. Note that the scaling animations also use easing functions.

Creating a Custom Animation

The advantage of the Animation class is that it has access to the callback method that’s executed when the animated value changes. This allows the callback to implement any desired animation. For example, a ColorTo extension method can be created that animates a color from one value to another by using a callback method that passes the interpolated Color value back to the caller. The following code example from the sample application shows the code for the ColorTo method:

public static class ViewExtensions
{
  public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
  {
    Func<double, Color> transform = (t) =>
      Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R),
                     fromColor.G + t * (toColor.G - fromColor.G),
                     fromColor.B + t * (toColor.B - fromColor.B),
                     fromColor.A + t * (toColor.A - fromColor.A));

    return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
  }

  public static void CancelAnimation(this VisualElement self)
  {
    self.AbortAnimation("ColorTo");
  }

  static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
  {
    easing = easing ?? Easing.Linear;
    var taskCompletionSource = new TaskCompletionSource<bool>();

    element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));

    return taskCompletionSource.Task;
  }
}

The Animate method requires a transform argument, which is a callback method. The input to this callback is always a double ranging from 0 to 1. Thus, the ColorTo method defines its own transform Func that accepts a double ranging from 0 to 1 and that returns a Color value corresponding to that value. The Color value is calculated by interpolating the R, G, B, and A values of the two supplied Color arguments. The Color value is then passed to the callback method for application to a particular property.

This approach allows the ColorTo method to animate any Color property, as demonstrated in the following code example from the sample application:

await Task.WhenAll(
  label.ColorTo(Color.Red, Color.Blue, c => label.TextColor = c, 5000),
  label.ColorTo(Color.Blue, Color.Red, c => label.BackgroundColor = c, 5000));

await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
await boxView.ColorTo(Color.Blue, Color.Red, c => boxView.Color = c, 4000);

In this example, the ColorTo method animates the TextColor and BackgroundColor properties of a Label, the BackgroundColor property of a Page, and the Color property of a BoxView.

Wrapping Up

The Animation class is the building block of all Xamarin.Forms animations and can be used to create and cancel animations, synchronize multiple animations, and create custom animations that animate properties that aren’t animated by the existing animation methods. For more information, see Custom Animations.

0 comments

Leave a comment