I know. Every time you try to animate something in a Silverlight scene, but also in WPF, the result is impressive, but also is always slightly unnatural. The problem here is that in the real world the movements is ruled by some natural forces that simply modify the path and speed of moving object. Friction, gravity,  centrifugal force and other phisical rules modify movements in a non linear way. We live in a world filled by this rules and every thing not respecting them appear unnatural.

Simulating a bounce or a gravity deceleration or acceleration in Silverlight 2.0 require to handle complex animations made of a big amount of keyframe, and is very hard to accomplish. All this rules are often easily to write as a mathematical formula so the Silverlight 3.0 team have introduced the Easing functions, a way to let the maths do its work and to let our animation to become very easy and simple.

Using an Easing function is really easy. You have to declare the function in the resources, using one of the built-in functions, and then refer to it from an animation EasingFuncion attribute. Here is an example:

   1: <Canvas.Resources>
   3:     <SineEase x:Key="easeOut" EasingMode="EaseOut" />
   5:     <Storyboard x:Name="animRXOut">
   6:         <DoubleAnimation To="0" Duration="00:00:00.300"
   7:                          Completed="animRXOut_Completed"
   8:                          EasingFunction="{StaticResource easeOut}"
   9:                          Storyboard.TargetName="ball1" Storyboard.TargetProperty="(RenderTransform).(Angle)" />
  10:     </Storyboard>
  12: </Canvas.Resources>

Every easing function let us choice if we need an EaseIn, EaseOut or both EaseInOut. This cause the easing function to be applied to the start of the animation, to the end or to both start and end. There are a couple of built-in functions in silverlight 3.0:

  • BackEase: This moves the animation backwards a little before continuing. It’s a little bit like starting a car on a hill, you roll back a little before you move forward.
  • BounceEase: As we saw in the previous example, this creates a bouncing effect.
  • CircleEase: This accelerates the animation based on a circular function, where the initial acceleration is slower and the latter acceleration is higher.
  • CubicEase: This is similar to the CircleEase, but is based on the cubic formula of time causing a slower acceleration in the beginning and a more rapid one towards the end of the animation.
  • ElasticEase: This is similar to the BounceEase in that it oscillates the value until it comes to a rest.
  • ExponentialEase: Similar to the Circle and Cubic ease in that it is an exponential acceleration from one value to the next.
  • PowerEase: This is an exponential acceleration where the value of the ease is proportional to the power of the time.
  • QuadraticEase: This is very similar to the CubicEase except that in this case the value is based on the square of the time.
  • QuarticEase: Similar to Quadratic and Cubic. This time the value is based on the cube of the time.
  • QuinticEase: Again, similar to Quadratic, Cubic and Quartic. This time the value is based on the time to the power of 5.
  • SineEase: This accelerates the value along a sine wave.

Using Easing functions in practice

Now let you imagine to have to create a simple animation. We have to simulate two iron balls bouncing each on the other. Think at the games were you have two or more balls suspended by a wire.

The animation is composed of 4 segments:

1) the left ball goes to an angle of 40 degree

2) the left ball returns to an angle of 0 degrees

3) the right ball goes to an angle of -40 degrees

4) the right ball returns to an angle of 0 degrees

This animations are concatenated each other using the completed event. Every time an animation completes it start the next animation so we have a perpetual movement. If we run the example without any easing we see the balls moving in an unnatural way. We need to add at least two easing function. The first one is to easing-out the animations 1) and 3). The second is to easing-in the animations 2) and 4). The ball decelerate using a SineEase when goes to 40 (or -40) degrees because of the gravity and accelerate for the same reason when returns to zero using a CubicEase.

You may see a full size screenshot here:

How the Easing Function works

We already said that an easing function is simply a mathematical function applied to the steps of an animation. It is really simple to create your own easing functions by implementing the abstract class EasingFunctionBase, but you need to understand how the function has to calculate the values. If you think at the animation as a 0 when it starts and 1 when it stops, then you will have a value from 0 to 1 to represent a fraction of the animation itself.

The easing function has to work with this values to calculate a resulting position for each step. The values may also have a value out of the range of 0 to 1 resulting in the animation to overcome the bounds of the animation. This is the case of an ElasticEase where the end of the animation go up and down of the final value.

To better understand the easing function let me do another example using the previous scene. To avoid the balls moving perpetually after you start the animation clicking the button, every time the ball reach the upper bound the maximum limit has to be decreased so it reach the value of zero after a couple of bounces. In a real world also the ramp down is not linear. For this sample I've used a QuadraticEase initializing it in the code and calling by myself the Ease method. Here is the code snippet:

   1: /// <summary>
   2: /// Initializes a new instance of the <see cref="Page"/> class.
   3: /// </summary>
   4: public Page()
   5: {
   6:     this.OscillationEase = new QuadraticEase { EasingMode = EasingMode.EaseOut };
   7:     InitializeComponent();
   8: }

I've managed to have a number from 1 to 0 representing the steps of the animation decreased by 0.025 every time an animation reach the end. This number multiplied by 40 give the maximum amount of degree to rotate. To ease the transition from 1 to 0 I call the Ease method:

   1: private void animRXIn_Completed(object sender, EventArgs e)
   2: {
   3:     this.current -= 0.025;
   5:     if (this.current > 0)
   6:     {
   7:         ((DoubleAnimation)animLXOut.Children[0]).To = this.OscillationEase.Ease(this.current) * 40.0;
   8:         animLXOut.Begin();
   9:     }
  10:     else
  11:         startButton.IsEnabled = true;
  12: }

I think this sample have explained how the EasingFunction work. Now you have to try by yourself. Easing Function are powerful tools in the hands of a designer because give an interface a better appeal and a more fluid and believable animations.

Download Code: (552 KB)

Demo Video: (720 KB)

Aggiungi Commento