Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Easing

by Voetsjoeba   | filed under Flash and ActionScript

This is an archived tutorial from the kirupa.com legacy collection. It covers software that may no longer be available, but it is kept online because the ideas still hold up.

Easing is a way of moving from one location to another, so that the speed of the object that moves along its track varies. This tutorial will deal with a specific type of easing called ‘Easing Out’; easing in a way so that the object’s speed decreases as it nears its destination. The complement of Easing Out is Easing In, and since they are both similar, I will mention Easing In only in passing towards the end of the tutorial.

1. Introduction to Easing

Consider the following two examples to get a good grasp of what the difference is between the two. Both represent an object moving from the left towards a destination on the right, but in two different ways. The first one uses no easing at all ( a constant speed ) to move to its destination, the second uses ‘Easing Out’ to move to its destination. From now on I will refer to ‘Easing Out’ as simply ‘easing’, for the sake of consistency and readability.

 

The above example does not use any easing. Notice how the square's speed stays constant throughout the movement.

On the other hand, the above square contains a slight easing. You should see the square start to decelerate as it nears its destination.

So, easing as we’ll discuss it here is the way of moving where an object slows down as it nears its goal. The key to implementing this in Flash might be simpler than you thought. What happens is basically you take the distance between the object and its destination, divide it by a number greater than 1, and you now have a distance that is smaller. We then move our object to be at the newly calculated distance from its destination, and repeat this reduction as long as necessary.

Why does this make it ease? Well, since we’re dividing, the greater the distance is between the object and its destination, the greater the difference in calculated distance will be in absolute terms. Put otherwise: the closer the object gets to its goal, the smaller the distance to the goal is. The smaller the distance to the goal, the smaller the distance will be that we calculate. The smaller the distance is that we calculate, the less we move the object moves towards its goal. Thus we have easing movement.

That might sound a bit confusing. Consider the following example: we have an object that is currently at position 0, and we want to ease it to position 120. We must first pick a number to divide each distance by, as this will determine the speed of our easing motion. Let’s pick 2. We can now follow these steps:

  1. Take the distance between the object’s current position and its destination

  2. Divide this distance by 2

  3. Move the object towards its destination, so that it is the distance we calculated in step 2 away from it.

  4. Repeat Step 1

Indeed, this is a loop, every iteration through it updating the object’s position. To clarify how it works, here’s a visual representation. The line on the left indicates the object’s starting position (0), and the line on the right indicates the object’s destination (120). The orange line represents the object’s new position, calculated by dividing the distance between current position and destination by 2.

Considering that you can go on and on, always dividing the distance by 2 (don’t worry about ending the loop yet), we get the following movement pattern:

Now that you have an understanding of how to think about easing out effects, let's delve into how easing can be implemented in Flash.

From the previous section, we have thought out how our easing works. Now, how can we apply this easing method in Flash? This page will start you on your path to finding out!

2. Creating the Easing Function in Flash

In any case, our function will have to use some sort of loop construction, so that we can have it follow the above steps inside it to create our easing movement. For and While loops are out of question here, as these would loop way too fast, making the movement practically invisible. The human eye cannot interpret a series of images as fast as a computer can execute for and while loops.

Luckily, Flash provides us with the onEnterFrame handler, which is called every time right before Flash draws a frame of the movie to the screen. The speed at which Flash does that is set by the movie’s Frames Per Second ( FPS ) value. An FPS value of 24 means that Flash will draw a frame to the screen 24 times every second. This value is also the minimum FPS needed for the human eye to consider a sequence of images as an animation. I personally tend to use an FPS setting of 40: more than sufficiently fast, and not too resource-consuming.

By relying on the movie’s FPS, we have an excellent loop to use. Here is some example code that implements the very first version of our easing function that will ease MovieClips along the stage following the x axis. In Flash, we will most commonly be easing MovieClips, as these are the most generic stage objects in Flash. Other stage objects such as TextFields or Buttons can also easily be eased using our final version of the function, but that’s an issue for when we get there. Furthermore, MovieClips directly implement the onEnterFrame handler which other stage objects don’t, so that we do not have to worry about using helper MovieClips and the likes just yet.

var speed:Number = 2;
ease = function( what:MovieClip , to:Number ){
  what.onEnterFrame = function(){
  var distance:Number = to - this._x;
  var newDistance:Number = distance / speed;
  this._x = to - newDistance;
  }
}

The first line sets the speed at which we will be easing. This is not really a speed at which the MovieClip advances (because that will decrease as it nears its destination), it is instead the value we will use to divide each distance by. This uniquely defines the speed of the easing.

Important

The value of speed must always be strictly greater than 1! If not, you will be moving your object away from it’s goal rather than towards it, or not moving it at all.

Check out the following example:
 

What’s important here is the ease function. It takes two arguments: the MovieClip to ease, and the x position to ease it to. Inside that function, we can see that the onEnterFrame event handler is being set for the MovieClip we’re easing. Every frame, we take the distance between the destination and the current position. We then divide that distance by 2; I assigned this value to the variable speed earlier. We then position the MovieClip to be newDistance away from its goal, thus we have successfully implemented our easing movement.

In the previous section, we reviewed what we want our easing function to do in three statements. We can write those 3 statements inside the onEnterFrame handler’s body as a single statement like this:

var speed:Number = 2;
ease = function( what:MovieClip , to:Number ){
  what.onEnterFrame = function(){
  this._x = to – ( to – this._x ) / speed;
  }
}

And there we go ! We have achieved a function that will easy any MovieClip to any point on the x axis, in just these 6 lines of ActionScript ! Excellent, but we have been ignoring something up till now: this onEnterFrame handler has not been told to ever stop, which means it will still be calculating even long after the destination has been ‘reached’.

Theoretically, it can never reach its destination, much like 0.9999999…. will never reach 1. But, computers have limitations in calculating floating point numbers, and therefore so does Flash, and so do screens. At a certain point you’ll end up calculating these insanely small numbers, and moving the MovieClip on the stage by that distance won’t have any effect, because those amounts won’t even add up to a hundredth of an actual pixel to move.

So, we have to build in some kind of a check to see when the MovieClip has very closely neared its destination, and then remove the onEnterFrame handler. That way, the onEnterFrame handler won’t keep calculating values that have no effect anyway. This is important in big projects where CPU load is an issue. Trust me, you do not want an onEnterFrame stalling in the background for every MovieClip you’ve ever eased around.

Therein lies a problem: because the MovieClip will never actually reach its destination, it is pointless to check if it has. What we could do is set some kind of ‘null distance’ that will determine how far the MovieClip needs to be away from its destination to be considered ‘there’. This method works well if you stick to a certain speed and ease all MovieClips using that speed, but unfortunately it does not make for a good general solution. This is because the null distance depends on the speed of the easing. The slower the speed, the slower the object will be moving towards its destination, and the finer the dividing of the distances. The finer the dividing of the distances, the more MovieClip positioning limitations will take effect. This makes the null distance method fairly unreliable.

A better solution is to keep track of the previous positions of the MovieClip, and compare them to each other. There will always be a certain point where the difference between the distances will be so small that Flash won’t bother to update the MovieClip to that position because moving a MovieClip over such a small distance is either meaningless or simply impossible. When that happens, two consequent positions will be the same, indicating that Flash has reached its limitation of MovieClip positioning and that further calculations will have no effect anymore. This is when we want our loop to end.

Fortunately, performing this check is easy. All we need is an extra variable that holds the previous position. When we move the MovieClip to its new position, we check if this new position is the same as the last one. If it is, then we have reached our limit and we can safely kill the loop because further calculations are pointless as they will all result in the same position. And to ensure maximum positioning accuracy, we can set our MovieClip to its exact destination once this happens. Just to be sure.

With that in mind, we can redefine our easing function like this:

var speed:Number = 2;
ease = function( what:MovieClip , to:Number ){
  var previousPosition:Number = what._x;
  what.onEnterFrame = function(){
  this._x = to – ( to – this._x ) / speed;
  if( this._x == previousPosition ){
  this._x = to;
  delete this.onEnterFrame;
  }
  previousPosition = this._x;
  }
}

We now have our easing function. But a function is only a function, and when you’re working on a project with a rather large hierarchy of MovieClips, you’ll find it annoying to always have to create a huge path to that one function somewhere on the main timeline to call it from way down in your MovieClip hierarchy.

3. Making Usability and Good-Practice Adjustments

Luckily, Flash provides us with the MovieClip.prototype object to easily solve this issue. I will not discuss the ins and outs of what prototype objects are and how they work: for an excellent and all-in explanation anything you’ll ever need to know about AS 1.0 OOP, please read this tutorial by resident Flash guru Senocular. It’s a long read, but it’s your road to Flash enlightenment.

By defining this function in the MovieClip.prototype object, every MovieClip anywhere will be able to call this method as if it were their own. The prototype function looks like this:

MovieClip.prototype.ease = function( to:Number , speed:Number ){
  var previousPosition:Number = this._x;
  if( isNaN( speed ) || Number(speed) !== speed || speed <= 1 ) speed = 1.2;
  this.onEnterFrame = function(){
  this._x = to – ( to – this._x ) / speed;
  if( this._x == previousPosition ){
  this._x = to;
  delete this.onEnterFrame;
  }
  previousPosition = this._x;
  }
}

Notice that we have made a few additional changes here. The speed value can now be passed along as an argument to the function, allowing for every MovieClip to be eased at an individual speed. If the speed value is in any way incorrect, it will default to 1.2.

Now that we have defined our function as a prototype method, we can apply it to any MovieClip we want. Whereas previously you would call the easing function like this:

ease(myMovieClip,600);

We can now call it like this:

myMovieClip.ease(600, 1.2);

Which, I’m sure you’ll agree, is handier to work with, especially when we’ll be adding functionality later on.

We can now look further into optimizing this method of easing. For example, what if the MovieClip is already at the position it was called to ease to ? Simple: the onEnterFrame loop will be entered, its next position will be calculated, the difference in position will be 0, so that the next position will equal its starting position. Therefore the new position will immediately equal its previous (starting) position, and the loop will immediately exit.

Even though this will work just fine, this is not good programming practice. When you move a MovieClip to the position it’s already at, you haven’t done anything. So it also would make sense for our function not to do anything either. An easy if check will make sure of that:

MovieClip.prototype.ease = function( to:Number , speed:Number ){
  if( what._x != to ){
  var previousPosition:Number = this._x;
  if( isNaN( speed ) || Number(speed) !== speed || speed <= 1 ) speed = 1.2;
  this.onEnterFrame = function(){
  this._x = to – ( to – this._x ) / speed;
  if( this._x == previousPosition ){
  this._x = to;
  delete this.onEnterFrame;
  }
  previousPosition = this._x;
  }
  } else {
  // do nothing
  }
}

We now have generic easing function that can be applied to any MovieClip anywhere in the movie using an individual speed setting, and cleans up after itself. Looking even better!
 

The above animation shows all of the code I have explained in action.

So far we have focused on implementing easing and making the implementation more portable (see previous section), but there are some edge cases that we will need to address before you can use this effect without running into any issues.

4. Delegating the onEnterFrame Handler

Up till now, our method has always been using the onEnterFrame handler of the MovieClip it was easing. This is an issue when we want to ease a MovieClip that already has an onEnterFrame handler set to perform some other task. We can’t just assume that it’s ok for us to overwrite any MovieClip’s onEnterFrame handler!

[ the MovieClip’s onEnterFrame handler directly sets its own _x property – not good ! ]

Considering that every MovieClip instance listens to the onEnterFrame event, the solution to this problem is obvious: we’ll just use another MovieClip’s onEnterFrame handler ! But which MovieClip ? It has to be one of which be can be sure it has no onEnterFrame handler already in use. The only way we can be sure about that, is by spawning and using an auxiliary MovieClip. Because our auxiliary MovieClip has just been created, we are sure that it does not have an onEnterFrame handler already in use.

So where do we spawn this auxiliary MovieClip ? Since every MovieClip must be able to ease separately from any other, we will create it inside the MovieClip we will be easing. This also enables for easy targeting: our auxiliary MovieClip will only need to target its parent MovieClip to get a reference to the MovieClip it will be easing around.

[ A better way of doing it: delegating the onEnterFrame to an auxiliary child MovieClip ]

Spawning MovieClips through ActionScript is done using the createEmptyMovieClip function. The new MovieClip will be created inside the MovieClip that called the method, making the MovieClip that called the method the parent of the newly created MovieClip. When creating it, you must specify an instance name and a depth for the MovieClip. The choice of these two values is important, as they both have to be unique for each easing method.

Right now we’re seeing a method of easing along the X axis, but we will also see methods for easing a MovieClip’s width, height, y position, scale, etc. That’s why we will now rename our easing function to easeX, to differ between any additional future easing methods. Because a MovieClip should be able to ease more than 1 of these properties at the same time, we must make sure that all the easing methods use different auxiliary MovieClips that take care of the separate easing loops. And to ensure they are all different, we must ensure that they all have both consistently unique instance names and consistently unique depths.

MovieClip.prototype.easeX = function( to:Number , speed:Number ){
  if( what._x != to ){
  var _this:MovieClip = this;
  var aux:MovieClip = this.createEmptyMovieClip( “aux_easeX” , 1337 );
  var previousPosition:Number = this._x;
  if( isNaN( speed ) || Number(speed) !== speed || speed <= 1 ) speed = 1.2;
  aux.onEnterFrame = function(){
  this.x = to – ( to - this.x ) / speed;
  if( this.x == previousPosition ){
  this.x = to;
  this.removeMovieClip();
  }
  previousPosition = this.x;
  }
  } else {
  // do nothing
  }
}


Notice that we are now no longer just removing the MovieClip’s onEnterFrame, but rather the entire auxiliary MovieClip. This because when the easing movement has ended, this extra MovieClip has lost its purpose and should be deleted. For easy code adjustment within the loop body, we have saved a reference to the parent MovieClip being eased as _this, so that we need only replace the this by _this in order to target the MovieClip we’re easing instead of the auxiliary MovieClip.

So now, we can ease MovieClips around just like before, but this time keeping their onEnterFrame handler intact.

We are almost done with this tutorial. In the previous section, you learned how to avoid overwriting a movie's onEnterFrame handler. In this page you will learn one more trick, and then I will conclude this tutorial with a collection of source files used in the various animations you have seen.

5. Applying Function Chaining

When applying this easing method in various projects, you will find that you will often want to call another function after the easing has ended. For example, you may want to slide open a panel by moving it from one side to the either, and when it has, load your content into it. You don’t want your content to already start loading before your MovieClip is in place.

To solve this problem, we can use what I like to call function chaining. More accurately I’d call it function call chaining, but the former sounds better. The idea is to pass along a function to the easing method that is to be called when the easing has completed. That way, you can say: ease this MovieClip to this position at this speed, and when it’s done, call this function.

The way we’ll be doing this is by using the Function.apply method. The syntax is as follows:

myFunction.apply(thisObject,argumentsObject);

This will call the function myFunction as a method of thisObject, using the set of parameters as indicated by the argumentsObject array. Notice that this allows for a method to be applied as a method of another object rather than the object that defined it. Consider the following example:

var objectA:Object = new Object();
var objectB:Object = new Object();
objectA.myProperty = "property of A!";
objectB.myProperty = "property of B!";
objectA.theProperty = function(){
  trace( this.myProperty );
}
objectA.theProperty.apply(objectB,[]);

This will apply the method theProperty as defined by objectA to objectB, even though it is was defined in objectA. This allows for great flexibility in calling functions.

To implement this, the code looks like this:

MovieClip.prototype.easeX = function( to:Number , speed:Number , endF:Function , endO:Object , endP:Array ){
  if( what._x != to ){
  var _this:MovieClip = this;
  var aux:MovieClip = this.createEmptyMovieClip( "aux_easeX" , 1337 );
  var previousPosition:Number = this._x;
  if( isNaN( speed ) || Number(speed) !== speed || speed <= 1 ) speed = 1.2;
  aux.onEnterFrame = function(){
  _this._x = to - ( to - _this._x ) / speed;
  if( _this._x == previousPosition ){
  _this._x = to;
  this.removeMovieClip();
  if( endF ) endF.apply( endO , endP );
  }
  previousPosition = _this._x;
  }
  } else {
  if( endF ) endF.apply( endO , endP );
  }
}

We can now optionally pass three more arguments to our easing method: endF ( for endFunction ) refers to a function that will be applied once the easing is complete. Consistently with endF, we pass along endO ( for endObject ) and endP ( for endParameters ) which are respectively the object that endF will be applied to and the set of parameters that will be sent along with the call. Notice that our else clause has now become useful: if our object is already at its destination, we still want our ending function to be called. Before, that else clause was just doing nothing.

We can now chain functions calls one after the other; for example, we can have a MovieClip ease to a certain position and then back at a different speed:

var startX:Number = myMovieClip._x;
var endX:Number = startX + 500;
myMovieClip.easeX(endX, 1.2, myMovieClip.easeX, myMovieClip, [startX, 1.4]);

Again, notice the flexibility of the Function.apply method: the function we are calling is myMovieClip.easeX, but that doesn’t necessarily mean we also want to apply it to myMovieClip, we could also have applied it to any other MovieClip to ease that one instead of myMovieClip. And because myMovieClip.ease isn’t defined in myMovieClip but in its prototype object, all the following are equivalent:

myMovieClip.easeX(endX, 1.2, myMovieClip.easeX, myMovieClip, [startX, 1.2]);
myMovieClip.easeX(endX, 1.2, MovieClip.prototype.easeX, myMovieClip, [startX, 1.2]);
myMovieClip.easeX(endX, 1.2, myMovieClip.__proto__.easeX, myMovieClip, [startX, 1.2]);
myMovieClip.easeX(endX, 1.2, myMovieClip.createEmptyMovieClip("randomMC", myMovieClip.getNextHighestDepth()).easeX, myMovieClip, [startX, 1.2]);
myMovieClip.easeX(endX, 1.2, _root.easeX, myMovieClip, [startX, 1.2]);

Again, for a closer understanding why this is, please read Senocular's AS1 OOP tutorial.

We can even have it continuously ease it back and forth:
 

The code I used the call the easeX prototype function is the following:

var startX:Number = myMovieClip._x;
var endX:Number = startX+500;
chain = function () {
  myMovieClip.easeX(endX, 1.2, myMovieClip.easeX, myMovieClip, [startX, 1.4, chain, null, []]);
};
chain();

This to illustrate the power of function chaining. In fact, I don’t think I have ever been working on a project where I haven’t used this easing method. It’s just everywhere, and the function chaining makes it easy to control the exact behavior of your application, with no guessing work involved.

6. Globalizing the Easing Method to other Properties

As I have mentioned earlier, this method of easing can be used to ease all kinds of properties, including but not limited to the _y, _xscale and _yscale, _width, _height, and even _alpha properties. And this is where the power of the function chaining really comes into play: you can control exactly how and when you want your ActionScripted easing animations to take place.

 
Download proto.as

In the file proto.as you will find easing methods for the most common properties such as _x, _y, _width, _height and the likes, logically called easeX, easeY, easeWidth, easeHeight, and so on. You can easily use these in your movies by placing proto.as in the same folder as your .fla is in, and then include them:

#include "proto.as"

This will import the prototype method definitions into your movie, allowing you to easily use them without having them clutter up your ActionScript panel. Have a look at the example file example7_proto.fla in the following zip file to see them all in action.
 

This concludes this tutorial. If you have any further questions or if you notice something you think is wrong, please don’t hesitate to let us know on the forums.

Peace out,

Voetsjoeba
voetsjoeba.com

Just a final word before we wrap up. What you've seen here is freshly baked content without added preservatives, artificial intelligence, ads, and algorithm-driven doodads. A huge thank you to all of you who buy kirupa's books, became a paid subscriber, watch the videos, and/or interact on the forums.

Your support keeps this site going! 😇

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2026 //--