actionscript-3mouserolloverhittestrollout

What is the preferred way to do a mouse rollover/rollout detection in Flash with AS3


Suppose we have a stage with two squares, like so: Our example Flash stage

Suppose we'd like the yellow square to be initially hidden, and we'd like that as long as the mouse cursor is inside the bounds of the red square - the yellow square will be visible, and as long as the mouse cursor is out of the bounds of the red square - that the yellow square will be hidden.

The intuitive approach is to write something like this:

inSqr.visible = false;
outSqr.addEventListener (MouseEvent.ROLL_OVER,sqrOver);
outSqr.addEventListener (MouseEvent.ROLL_OUT,sqrOut);

function sqrOver(e:MouseEvent) {
    inSqr.visible = true;
}

function sqrOut (e:MouseEvent) {
    inSqr.visible = false;
}

With this code, however - any time you move the mouse cursor to within the yellow square - it, apparently, counts as a ROLL_OUT for the RED square - thus the sqrOut function is triggered - making the yellow square disappear, and once the yellow square is not there - the cursor is suddenly within the bounds of the RED square again - so the sqrOver function gets called - bringing back the yellow square's visibility - triggering sqrOut and so on and so forth, thus creating a "flickering" yellow square when the mouse-cursr is over him: the yellow square disappears and reappears repeatedly over and over again.

A possible "fix" for this is to remove the listener for the red's roll-out event while the cursor is inside the yellow (if it's inside the yellow it surely is inside the red as well), and bring it back when it is out, by adding this to the code above:

 inSqr.addEventListener (MouseEvent.ROLL_OVER,insqrOver);
 inSqr.addEventListener (MouseEvent.ROLL_OUT,insqrOut);
 function insqrOver(e:MouseEvent) {
     if (outSqr.hasEventListener (MouseEvent.ROLL_OUT)) {
         outSqr.removeEventListener(MouseEvent.ROLL_OUT,sqrOut);
     }
     inSqr.visible = true;
 }
 function insqrOut(e:MouseEvent) {
     if (!outSqr.hasEventListener (MouseEvent.ROLL_OUT)) {
         outSqr.addEventListener(MouseEvent.ROLL_OUT,sqrOut);
     }
 }

This works. But it is cumbersome. You have to do it for each any object inside the bounds of the red square - resulting in long code and many event listeners and continuous listener-registration and unregisteration.


A few years ago someone suggested to me this technique:

 outSqr.addEventListener (Event.ENTER_FRAME,hoverCheck);

 function hoverCheck (e:Event) {
     if (e.currentTarget.hitTestPoint(stage.mouseX,stage.mouseY,true)) {
         inSqr.visible = true;
     }
     else {
         inSqr.visible = false;
     }
 }

This is a simple short code that works. But, if your project doesn't really need to use the ENTER_FRAME event it creates the unnecessary overhead and cpu-cycles of repeatedly running the hit test. Also, if the red square covers the entire stage (has the same dimensions as the stage) - it creates problems (it doesn't work).


Is anyone aware of a simple elegant way to accomplish this - one that would not involve too cumbersome and long code and that would not have to use a repeated timer that hit-tests over and over again...?


Solution

  • Place the yellow square inside the red square.

    outSqr.addChild(inSqr);
    

    That will solve your problems nicely and simply. Just be sure outSqr is an instance of the Sprite or MovieClip class.