Pages

Google+

Friday, November 27, 2009

Improving Flash's Interactive Timeline Animations

Something that has become (somewhat) of a lost art these days is the animated button. Designers used to go crazy with engrossing roll overs and roll outs - sometimes to the detriment of usability. Now-a-days, I don't see many interesting buttons.

Perhaps it's due to how button animations can be  decently executed with Javascript and CSS;  people are getting used to those and many designers are simplifying Flash buttons to mimic Javascript and CSS buttons. Another theory might be do to how more of Flash work has shifted towards developers handling  animations.  They prefer to just code the animation and in doing so can do some great things, but some things still can only done by a hand made animation.

Then....there is another theory of mine; a more subtle detail that everyone thinks, but doesn't say out loud...

Hand animated buttons are jumpy!


Hand animated buttons use the timeline. When someone 'rolls over' a button the play head jumps to a certain frame and plays that animation and the same occurs for a 'roll over'.  The problem is, if someone 'rolls out' while the 'roll over' animation is playing, the playhead jumps to another animation without any context of where it was during a 'roll over'.




[caption id="" align="aligncenter" width="645" caption="Bad Timeline!"]Bad Timeline![/caption]

The code looks something like this:
private function roll_over(event : MouseEvent) : void
{
jumpy_button.gotoAndPlay("ROLL_OVER");
}

private function roll_out(event : MouseEvent) : void
{
jumpy_button.gotoAndPlay("ROLL_OUT");
}

What this has also lead to is, if hand made animations are created for a button, they are designed to animate fast.  So if someone quickly mouses over a navigation with several buttons, the jumpiness is minimized.


Consider the button below.  Start by rolling over the button, wait for it to complete, then roll out.  Then roll over and out quickly. The animation lasts a second - exaggerated for demonstration reasons:


[SWF]http://blog.alanklement.com/files/examples/smooth_animations/jumpy_button.swf, 634, 200[/SWF]


I checkout out a great site today and was distracted by jumpy button animations. But ya know, it doesn't have to be that way.... here is the same hand animation below:


[SWF]http://blog.alanklement.com/files/examples/smooth_animations/smooth_button.swf, 634, 200[/SWF]


Now we're cooking with evil gas!


My solution is quite simple.  It's not really a fix, rather a different way of moving the playhead. Here is the timeline for the button above:




[caption id="" align="aligncenter" width="645" caption="Good Timeline..."]Good Timeline...[/caption]

and here is my code:



public var current_frame : int = 10;
private var button : Button;

// code that adds event listeners and adds button to display list

private function smooth_roll_over(event : MouseEvent) : void
{
TweenLite.to(this, 1, {current_frame:34, onUpdate:animate_movie_clip, ease:Linear.easeNone});
}

private function smooth_roll_out(event : MouseEvent) : void
{
TweenLite.to(this, .5, {current_frame:10, onUpdate:animate_movie_clip, ease:Linear.easeNone});
}

private function animate_movie_clip() : void
{
button.gotoAndStop(current_frame);
}

Sometime ago I learned a great technique from a developer I work with, Yoann,  about using Tweenlite to manpulate field variables. Since then I've been using it intermittently, but when I saw some jumpy buttons this morning, it occoured to me: 'wait these animations are just numbered frames....'

I suppose you could use a timer, or some other technique, but this is easy and fast.

Just declare a variable to hold the current frame for the play head to move to:
public var current_frame : int = 10;

'10' is the frame I want to animation to start at - check out the 'good timeline' example above for reference.

The event handlers then simply tween the current frame property to the number '34', the frame number where the animation ends, or back down to '10' where the animation starts. On update, TweenLite calls the 'animate_movie_clip' method, which moves the play head.

Tweenlie is using no ease, 'Linear.easeNone', because easing would alter the speed in which the 'current_frame' would be in/decremented.

Another great benefit of this technique is the fact that I can adjust the speed of the animations on the fly, just change the 'time' property of Tweenlite, '1' and '.5' respectively in my example. In this example, it is particularity useful since the timeline uses a motion guide. Altering key frames with a motion guide on the time line is difficult and clumsy.

A drawback of this technique is that the animations are not quite as smooth as the timeline.  I suppose I could force the frame to render, but it's not worth the side effects of that.  I will gladly trade slightly less smooth animations for the increased flexibility and the potential for this.