Detect Mouse leave stage while dragging in Actionscript 3

40,344

Solution 1

To get all of that requires a little bit of a hack. You have to store whether the mouse is off the stage or not and handle the Event.MOUSE_LEAVE event accordingly. Doing it this way gives you all the normal mouse functionality including not stopping the drag just because the mouse went off stage. Since the user might come back on stage and continue the drag it waits 'til the user releases the mouse either on or off stage.

var mouseOffStage:Boolean;

var bonk:YourDisplayObject = new YourDisplayObject()
addChild(bonk);
bonk.addEventListener(MouseEvent.MOUSE_DOWN, function():void {
  mouseOffStage = false;

  bonk.startDrag();

  stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
  stage.addEventListener(Event.MOUSE_LEAVE, mouseLeave);
  stage.addEventListener(MouseEvent.MOUSE_OUT, mouseOut);
  stage.addEventListener(MouseEvent.MOUSE_OVER, mouseOver);
})

private function mouseUp(e:MouseEvent) :void {
  trace("Mouse Up On Stage")
  bonk.stopDrag()
}

private function mouseLeave(e:Event) :void {
  if(mouseOffStage){
    trace("mouse up and off stage");
    bonk.stopDrag();
  }else{
    trace("mouse has left the stage");
    //no reason to stop drag here as the user hasn't released the mouse yet
  }
}

private function mouseOut(e:MouseEvent) :void {
  mouseOffStage = true;
  trace("mouse has left the stage")
}

private function mouseOver(e:MouseEvent) :void {
  mouseOffStage = false;
  trace("mouse has come back on stage");
}

The hack is that the MOUSE_LEAVE event, not the MOUSE_UP event, gets fired when the mouse is released off stage so you have to keep track of whether or not the mouse was already off stage when it was released.

after the drag is finished you of course want to remove all the event listeners associated with detecting mouse-outs and mouse-ups but that code was left out for readability.

Solution 2

here's what I do:

mc.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);

private function onMouseDown(_e:MouseEvent):void
{
    mc2.startDrag(params);

    stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
    stage.addEventListener(Event.MOUSE_LEAVE, onMouseLeave);
    stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
}

private function onMouseUp(_e:MouseEvent):void
{
    ms2.stopDrag();
}

private function onMouseLeave(_e:Event):void
{
    mc2.stopDrag();
}

private function onMouseOut(_e:MouseEvent):void
{
    if (e.stageX <= 0 || e.stageX >= stage.stageWidth || e.stageY <= 0 || e.stageY >= stage.stageHeight)
    {
        mc2.stopDrag();
    }
}

Solution 3

Here's a couple tricky traps not to fall into :

One bizarre thing is that in Chrome + Firefox, the MOUSE_LEAVE event isn't dispatched for a WMODE of OPAQUE orTRANSPARENT. It just doesn't fire - mouse down or up.

With WINDOW it works fine. That one took me a long time to find out! grr... http://bugs.adobe.com/jira/browse/FP-892


Second, make sure you're using Event for the parameter type for your Event.MOUSE_LEAVE handler and not MouseEvent. If you try to handle MOUSE_LEAVE with e:MouseEvent you'll get an error that you may never see (unless you're using the debug flash player). It's a very easy mistake to make because you're probably pointing all your other handlers to the same method.

Here's what I do: (just call my main endDrag from mouseLeave(e:Event)

stage.addEventListener(MouseEvent.MOUSE_MOVE, drag);
stage.addEventListener(MouseEvent.MOUSE_UP, endDrag);
stage.addEventListener(Event.DEACTIVATE, endDrag);
stage.addEventListener(Event.MOUSE_LEAVE, mouseLeave);

private function mouseLeave(e:Event):void
{
    endDrag(new MouseEvent("MOUSE_LEAVE"));
}

public function endDrag(evt:MouseEvent):void
{
    /// handle end drag
}

Solution 4

I encountered a similar problem in a PDF type viewer I had to build into a Flex application. I wanted the pan functions to still work even if the mouse left the stage or the browser window. Here is how I accomplished this, I've changed the code to remove references to Flex Framework classes so this should be applicable to any AS3 project. On mouseDown I would start tracking these values on a timer. _client can be any flash.display.DisplayObject in the target stage. In my case it was a Flex mx.controls.SWFLoader object, but in your case I suppose it would be the drag target:

private function get currentMouseX():Number
{
     return _client.stage.mouseX; 
}

private function get currentMouseY():Number
{
     return _client.stage.mouseY; 
}

The stage.mouseX and stage.mouseY values are defined relative to the stage whether the mouse is in the stage or even in the browser window (at least in Flash Player 10, I haven't tested this in earlier flash player versions). To see if the mouse is outside the stage just test and see if these values are within the stage, like so:

if (currentMouseY < 0 || 
    currentMouseY > _client.stage.height || 
    currentMouseX < 0 || 
    currentMouseX > _client.stage.width)
{
     // Do something here
}

EDIT: As to detecting a mouseUp event outside of the stage, if you register a listener on the stage, a mouseUp will be issued even if the event occurs outside of the stage or the browser. Here is the code for how I handle events function for reference. The _client object can be any flash.display.DisplayObject:

 // attach the events like so when you initialize
 _client.addEventListener(MouseEvent.MOUSE_DOWN  , handleMouse);   
 _client.addEventListener(MouseEvent.MOUSE_OUT   , handleMouse);
 _client.addEventListener(MouseEvent.MOUSE_OVER  , handleMouse);
//

// and handle them like this:
 private function handleMouse(e:MouseEvent):void
 {
      switch(e.type)
      {

          case "mouseDown":

         // add listeners, notice the mouse move and mouse up are 
         // attached to the stage, not the display object this way
         // events are issued regardless of whether the mouse is in 
         // the stage or even within the browser window

         _client.stage.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
         _client.addEventListener(MouseEvent.CLICK, handleMouse);      
         _client.stage.addEventListener(MouseEvent.MOUSE_MOVE, handleMouse);    


         // remove listeners     
         _client.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouse); 

         //
         // commands / actions 

         break;


         case "mouseUp":

         // add listeners
        _client.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse); 


         // remove listeners 
         _client.stage.removeEventListener(MouseEvent.MOUSE_UP, handleMouse);
         _client.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouse);    


         // commands/actions 

         break;


         case "click":


         // add listeners
         _client.addEventListener(MouseEvent.DOUBLE_CLICK, handleMouse);


         // remove listeners    
         _client.removeEventListener(MouseEvent.CLICK, handleMouse); 


         // commands / actions

         break;

         case "mouseMove":

         // add listeners


         // remove listeners
         _client.stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleMouse);
         _client.removeEventListener(MouseEvent.CLICK, handleMouse);   


         // commands 

         break;

         case "mouseOut":

         // add listeners


         // remove listeners

         // commands / actions

         break;

         case "mouseOver":

         // add listeners


         // remove listeners


         // commands /actions

         break;
     }
 }

EDIT: Removed references to Flex framework classes EDIT: I remember that there may be some problem with events outside of the browser window when the application is run in the Safari browser on Mac OSX. Make sure you test this code in that browser if you use it. This wasn't a problem in my application, so I didn't look into the issue further.

Share:
40,344
Adam Harte
Author by

Adam Harte

I am a digital media developer who loves building apps, games, and things for the web.

Updated on November 21, 2020

Comments

  • Adam Harte
    Adam Harte over 3 years

    Event.MOUSE_LEAVE is great in Actionscript 3, but it doesn't seem to fire if the user is holding their left (or right for that matter) mouse button down.

    Is there a way to detect if the mouse leaves the Flash movie while the mouse is held down? Or if it is released outside the flash movie?

  • Adam Harte
    Adam Harte over 14 years
    That would work, if what you are dragging was always under the mouse. But in my case, it's for a horizontal scrollbar. The problem is that if you drag the scrollbar, and move the mouse up or down (off the bar) then it will fire the event.
  • Allan
    Allan over 14 years
    Ah yes I see what you mean :S
  • Allan
    Allan over 14 years
    Perhaps if all else fails you could always do something where on ENTER_FRAME you check the mouse coordinates and see if they are outside of the stage?
  • Adam Harte
    Adam Harte over 14 years
    Checking to see if the mouse coordinates are out side the stage wont work, because flash stops updating the coordinates when the mouse leaves the stage. So they can never be "outside the stage".
  • Ryan Lynch
    Ryan Lynch over 14 years
    I made some edits to include additional code and explanation, as well as remove references to Flex Framework classes. Hopefully this helps.
  • Ryan Lynch
    Ryan Lynch over 14 years
    Attaching a mouseUp instead of a mouseOut listener to the stage will work. See my answer for code.
  • Allan
    Allan over 14 years
    MOUSE_OUT did work, the only problem was if there was say a component on stage moving out of the object would trigger the event. By checking the co-ordinates on the event handler it could be determined if it was actually out of the stage which is what Michal M's solution does. He just beat me to it after I relooked at the problem :P
  • hooleyhoop
    hooleyhoop about 13 years
    Words cannot really express how long i've been f**king this up and how glad i am i stumbled on this answer.
  • Adam Harte
    Adam Harte over 12 years
    This does not really answer the question that was asked.
  • Katax Emperore
    Katax Emperore over 12 years
    .. line " stage.addEventListener(Event.DEACTIVATE, endDrag);" , what kind of event is that?
  • toniedzwiedz
    toniedzwiedz over 11 years
    Please try to avoid posting code-only answers. Explain what your code does and how. Describe how it solves the problem posed in the question you're answering.
  • Bill Kotsias
    Bill Kotsias over 9 years
    In Flash 15 (or earlier), mouse-up out-of-stage doesn't produce mouse-leave (as greggreg suggests) but mouse-up. Sandra Bollocks. Just check the mouse (stage) co-ordinates in mouseMove and compare against stage dimensions to realize if in-or-out of stage..!
  • Bill Kotsias
    Bill Kotsias over 9 years
    It's self-explanatory for god's shake
  • Bill Kotsias
    Bill Kotsias over 9 years
    That's even better than my solution, but mine has the advantage that it can be used with any display object (i.e. Bitmaps too), where as 'startDrag' is a Sprite-only function
  • Adam Harte
    Adam Harte over 9 years
    Can mouse x/y ever be less than 0 and greater than the stage width/height?
  • Bill Kotsias
    Bill Kotsias over 9 years
    Yup, if having the mouse button pressed
  • Bill Kotsias
    Bill Kotsias over 9 years
    It's funny, you said beware of MOUSE_LEAVE being passed as a MouseEvent parameter, yet you made the same mistake with Event.DEACTIVATE -> endDrag. :-)
  • aruno
    aruno over 9 years
    Oops. I got away for it for almost four years :)