Flash: Navigation Control AS 2.0

05.02.08 | Author: Deborah
Write-Up by Danny Staten

Introduction

Mouse-mashing users can easily break many flash pages with their click-happy ways. The ability to properly react to inputs during animations has long been a frustrating problem for flash development

The standard solution is to explicitly disable all clickable movie clips whose click events can mess up the animation, and then enable them when the animation finishes. This solution works, but has many limitations including:

  • For large lists of clips to disable (say a very large navigation), it has the potential to influence performance.
  • For dynamically generated content, it can be very difficult to determine all the clickable items to disable/enable.
  • For dynamically generated content, it can be very difficult to manage the state of all the clickable items for all cases.

An unobvious but very simply and elegant solution does exist. It can be used to disable all mouse clicks by region or for the entire flash stage. Generally I use it to disable clicks for the entire stage, but it can be done in other ways to disable specific regions.

The Basic Solution

The Solution can be thought of like a glass panel that you put on top of the page. It takes advantage of the fact that a movie clip with a fill drawn in clickable even if it has an alpha value of 1. I will call that clip the glass panel.

The basic solution utilizes two globally accessible functions.

disableNavigation():

This function creates a movie clip to act as the glass panel. A simple implementation is to create it at the root with a hard coded depth of 1000. Obviously, that detail of the implementation is entirely flexible. I generally try to organize my clip creations so that they are organized inside a small group of clips. If you have 1000 clips right in your root level you are probably not organizing your clips effectively.

enableNavigation():

This function basically just calls removeMovieClip on the glass panel.

Basic Solution's Code

function disableNavigation()
{
            createEmptyMovieClip(“glassPanel_mc”,1000);
            utilities.draw_box(glassPanel_mc,Number(0xFF0000),0,0,0,Stage.width,Stage.height);
            /*utilities.draw_box takes the movie clip to draw to, the color, alpha, cornerX, cornerY, width, and height as parameters.  For the disable clip functionality you can either have the fill’s alpha at zero, or fill it and then set the clip’s alpha to zero.  Either one works.  I like to have it actually fill red for debugging purposes.  You can change the alpha to 25% and get a visual identifier of when your navigation is disabled for testing. */

            glassPanel_mc.onPress=function()
            {}
            /*the empty click function is important.  If you don’t give this a press event function then it won’t intercept the click event and other items that are lower in depth will still get clicked on.*/
            glassPanel_mc.useHandCursor=false;
            /*This last part is purely an aesthetic effect, but it makes the final touch to make it cool.  This way the user is actually triggering a click event when they do click, but they don’t know they are because the mouse cursor doesn’t change at all.*/
}

function enableNavigation()
{
removeMovieClip(glassPanel_mc);
}

This solution is very powerful, and can be modified to disable specific regions of your stage very easily.  Any time you need to disable clicks you can call disableNavigation and the mouse press events will not cause you any grief.  To turn them off simply call enableNavigation. 

The More Robust Solution

The basic solution is nice and all, but it doesn’t quite reach the power that I was looking for.  I basically wanted to simulate event driven programming where multiple events can all have input to when to disable and enable your navigation.

Say for example, that I have a data call via flash remoting, and an animation running.  I want to wait until both of those have finished.  The trick is that the data call is often faster than the animation, but there are occasions where the remoting call can be slower than the animation.  We need a way for the glass panel to only remove itself when both events are done.  I wanted a means that could be extended to n asynchronous items as a general case.

Now suppose that every time you call disableNavigation, you are also putting a marker on the glass panel.  Every time you call enableNavigation you then remove one marker from the panel.  If the panel has no markers on it then enableNavigation removes the panel.  You can have every event that needs to ensure the user clicking is disabled to function properly put a marker on the panel.  Then when the event finishes it removes a marker.  As long as every event has one disableNavigation and one enableNavigation call, then a simple counter will suffice.

We can solve this problem quite elegantly by adding a few lines of code to our functions.  Basically you add a counter to the glass panel when it is created.  Every call to disableNavigation will increment this counter, and every call to enableNavigation will decrement it.  If the counter gets to zero in the enableNavigation call then it is removed and navigation is active again. 

Robust Solution’s Code

function disableNavigation()
{
            if(glassPanel_mc==undefined)
            {
                        createEmptyMovieClip(“glassPanel_mc”,1000);
utilities.draw_box(glassPanel_mc,Number(0xFF0000),0,0,Stage.width,stage.height);
                        glassPanel_mc.holds_num=0;
glassPanel_mc.onPress=function()
            {
            }
glassPanel_mc.useHandCursor=false;
}
            glassPanel_mc.holds_num++;
           
           
}
function enableNavigation()
{
           
            glassPanel_mc.holds_num--;
            if(glassPanel_mc.holds_num <=0)
            {
                        glassPanel_mc.removeMovieClip();
            }
}

Important Note

This solution does have some challenges too it, but its simplicity in implementation means that it is usually what I go with rather than implementing more complicated alternatives.  The challenge is that it is very easy to accidentally lock your navigation.  It is up to you as the developer to ensure that there is a corresponding enable for every disable.  If you have too many enable calls, then your navigation can unlock too early, and if you have too few enable calls then your navigation will be permanently locked. Depending on the complexity of the logic in your code, this may not be trivial.  In the first iteration of this solution, you can call disableNavigation multiple times and one call to enableNavigation will unlock the interface.  That is nice for a lot of things but doesn’t solve more complex management issues.  This solution allows for more complex management, but it also makes it very easy to accidentally lock your navigation. 

Even more complicated versions are always a possibility, but none that I have found have had enough benefits to justify the effort.

Of course, if you want to take the time to identify segments of code where you are absolutely certain the navigation should be enabled, you can enforce that as well.  Simply wrap removeMovieClip(glassPanel_mc) in a function such as masterUnlockNavigation() to abstract the details of the implementation from the main code of the program.  I have never had cause to do that.  Generally speaking, if you manage your glass panel properly you shouldn’t need to force it to enable.

Final Thoughts

I hope you have found this bit of information useful.  For the longest time, I was busting my tail trying to manage individual clips enabled states to control user input.  One day as I was trying to solve why something was clickable when I couldn’t see it, the synapses in the brain started firing and my interface management frustrations were a thing of the past.  So if you have already got a better solution, I would love to hear it.  Chances are that if you are reading this article it is because you have been frustrated by the same issue.  So I hope this prevents a few headaches for you.
Add to Technorati Favorites

1 RESPONSE:

Flash Tutorials says:
07.11.08

We have already discussed how to handle mouse-mashing clicks using Actionscript 2. The more structured

LEAVE A COMMENT

 
*