Detect Mouse leave stage while dragging in Actionscript 3
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.
Adam Harte
I am a digital media developer who loves building apps, games, and things for the web.
Updated on November 21, 2020Comments
-
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 over 14 yearsThat 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 over 14 yearsAh yes I see what you mean :S
-
Allan over 14 yearsPerhaps 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 over 14 yearsChecking 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 over 14 yearsI made some edits to include additional code and explanation, as well as remove references to Flex Framework classes. Hopefully this helps.
-
Ryan Lynch over 14 yearsAttaching a mouseUp instead of a mouseOut listener to the stage will work. See my answer for code.
-
Allan over 14 yearsMOUSE_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 about 13 yearsWords cannot really express how long i've been f**king this up and how glad i am i stumbled on this answer.
-
Adam Harte over 12 yearsThis does not really answer the question that was asked.
-
Katax Emperore over 12 years.. line " stage.addEventListener(Event.DEACTIVATE, endDrag);" , what kind of event is that?
-
toniedzwiedz over 11 yearsPlease 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 over 9 yearsIn 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 over 9 yearsIt's self-explanatory for god's shake
-
Bill Kotsias over 9 yearsThat'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 over 9 yearsCan mouse x/y ever be less than 0 and greater than the stage width/height?
-
Bill Kotsias over 9 yearsYup, if having the mouse button pressed
-
Bill Kotsias over 9 yearsIt'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 over 9 yearsOops. I got away for it for almost four years :)