Creating Realistic Movement in Animations

Creating Realistic Movement in Animations

In both of the previous examples, we implemented very primitive techniques for animating our display. For instance, the car animation loop simply incremented the x position of the car by 3 pixels each time the function was called. Did anyone think that looked believable? No, we didn’t think so. Let’s fix that first of all.

To do this, we will use easing to smooth the start or end of the animation (or both). For instance, applying some appropriate easing to our animation would make the car appear to accelerate up to speed or brake to a stop.

As this isn’t a book specifically focused on animation, we won’t go into depth on what is involved in creating an easing effect nor attempt to write code from the ground up. Rather, we will use some of Robert Penner’s existing easing equations (see www.robertpenner.com/easing) to create a more realistic effect of motion for our car. These equations were first written for Flash, but have a look in the source of many of the JavaScript libraries that implement easing animation and you will find a reference to Robert’s excellent work.

It’s likely we will make use of these easing equations again, so let’s add them to our prowebapps.js file:

PROWEBAPPS = (function() { ...

var module = { ...

Easing: (function() { var subModule = { Linear: function(t, b, c, d) { return c*t/d + b; },

Sine: { In: function(t, b, c, d) {

return -c * Math.cos(t/d * (Math.PI/2)) + c + b;

Out: function(t, b, c, d) {

return c * Math.sin(t/d * (Math.PI/2)) + b;

InOut: function(t, b, c, d) {

return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;

CHAPTER 7: Exploring Interactivity

return subModule; })(),

return module; })();

In the preceding code, we added two of the many easing functions available in Penner’s work. Each of these easing functions takes four parameters:

t: The elapsed time for the animation

b: The beginning value, or the value we are easing from

c: The change value, or the difference between the end value and the start

d: The duration of the animation So, by way of example, the following would tell us what the value should be if we were

easing from 0 to 500, 600 milliseconds in, for a 2-second animation: newValue = PROWEBAPPS.Easing.Linear(600, 0, 500, 2000); And if we were easing from 10 to 1700 at the same point in time: newValue = PROWEBAPPS.Easing.Linear(600, 1100, 600, 2000);

If that doesn’t make complete sense yet, don’t worry—it will by the time we have a few examples down. Let’s integrate the easing code into our car animation sample. We would suggest creating a separate JavaScript file so that you can do a side-by-side comparison.

Download from Wow! eBook <www.wowebook.com>

Here’s the sample code for car-easing.js: (function() {

var ANIMATION_DURATION = 1000;

var canvas = null, context = null, car = null, endPos = null, animationStart = 0;

function resetCanvas() { ... } // resetContext

function animate() { context.save();

CHAPTER 7: Exploring Interactivity

try { if (endPos && car && car.complete) { // determine the elapsed time

var elapsedTime = new Date().getTime() - animationStart,

carX = PROWEBAPPS.Easing.Linear( elapsedTime, 0, endPos.x, ANIMATION_DURATION) - car.width;

// clear the drawing surface context.clearRect(0, 0, canvas.width, canvas.height);

// draw the car context.drawImage(car, carX, endPos.y - car.height);

// if the car x is greater than the end pos, then remove it if (elapsedTime > ANIMATION_DURATION) {

endPos = null; } // if

} // if } finally {

context.restore(); } // try..finally } // animate

$(window).bind("resize", resetCanvas).bind("reorient", resetCanvas);

$(document).ready(function() { window.scrollTo(0, 1); resetCanvas();

document.body.addEventListener("touchstart", function(evt) { endPos = { x: evt.touches[0].pageX / window.devicePixelRatio, y: evt.touches[0].pageY / window.devicePixelRatio

// capture the animation start tick count animationStart = new Date().getTime();

// prevent screen scrolling evt.preventDefault();

}, false);

// load our car image car = new Image(); car.src = "car.png";

setInterval(animate, 40); }); })();

We’ll quickly go through the notable sections of this code:

CHAPTER 7: Exploring Interactivity

The “constant” ANIMATION_DURATION is used to set the time that the animation will run for.

Each time the animate function is called, and when the animation is first triggered (in the touchstart event handler), we use a call to new Date().getTime() to determine the current time in milliseconds. In the context of the animate function, we use that figure to determine how much time has elapsed since the animation started.

The calculation of the carX variable has changed to use the PROWEBAPPS.Easing.Linear function. This variable can now be declared locally in that function. The Linear easing function doesn’t actually perform any easing. Once we have validated our modifications, we will drop in the Sine easing functions to replace the Linear easing.

Determining that the animation has reached its final value is now done based on a comparison between elapsedTime and the animation duration. This is done as some easing functions return higher values than the destination on the way to the end value (sounds confusing, but you’ll see).

Running this sample should display the car animating, but still show an animation that doesn’t look any smoother—the car still stops very abruptly. Let’s fix that now. Replace the reference to PROWEBAPPS.Easing.Linear with PROWEBAPPS.Easing.Sine.Out, and you should see the car image slow down as it approaches the x coordinate of your touch start point.

NOTE: The majority of Penner’s easing equations come in three variants: In, Out, and InOut. The In variant will apply the easing at the beginning of the animation, and, in the case of our car, this means it will start slow and then speed up. Easing out means that values will have easing applied as the animation approaches its final value, which is exactly what we want with our car—for it to slow to a stop. An InOut easing function applies easing at both the start and end of the animation. We’d recommend playing around with the different variants to get a feel for how they work.

CHAPTER 7: Exploring Interactivity

ADDING AN ADDITIONAL EASING FUNCTION TO PROWEBAPPS

As mentioned previously, we really only implemented one of Penner’s easing functions for our animation, and there are many more useful easing functions in his library. It is a reasonably simple exercise to take another of his existing samples from ActionScript and port it to JavaScript and into the prowebapps.js file. One that would look good with the car animation (and a personal favorite of mine) would be the “Back Out” easing function.

The Back Out easing function is a good pick for this particular situation as the effect is to slightly overshoot the actual animation end point, and then slowly reverse back to the target point. In the case of a car, this looks quite believable. We don’t think you’ll be disappointed with whichever additional easing function(s) you may choose. Trust us, it’s hard to stop applying easing to your animations once you start.