- Home
- Tutorials
- Flash
- Intermediate
- tweening

Page 2 of 4
EASING
Easing, what is it and why do I care? I'm sure you've noticed that our ball doesn't seem to be affected by the laws of physics. I mean it sort of looks like a ball but it seems to be able to take off at 45 pixels per frame without any hesitation then stops equally fast right on a dime. Such performance might make Ferraris or Porsches envious but it doesn't do much to help convince your viewers that the ball is real and has some weight. Real balls and cars need time to get up to speed as well as slow down and this is where easing comes into play. Easing alone can't make you a super designer or scripter overnight, but it can add a sense of realism to your work often lacking in some lesser animations. You may not notice when a tween is being eased, but you almost always notice when it's not. So I hope you'll adopt the following as your new motto, "Don't be a wiener, be an easing tweener". Catchy no?
Now back to business. In our previous example...
//____________________________________________
//
currentDistance = startX +(currentFrame/totalFrames) * changeInX;
//____________________________________________
...(currentFrame/totalFrames) was increased at a constant rate which in turn moved our ball across the screen at an even pace. For example during a 10 frame tween our ratio (currentFrame/totalFrames) would hold the values:
0
.1 // a difference of .1 // Taking medium steps
.2 // a difference of .1
.3 // a difference of .1
.4 // a difference of .1
.5 // a difference of .1
.6 // a difference of .1
.7 // a difference of .1
.8 // a difference of .1
.9 // a difference of .1
1 // a difference of .1 // Taking medium steps
But if we're going to have our ball start slow and gradually accelerate then we need to slowly increase the value of our ratio (currentFrame/totalFrames). But how can we do that? Well... what would happen if we squared each of our initial values? We'd end up with something like this:
0
.01 // a difference of .01 // Taking small steps
.04 // a difference of .03
.09 // a difference of .05
.16 // a difference of .07
.25 // a difference of .09
.36 // a difference of .11
.49 // a difference of .13
.64 // a difference of .15
.81 // a difference of .17
1 // a difference of .19 // Taking big steps
Notice how the values change at an increasing rate yet still start at 0 and end at 1, this is perfect for our easing purposes. We can now ease our ball with the following code:
//____________________________________________
//
Math.square = function (n){
return n*n;
}
var currentFrame = 0;
Ball_mc.onEnterFrame = function(){
//
this._x = 50 + Math.square(currentFrame/10) * 450;
//
// Which is the same as
//
// newX = startX + Math.square(currentFrame/totalFrames) * changeInX;
//
if (++currentFrame > 10) delete this.onEnterFrame;
}
//____________________________________________
What if we were to cube the ratio?
0
0.001 // a difference of 0.001 // Taking tiny steps
0.008 // a difference of 0.007
0.027 // a difference of 0.019
0.064 // a difference of 0.037
0.125 // a difference of 0.061
0.216 // a difference of 0.091
0.343 // a difference of 0.127
0.512 // a difference of 0.169
0.729 // a difference of 0.217
1 // a difference of 0.271 // Taking huge steps
//____________________________________________
//
Math.cube = function (n){
return n*n*n;
}
var currentFrame = 0;
Ball_mc.onEnterFrame = function(){
//
this._x = 50 + Math.cube(currentFrame/10) * 450;
//
// Which is the same as
//
// newX = startX + Math.cube(currentFrame/totalFrames) * changeInX;
//
if (++currentFrame > 10) delete this.onEnterFrame;
}
//____________________________________________
Did you notice how much more pronounced the easing effect is? All we had to do was switch the function (Math.square to Math.cube) which eases the initial ratio (currentFrame/totalFrames) and BLAMMO! Just like that we switched from quadratic to cubic easing. We've come a long way haven't we? Hopefully you understand the following key points:
Point-1) The relationship between the current frame and the amount of total frames is a "ratio" from 0 to 1 and changing that "ratio" is what moves or "drives" the ball across the stage.
Point-2) To ease the tween all we have to do is provide a function which distorts the values of the "ratio" so the results are weighted towards one side.
Point-3) Just for repetition's sake, it's all about the "ratio". Ratio ratio ratio.
Now that you understand the basics of tweening and easing, it's time to get a little fancy. Consider the following code:
//____________________________________________
//
Math.easeIn = function (ratio, power){
if (power == undefined) power = 2;
return Math.pow(ratio, power);
}
//____________________________________________
//
The Math.easeIn function accepts 2 arguments but only the first one is required. The first argument "ratio" is the number you want to multiply times itself and the second argument "power" is the amount of times the first argument will be multiplied by itself (for example, squared(2), cubed(3)... etc.). So now rather than being forced to switch between a Math.square function and a Math.cube function all we have to do is change the second argument of our easeIn function. Here's a little code to help clarify:
//____________________________________________
//
var currentFrame = 0;
Ball_mc.onEnterFrame = function(){
//
//
//
// To square the ratio (quadratic easing)
// we don't need to specify a "power" argument since 2 is the default
this._x = 50 + Math.easeIn(currentFrame/10) * 450;
//
// or
//
// To cube the ratio (cubic easing)
// we need to specify a power of 3
this._x = 50 + Math.easeIn(currentFrame/10, 3) * 450;
//
//
//
// Which is the same as
//
// newX = startX + Math.easeIn(currentFrame/totalFrames, power) * changeInX;
//
if (++currentFrame > 10) delete this.onEnterFrame;
}
//____________________________________________
Using our new Math.easeIn function has gained us a good deal of flexibility and control. Not only can we change the power argument on the fly, but we can set the power to non whole numbers (such as 1.5 or 3.8) as well. If the easing effect isn't prominent enough, just increase the Power. If the easing effect is too prominent, decrease the Power. It's almost too "easy" once you understand what's going on. But wait! We're not done yet. Right now all we can do is ease in, what about easing out?
As discussed earlier, these are the results returned by a 10 frame quadratic ease in:
0
.01 // a difference of .01 // Taking small steps
.04 // a difference of .03
.09 // a difference of .05
.16 // a difference of .07
.25 // a difference of .09
.36 // a difference of .11
.49 // a difference of .13
.64 // a difference of .15
.81 // a difference of .17
1 // a difference of .19 // Taking big steps
But to ease OUT we need to take big steps in the beginning and small steps in the end, so let's just reverse the order of the lines above. We're not doing anything with math yet, just changing the order for clarity.
1 // a difference of .19 // Taking big steps
.81 // a difference of .17
.64 // a difference of .15
.49 // a difference of .13
.36 // a difference of .11
.25 // a difference of .09
.16 // a difference of .07
.09 // a difference of .05
.04 // a difference of .03
.01 // a difference of .01 // Taking small steps
0
That sort of gets us headed in the right direction, at least now we're taking big steps first and little steps last. If you give it a bit of thought, it shouldn't be too hard to see that results above can be obtained by using the following formula:
//____________________________________________
//
Math.easeIn(1-ratio, 2);
//____________________________________________
For example, if "ratio" holds a value of ".1", you would get the result (1-ratio)*(1-ratio) or (.9)*(.9) or ".81" and if "ratio" held a value of ".2" then (1-.2)*(1-.2) would produce the result ".64".
But we still have a problem because the above code is returning values from 1 to 0 instead of values from 0 to 1 and that has the unwanted result of rolling our ball backwards. Fortunately we can easily get around this minor setback by changing our code to this:
//____________________________________________
//
1 - Math.easeIn(1-ratio, 2);
//____________________________________________
A simple test will show how well this works:
//____________________________________________
//
Trace("EaseIn");
for(var i=0; i<=10; i++){
var ratio = i/10;
trace(Math.easeIn(ratio));
}
trace("\rEaseOut");
for(var i=0; i<=10; i++){
var ratio = i/10;
trace(1-Math.easeIn(1-ratio));
}
</p><p>
/* Traces these results
EaseIn
0
0.01
0.04
0.09
0.16
0.25
0.36
0.49
0.64
0.81
1
EaseOut
0
0.19
0.36
0.51
0.64
0.75
0.84
0.91
0.96
0.99
1
*/
//____________________________________________
</p>
Now we're taking big steps in the beginning and returning values from 0 to 1 rather than 1 to 0. Everything works great but all that "1-" stuff is a pain to keep track of so we're going to turn the previous code into an easy to use easeOut function then show it in action.
//____________________________________________
//
Math.easeIn = function (ratio, power){
if (power == undefined) power = 2;
return Math.pow(ratio, power);
}
Math.easeOut = function(ratio, power){
return 1-Math.easeIn(1-ratio, power);
}
var currentFrame = 0;
Ball_mc.onEnterFrame = function(){
//
this._x = 50 + Math.easeOut(currentFrame/10, 3) * 450;
//
// Which is the same as
//
// newX = startX + Math.easeOut(currentFrame/totalFrames, power) * changeInX;
//
currentFrame++;
if (currentFrame>10) delete this.onEnterFrame;
}
//____________________________________________
So we have two functions, one to ease in and one to ease out. Now it's time to combine the two and make another function for what's probably the most commonly used type of tween, the good old "easeInOut". As you may have guessed, the "easeInOut" tween starts our ball off slowly, speeds it up as it approaches the middle then slows it down again as it nears the end. This is probably the most commonly used type of easing tween so it would be quite handy to combine our two "easeIn" and "easeOut" functions into one easy to use "easeInOut" function. It's time to put your thinking caps on.
When our initial ratio of completion (currentFrame/totalFrames) is less than .5 we need to easeIn and when the ratio is greater than .5 we need to ease out, but we can't just do this:
//____________________________________________
//
Math.easeInOut = function (ratio, power){
if(ratio<=.5){
return Math.easeIn(ratio, power);
}else{
return Math.easeOut(ratio, power);
}
}
//____________________________________________
One problem with the above code is that our "easeInOut" function only provides the "easeIn" function values from 0 to .5. This is bad because when we feed our "easeIn" function the value .5, it will return the value .25 because .5*.5=.25. This means our ball will only be 25% of the way to its destination before it will start to ease out. What's even worse is our "easeInOut" function will then start feeding all numbers greater than .5 to the "easeOut" function so for example when .50001 is fed into the "easeInOut" function, the "easeOut" function will return the value 1-(1-5.0001)*(1-5.001) which equals .7501. That means all the numbers between .25 and .75 will be completely ignored and make our tween look really stupid. Therefore we have to make a few modifications to our easeInOut function if we're going to get the smooth transitions we desire.
The solution is to double every number we feed the easeIn function then cut the number it returns in half. For example, when our initial ratio equals .5 we'll double it (to 1), feed 1 into the easeIn function and then cut the number it returns (1*1=1) in half which will equal .5. So now when we feed the easeInOut function numbers between 0 and .5 it will return numbers eased in from 0 to .5 and that's exactly what we want. After making the necessary modifications, our easeInOut function will look like this:
//____________________________________________
//
Math.easeInOut = function (ratio, power){
if(ratio<.5){
return Math.easeIn(2*ratio, power)/2;
}else{
return Math.easeOut(2*(ratio-.5), power)/2+.5;
}
}
//____________________________________________
As you can see, I took the liberty of applying the same principle to the "easeOut" portion of the "easeInOut" function as well as the easeIn portion. The only difference being that we first subtract .5 from our "ratio" to offset the values from (.5 through 1) to (0 through .5) so when we double the ratio the "easeOut" function will receive values ranging from 0 to 1 as expected. Then we halve the result returned by the "easeOut" function just like we did for the "easeIn" portion of the function and add .5 to shift the results back to the right end of the tween.
Now let's take everything we've learned so far and use it to ease our ball from an _x of 50 to an _x of 500 in 50 frames.
//____________________________________________
//
// Declare all our Math functions
Math.easeIn = function (ratio, power){
if (power == undefined) power = 2;
return Math.pow(ratio, power);
}
Math.easeOut = function(ratio, power){
return 1-Math.easeIn(1-ratio, power);
}
Math.easeInOut = function (ratio, power){
if(ratio<.5)return Math.easeIn(2*ratio, power)/2;
return Math.easeOut(2*(ratio-.5), power)/2+.5;
}
//
// Make the ball
createEmptyMovieClip("Ball_mc", 10);
with(Ball_mc){
lineStyle(40, 0xFF0000);
lineTo(.5, 0);
_x = 50;
_y = 200;
}
//
// Declare a variable to track the current frame
var currentFrame = 0;
// Do the actual easing
Ball_mc.onEnterFrame = function(){
this._x = 50 + Math.easeInOut(currentFrame/50) * 450;
if (++currentFrame > 50) delete this.onEnterFrame;
}
//____________________________________________
The next portion of this tutorial explains how to use different easing equations to achieve a wider variety of easing effects.
Spread The Word
Article Series
-
tweening
