Easing is… Easier

For those who have been wondering “Where’s Jesse??” the answer is that I’ve been head’s down preparing for my presentation at Tech Ed (you are going to Tech Ed aren’t you?!) 

Microsoft has three major conferences. The following is my personal description:

PDC – What is coming in the next few years. The BIG conference.
Tech Ed – What is the current state of the art. – Dive deep with geeks
Mix – Let’s get developers and designers together and create magic.

I have not abandoned any of my other projects; and all will be ramped back up to full speed as soon as my presentation is ready.  In the meantime, this entry is a brief exploration of Easing in Silverlight 3 and Blend 3.

A Note About Reduced Blogging
It’s hard cutting down on blogging. I’ve been experience  sweating, malaise,  chills and compulsive scratching.  Hmmm….

 

In Silverlight 2, Easing Wasn’t Obvious

One aspect of animation that has always looked more difficult than it really is, is easing

First, the term itself is a bit intimidating to those of us who are not specialists: what exactly is being eased?  Second, it is usually introduced like this: “oh, and then you can double click on the animation and adjust the easing. Drag the yellow dots until the easing gives you the effect you want…

The problem of course is that this presupposes that you know what easing is and what effect you want.  Happily, Silverlight 3 and Blend 3 make all of this much more straight forward.

What Are We Trying To Do?

First, let’s get the fundamentals straight:

  • Easing is a term of art for modifying an animation so that the rate of change is not uniform: specifically so that you can introduce acceleration or deceleration
  • In refers to easing at the start of the animation
  • Out refers to easing at the end of the animation

Straight Line Acceleration

Step 1 might be to create an animation that moves a dot from a starting point to an ending point over 1 second.

The animation engine is going to divide the distance it has to move the dot over the one second you’ve allocated.  The effect is that the dot moves smoothly and at the same speed from beginning to end.

Its speed is uniform.  However, (and don’t panic!)  what we care about is not just speed but acceleration.

Without setting the easing your acceleration is, essentially infinite; the object moves from 0 to its uniform velocity in zero time. Unfortunately, things don’t do that in the real world.

 

 

 

 

 

 

 

Changing the Rate of Acceleration

We are used to the Newtonian laws of physics if only intuitively. Imagine watching a movie in which an old fashioned steam engine railroad is shown.  If it went from standing at the station, emitting steam, passengers embarking and the conductor calling out “’Board!” to 60 MPH with no acceleration we’d fall over laughing.  We have fully incorporated the assumption that the train will start out painfully slowly, building speed as it goes.

This is true of everything in our environment, and adding even a little bit of acceleration or deceleration can provide a subtle, even unconscious verisimilitude to animation that moves your animated effect from cartoon like to professional.

And there’s the rub: in Silverlight 2 this felt like the province of designers and other professionals who knew what they were doing with those tiny yellow dots and funny curves. 

Silverlight 3 Makes It Easy, and Teaches What It Is Doing

In Silverlight 3 you can still create your own hand-drawn easing, but the common easing functions have been created already. Instead of leaving you with a line and a graph to draw the line on, the new easing setting lets you pick the curve you’d like. That, and a quick translation of what these curves mean, and suddenly all the mystery disappears.

The first thing to notice is that you can just click on the easing you want (Back, Bounce, Circle, Cubic) and within each you have your choice of one of three modes (In, Out, In/Out).

Now, about those nasty curves….

Easing Is A Function

Easing is accomplished by a (usually) simple function. It works like this: the acceleration (or deceleration) will change as a function of time.  That means, as time passes, the acceleration will increase.

With no easing the function looks like this:

You read that the function of time is equal to time, or in English: the acceleration will increase uniformly over time.

With that, it is easy to go to the CubicEase

 

Thus after three units of time passes, you should see 9 times the acceleration. You would expect that if you had a cubic easing at the beginning of the animation (In mode) that you’d see a fairly radical bend upwards in the curve. Consider what happens to f(t) as t goes from 1 to 5 (shown in the small table on the right)

You can see the radical bend in the curve shown in the graph in the help files (excerpt in image below)
and in the drop-down in Blend (above)

An Example To Bring It Home

To see this at work, I created an example that lets you pick from a few different easing functions and to set any of the three modes. Let’s take a quick look at the surprisingly straight forward code.  The following is the complete Xaml,

<UserControl x:Class="EasingChoices.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400"
Height="500">

<!– I’ve divided my page into an upper part for the animation
and a lower part for the controls. –>

  <Grid x:Name="OuterGrid"
Background="Bisque">
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>

<!-- One storyboard and its animation.
Note that the animation is given a name as we’ll address it programmatically. -->
<StackPanel x:Name="LayoutRoot"
Background="White"
Grid.Row="0">
<StackPanel.Resources>
<Storyboard x:Name="myStoryboard">
<DoubleAnimation x:Name="theAnimation"
From="0"
To="200"
Duration="00:00:3"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty="Height">
</DoubleAnimation>
</Storyboard>
</StackPanel.Resources>

<!--The object we'll be animating-->
<Rectangle x:Name="myRectangle"
Fill="Blue"
Width="200"
Height="30" />
</StackPanel>

<!--The inner grid holds the radio buttons-->
<Grid x:Name="innerGrid"
ShowGridLines="False"
Grid.Row="1 ">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>

<!—First of two stack panels-->

<StackPanel Grid.Column="1">
<RadioButton x:Name="Bounce"
GroupName="Easing"
Content="Bounce"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="True"
Margin="10"
FontSize="18" />
<RadioButton x:Name="Sine"
GroupName="Easing"
Content="Sine"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="False"
Margin="10"
FontSize="18" />
<RadioButton x:Name="Power"
GroupName="Easing"
Content="Power"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="False"
Margin="10"
FontSize="18" />
</StackPanel>

<!--Second stack panel-->
<StackPanel Grid.Column="2">
<RadioButton x:Name="In"
GroupName="Mode"
Content="In"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="True"
Margin="10"
FontSize="18" />
<RadioButton x:Name="Out"
GroupName="Mode"
Content="Out"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="False"
Margin="10"
FontSize="18" />
<RadioButton x:Name="InOut"
GroupName="Mode"
Content="In/Out"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsChecked="False"
Margin="10"
FontSize="18" />
</StackPanel>
</Grid>
</Grid>
</UserControl>

The supporting code responds to any button being checked by creating a new EasingFunction and telling the animation to use our new function rather than the one that appears in the Xaml.  To do this we take advantage of the fact that all the easing functions (BounceEase, SineEase, etc.) derive from a common base class: EasingFunctionBase.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace EasingChoices
{
public partial class MainPage : UserControl
{
EasingFunctionBase easingFunction = null;

public MainPage()
{
InitializeComponent();

// all events call same handler
Bounce.Checked += new RoutedEventHandler( CreateNewEasingFunction );
Power.Checked += new RoutedEventHandler( CreateNewEasingFunction );
Sine.Checked += new RoutedEventHandler( CreateNewEasingFunction );
In.Checked += new RoutedEventHandler( CreateNewEasingFunction );
Out.Checked += new RoutedEventHandler( CreateNewEasingFunction );
InOut.Checked += new RoutedEventHandler( CreateNewEasingFunction );
}



private void CreateNewEasingFunction(object sender, RoutedEventArgs e)
{
if ( Bounce.IsChecked == true )
easingFunction = new BounceEase();
if (Sine.IsChecked == true )
easingFunction = new SineEase();
if ( Power.IsChecked == true )
easingFunction = new PowerEase();

if ( In.IsChecked == true )
easingFunction.EasingMode = EasingMode.EaseIn;
if ( Out.IsChecked == true )
easingFunction.EasingMode = EasingMode.EaseOut;
if ( InOut.IsChecked == true )
easingFunction.EasingMode = EasingMode.EaseInOut;

theAnimation.EasingFunction = easingFunction;
myStoryboard.Begin();
}
}
}

That’s it.  Set the easing sub-type, set the mode, assign to theAnimation’s EasingFunction and run the storyboard. Piece of pie.

Exercise for the reader:  Want to take this a step further? Determine which Easing functions would benefit from additional arguments (e.g., number of bounces), add UI to gather the values, and pass them in to the object.
Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.

Leave a Reply

Your email address will not be published.