16

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?

Nik Reiman
  • 39,067
  • 29
  • 104
  • 160
Adam Harte
  • 10,369
  • 7
  • 52
  • 85

8 Answers8

28

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.

greggreg
  • 11,945
  • 6
  • 37
  • 52
  • 3
    Words cannot really express how long i've been f**king this up and how glad i am i stumbled on this answer. – hooleyhoop Apr 19 '11 at 16:50
  • 1
    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 Nov 20 '14 at 17:42
4

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();
    }
}
Michal M
  • 9,322
  • 8
  • 47
  • 63
2

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
}
Community
  • 1
  • 1
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
1

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.

Ryan Lynch
  • 7,676
  • 1
  • 24
  • 33
  • I made some edits to include additional code and explanation, as well as remove references to Flex Framework classes. Hopefully this helps. – Ryan Lynch Nov 08 '09 at 20:29
0

If your doing something where you are dragging a MovieClip this seems to work nicely.

stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);

EDIT - never mind

Allan
  • 3,339
  • 2
  • 21
  • 24
  • 1
    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. – Adam Harte Oct 14 '09 at 01:48
  • Ah yes I see what you mean :S – Allan Oct 14 '09 at 03:29
  • 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? – Allan Oct 14 '09 at 03:44
  • 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". – Adam Harte Oct 14 '09 at 05:54
  • Attaching a mouseUp instead of a mouseOut listener to the stage will work. See my answer for code. – Ryan Lynch Nov 08 '09 at 20:36
  • 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 – Allan Nov 09 '09 at 00:03
0

Is there a way to detect if the mouse leaves the Flash movie while the mouse is held down?

Not that i know of

Or if it is released outside the flash movie?

Event.MOUSE_LEAVE does happen when you release outside.

more info here http://blog.zupko.info/?p=3 see JIMISAACS comment.

SketchBookGames
  • 464
  • 8
  • 14
0

Here is the right answer. Custom class you pass a DisplayObject to and will drag it till mouse-up or mouse out-of-stage. Customize at will:

package fanlib.gfx
{
    import flash.display.DisplayObject;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.ui.Mouse;

    public class Drag
    {
        private var obj:DisplayObject;
        private var point:Point = new Point();
        private var stg:Stage;

        public function Drag(obj:DisplayObject)
        {
            this.obj = obj;
            stg = Stg.Get();
            stg.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
            stg.addEventListener(MouseEvent.MOUSE_UP, stopDrag);
            //stg.addEventListener(Event.MOUSE_LEAVE, stopDrag); // sh*t just won't fire
            point.setTo(stg.mouseX, stg.mouseY);
        }

        private function mouseMove(e:MouseEvent):void {
            if (stg.mouseX <= 0 ||
                stg.mouseY <= 0 ||
                stg.mouseX >= stg.stageWidth ||
                stg.mouseY >= stg.stageHeight) {
                stopDrag();
                return;
            }
            obj.x += stg.mouseX - point.x;
            obj.y += stg.mouseY - point.y;
            point.setTo(stg.mouseX, stg.mouseY);
        }

        public function stopDrag(e:* = null):void {
            stg.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
            stg.removeEventListener(MouseEvent.MOUSE_UP, stopDrag);
            //stg.removeEventListener(Event.MOUSE_LEAVE, stopDrag);
        }
    }

}
Bill Kotsias
  • 3,258
  • 6
  • 33
  • 60
0
    var youMax_X:Number; //set this var to Max x
    var youMax_Y:Number; //set this var to `enter code here`Max y

    var dragBounds:Rectangle = new Rectangle(0,0,youMax_X,yourMax_Y);

    stage.addEventListener(MouseEvent.MOUSE_DOWN,handleDown);
    stage.addEventListener(MouseEvent.MOUSE_UP,handleUp);


    private function handleDown(e:Event):void{
            this.startDrag(false,dragBounds);
    }
    private function handleUp(e:Event):void{
        this.stopDrag();
    }