Hack 26 Smooth Scripted Motion

figs/moderate.gif figs/hack26.gif

Increasing the frame rate can cause Flash's rendering engine to hog processor time. Create visually smooth animations without simply increasing the frame rate.

One of Flash designers' worst habits is increasing the frame rate to absurdly high values to make animations appear smoother. Although this may work in a simple FLA performing a lone animation, when you are building a larger Flash movie or site, you shouldn't let screen drawing hog all the processor time. Setting the frame rate to 95 frames per second (fps) forces Flash to constantly render images to the screen. The resulting lack of idle time can make sounds pop and cause delays in event processing, which can make interactivity appear sluggish.

This hack looks at how beginning Flashers are often taught to animate and why it isn't always the best solution. Then we'll look at ways to create smooth animations without changing the frame rate.

User-Actuated Movement (Breaking the onEnterFrame Monopoly)

In the simplest case of animations created at authoring time, the Flash playhead progresses through the timeline and displays each frame in turn. This is so-called frame-based animation, like a digital flip book. In such a scenario, the obvious way to increase the speed of the animation is to increase the frame rate (you can set the frame rate in the Document Properties dialog box accessible via ModifyDocument). When a traditional animator learns Flash, he feels right at home with these techniques.

But when that same Flasher learns scripted animation, he must change his mode of thinking. Most beginning scripters are taught to implement scripted motion using onEnterFrame( ) handlers. In simple cases, in which the frame rate is not excessive and scripting animation should be tied to the frame rate, using onEnterFrame( ) is a reasonable option. But when your animations become more sophisticated, so must your techniques. Otherwise, performance is likely to suffer or your creativity is apt to be limited.

Don't fall into the trap of thinking that all motion graphics should be controlled via onEnterFrame( ) handlers. Because onEnterFrame events are tied to the frame rate, the easiest way to create smoother animations is to increase the frame rate, which increases the frequency of onEnterFrame events. However, if the user interacts with graphics and animation, onMouseMove( ) is a far more efficient event handler to employ. A drag-and-drop feature is a good candidate to be implemented in an onMouseMove( ) handler. Likewise, features in which the user controls animation or performs drawing with the mouse can both be controlled via onMouseMove events.

Rather than increase the frame rate to make a draggable movie clip move smoothly, use updateAfterEvent( ) within an onMouseMove( ) handler to redraw the Stage while the mouse is moving.

The following code performs a drag-and-drop operation smoothly, even if you set a low frame rate, such as 1 fps:

function pressHandler( ) {

  this.startDrag( );

  this.onMouseMove = function( ) {

    // Refresh the Stage while the item is being dragged

    updateAfterEvent( );


  this.onRelease = function( ) {

    this.stopDrag( );

    delete this.onMouseMove;


  this.onReleaseOutside = this.onRelease;


// Create a movie clip and make it draggable

var puck:MovieClip = this.createEmptyMovieClip("puck", 

                  this.getNextHighestDepth( ));

puck.lineStyle(40, 0xCCCCCC, 100);

puck.moveTo(-1, 0);

puck.lineTo(1, 0);

puck._x = 275;

puck._y = 200;

puck.onPress = pressHandler;

Let's review key portions of the code. The main code (following the pressHandler( ) definition) creates a small movie clip and makes pressHandler( ) its onPress event handler. This triggers the action when the user clicks on the movie clip.

The function pressHandler( ) attaches an onMouseMove( ) event handler to the movie clip, which redraws the screen repeatedly as the mouse is dragged. This arrangement causes Flash to redraw the screen at a higher rate only when the item is being dragged, giving us smooth movement without having to increase the frame rate. This implementation is provided to allow ActionScript 1.0 scripters to focus on the techniques being taught. Similar drag-and-drop code can be implemented using OOP and ActionScript 2.0 [Hack #20] .

The same principle can be seen in the following listing. Here, the onMouseMove event is used to create a simple Pencil tool. If the onMouseMove event was changed to onEnterFrame, the event handler would be running when it is not required (i.e., when the cursor has not moved).

 function penDown( ) {

  this.moveTo(_xmouse, _ymouse);

  this.onMouseMove = function( ) {

    this.lineStyle(null, 0xCCCCCC, 100);

    this.lineTo(_xmouse, _ymouse);

    updateAfterEvent( );


  this.onMouseUp = function( ) {

    delete this.onMouseMove;



var drawClip:MovieClip = this.createEmptyMovieClip("drawClip", 

                                    this.getNextHighestDepth( ));

drawClip.onMouseDown = penDown;

Final Thoughts

Although the temptation to use onEnterFrame( ) event handlers to animate everything always exists, it is not the only event that can be used to create animation in Flash. We just saw a case in which using the onMouseMove event produced both more efficient and smoother animation. It was more efficient because it uses less processing power (it refreshes the screen only when needed), and it is smoother because the frequency of onMouseMove events (unlike onEnterFrame events) is unrelated to the frame rate.

Although using the onMouseMove event applies only when the user action is related to mouse movement (such as for drag-and-drop or pen drawing operations), you can look for other events on which to trigger screen refreshes. For example, you might update the screen when data is received or when a sound completes.

However, in some cases you may want an animation to be performed over time. Luckily, onEnterFrame is not the only time-related event available. When you want to create several animations that will run at different rates, it is usually better to use setInterval( ) to create a separate timed event for each [Hack #27] (rather than use onEnterFrame for all of them).