Monday, October 1, 2012

Javascript Physics: Using Easing Functions to Animate Acceleration

Physics is not just for gaming any more. It has become common place in just about every UI we use today. Look at how swiping on a iPhone or Android device flips you between screens nice and smoothly. The faster you swipe, the quicker the screen moves. These devices have inspired just about every UI made today to adopt a similar look and feel. Unfortunately, this eye candy comes with a complexity price tag for developers. You no longer can just change the position - you need to change the position over time. And not only over a linear path - it needs to appear to get gradually faster and then gradually slower until it reaches its destination.

Fortunately, Javascript libraries have solved this problem by adding animation functions to remove the complexity of moving an object over time.  They've gone further by adding easing functions to also create the effect of acceleration/deceleration to that motion. 

[caption id="attachment_355" align="alignleft" width="323"> Distance vs. Time at 3 m/s2 rate of acceleration[/caption]

[caption id="attachment_356" align="alignleft" width="321"> Time vs. Time Squared[/caption]

If you look specifically at jQuery, the core animation library includes two easing choices: linear and swing. Neither of these actually model the actual acceleration defined by physics. Realistic acceleration is going to look like the curves on the left.

In these two graphs, I plotted the actual math to find the distance over time that something would travel if it was accelerating at 3 m/s2 for 9 seconds and time vs. time squared for the same 9 seconds. You can see the curves are identical - the only difference is the magnitude in the y-axis. That is just the scaling caused by using 3 m/s2 in the first example.

That means acceleration is going to follow a quadratic curve so we need to find a quadratic easing function to emulate natural acceleration. To get that in jQuery, we have to add jQuery UI to our projects because among the animation enhancements it adds, there is a nice library of easing functions. Two of these functions - easeInQuad and easeOutQuad - provide acceleration and deceleration easing based on real-world physics, respectively.

Just to demonstrate how much easier it is to use the easing function than trying to solve the acceleration on your own, I created an example on my sandbox.



I created two boxes - one animated using the linear easing function which I then did the math for acceleration and the other using the easeInQuad and easeOutQuad easing function to do the work for me.

The code using the easing functions is quite simple:


$('#ease')
.animate({left: pos}, {
easing: 'easeInQuad',
duration: 1000 * pos / max
})
.animate({left: max}, {
easing: 'easeOutQuad',
duration: 1000 * (1 - pos / max)
});


Since I want to first accelerate to a position and then decelerate to the final position, the animate is split into two pieces. I want the whole animation to take 1000 ms so I split the time proportionately between the two parts. If you want the acceleration and deceleration to be split evenly (ie 500ms/500ms), you can use one animate call with 'easeInOutQuad'. However, I wanted to use a slider to adjust the distance the box traveled during each phase of the animation.

The equivalent functionality that does not use the easing function is a bit longer:


var etime = etime = (new Date()).getTime();

$('#calc')
.animate({tabIndex: 0}, {
easing: 'linear',
duration: 1000,
step: stepCalc
});

function stepCalc(now, fx)
{
var n = fx._left || 0,
tt = (new Date()).getTime() - etime,

t1 = tt,
t2 = 1000 - tt,

v1 = acc * t1,
v2 = dcc * t2,

d1 = v1 * t1,
d2 = max - v2 * t2,

d = (n <= pos ? d1 : d2);

fx._left = d;

fx.elem.style.left = fx._left+'px';
}


Its just not as easy to code as using the easing function. You have to animate a dummy property and use the step callback to do the math. Additionally, the deceleration part of the movement was tricky to solve because you have to flip the problem around so you're basically working backwards from the end to the start of the deceleration.

Its clear that the easing function provide a significant advantage to creating smooth, realistic movement on an element with very little effort. With these features, you're only task is finding the starting/ending points and the amount of time required to get there.

Most libraries have quite a few more easing functions that just the quadratic ones covered here. I was most interested in emulating actual physics based movements. However, there are many more possibilities available. Here's a few other examples of different other easing functions:

  • Robert Penner Easing Function Demo - You'll see these functions used in many libraries that provide easing. This demo allows you to see the different acceleration provided by each function.

  • jQuery UI Effects Easing Showcase - This page shows the graph of each easing function available in the jQuery UI library. There's some additional functions that create a bounce effect which can save you some calculations.

  • Tweenlite Easing Pack Demo - I had created this demo to try out Tweenlite. However, it also allows you to try each easing function and see how it works.



By using the built-in easing function available in the different animation libraries, we can achieve realistic movement in our animations without having to do all the math associated with finding the acceleration, velocity, and displacement over time. The function take care of this model - all we need to do is tell the animation where we want to go and the library will do the heavy lifting for us. Now, creating those fancy movements in our UI will be no more complex than just changing the position without the effect.