Обнаружение этапа покидания мыши при перетаскивании в ActionScript 3

Event.MOUSE_LEAVE отлично подходит для ActionScript. 3, но похоже, что он не срабатывает, если пользователь удерживает левую (или правую) кнопку мыши нажатой.

Есть ли способ определить, покидает ли мышь Flash-ролик, пока мышь удерживается нажатой? Или если он выходит за рамки флеш-ролика?


person Adam Harte    schedule 13.10.2009    source источник


Ответы (8)


Чтобы получить все это, требуется немного взлома. Вы должны запомнить, находится ли мышь вне сцены или нет, и соответственно обработать событие Event.MOUSE_LEAVE. Выполнение этого способа дает вам все обычные функции мыши, включая отсутствие остановки перетаскивания только из-за того, что мышь сошла со сцены. Поскольку пользователь может вернуться на сцену и продолжить перетаскивание, он ждет, пока пользователь не отпустит мышь на сцене или за ее пределами.

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");
}

Хакерство заключается в том, что событие MOUSE_LEAVE, а не событие MOUSE_UP, запускается, когда мышь отпускается со сцены, поэтому вы должны отслеживать, была ли мышь уже вне сцены, когда она была выпущена.

после завершения перетаскивания вы, конечно, хотите удалить все прослушиватели событий, связанные с обнаружением выхода и подъема мыши, но этот код был оставлен для удобства чтения.

person greggreg    schedule 10.11.2009
comment
Слова не могут выразить, как долго я все это дурачился и как я рад, что наткнулся на этот ответ. - person hooleyhoop; 19.04.2011
comment
Во Flash 15 (или более ранних версиях) перемещение курсора мыши вне рабочей области вызывает не отпускание мыши (как предлагает Греггрег), а перемещение курсора мыши вверх. Сандра Боллокс. Просто проверьте координаты мыши (рабочей области) в mouseMove и сравните их с размерами рабочей области, чтобы понять, находятся ли они на этапе или нет ..! - person Bill Kotsias; 20.11.2014

вот что я делаю:

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();
    }
}
person Michal M    schedule 05.11.2009

Вот пара хитрых ловушек, в которые нельзя попасть:

Одна странная вещь заключается в том, что в Chrome + Firefox событие MOUSE_LEAVE не отправляется для WMODE из OPAQUE илиTRANSPARENT. Просто не срабатывает - мышь вниз или вверх.

С WINDOW нормально работает. Мне потребовалось много времени, чтобы это выяснить! grr ... http://bugs.adobe.com/jira/browse/FP-892


Во-вторых, убедитесь, что вы используете Event в качестве типа параметра для обработчика Event.MOUSE_LEAVE, а не MouseEvent. Если вы попытаетесь обработать MOUSE_LEAVE с помощью e:MouseEvent, вы получите ошибку, которую никогда не увидите (если вы не используете отладочный флэш-плеер). Это очень простая ошибка, потому что вы, вероятно, указываете всем своим обработчикам тот же метод.

Вот что я делаю: (просто позвоните своему основному endDrag из 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
}
person Simon_Weaver    schedule 18.05.2011
comment
.. строка stage.addEventListener (Event.DEACTIVATE, endDrag); , что это за событие? - person Katax Emperore; 01.11.2011
comment
Забавно, вы сказали, остерегайтесь передачи MOUSE_LEAVE в качестве параметра MouseEvent, но вы сделали ту же ошибку с Event.DEACTIVATE - ›endDrag. :-) - person Bill Kotsias; 20.03.2015
comment
Ой. Я скрывался за это почти четыре года :) - person Simon_Weaver; 20.03.2015

Я столкнулся с аналогичной проблемой в программе просмотра PDF-файлов, которую мне пришлось встроить в приложение Flex. Я хотел, чтобы функции панорамирования продолжали работать, даже если мышь покидает рабочую область или окно браузера. Вот как я это сделал: я изменил код, чтобы удалить ссылки на классы Flex Framework, поэтому это должно быть применимо к любому проекту AS3. На mouseDown я бы начал отслеживать эти значения по таймеру. _client может быть любым flash.display.DisplayObject на целевой стадии. В моем случае это был объект Flex mx.controls.SWFLoader, но в вашем случае я предполагаю, что это будет цель перетаскивания:

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

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

Значения stage.mouseX и stage.mouseY определяются относительно сцены, находится ли мышь в рабочей области или даже в окне браузера (по крайней мере, в Flash Player 10, я не тестировал это в более ранних версиях флэш-плеера). Чтобы увидеть, находится ли мышь за пределами сцены, просто проверьте, находятся ли эти значения внутри сцены, например:

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

РЕДАКТИРОВАТЬ: Что касается обнаружения события mouseUp вне сцены, если вы зарегистрируете слушателя на сцене, будет выдано mouseUp, даже если событие происходит вне сцены или браузера. Вот код того, как я обрабатываю функцию событий для справки. Объект _client может быть любым 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;
     }
 }

РЕДАКТИРОВАТЬ: удалены ссылки на классы фреймворка Flex. РЕДАКТИРОВАТЬ: Я помню, что при запуске приложения в браузере Safari в Mac OSX могут возникнуть проблемы с событиями за пределами окна браузера. Обязательно протестируйте этот код в этом браузере, если вы его используете. В моем приложении это не было проблемой, поэтому я больше не разбирался в проблеме.

person Ryan Lynch    schedule 07.11.2009
comment
Я внес некоторые изменения, чтобы включить дополнительный код и пояснения, а также удалить ссылки на классы Flex Framework. Надеюсь, это поможет. - person Ryan Lynch; 08.11.2009

Если вы делаете что-то там, где перетаскиваете MovieClip, это работает нормально.

stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);

РЕДАКТИРОВАТЬ - неважно

person Allan    schedule 13.10.2009
comment
Это сработает, если то, что вы перетаскиваете, всегда находилось под курсором мыши. Но в моем случае это для горизонтальной полосы прокрутки. Проблема в том, что если вы перетащите полосу прокрутки и переместите мышь вверх или вниз (за пределы полосы), то событие запустится. - person Adam Harte; 14.10.2009
comment
Ах да, я понимаю, что вы имеете в виду: S - person Allan; 14.10.2009
comment
Возможно, если все остальное не поможет, вы всегда сможете сделать что-нибудь, где на ENTER_FRAME вы проверяете координаты мыши и видите, находятся ли они за пределами сцены? - person Allan; 14.10.2009
comment
Проверка того, находятся ли координаты мыши вне сцены, не будет работать, потому что вспышка перестает обновлять координаты, когда мышь покидает сцену. Так что они никогда не могут быть вне сцены. - person Adam Harte; 14.10.2009
comment
Присоединение к сцене mouseUp вместо слушателя mouseOut будет работать. Смотрите мой ответ на код. - person Ryan Lynch; 08.11.2009
comment
MOUSE_OUT действительно работал, единственная проблема заключалась в том, что, скажем, компонент на сцене, выходящий из объекта, запускал бы событие. Проверяя координаты в обработчике событий, можно определить, действительно ли он находится вне стадии, что и делает решение Михала М. Он просто опередил меня после того, как я пересмотрел проблему: P - person Allan; 09.11.2009

Есть ли способ определить, покидает ли мышь Flash-ролик, пока мышь удерживается нажатой?

Не то, что я знаю из

Или если он выходит за рамки флеш-ролика?

Event.MOUSE_LEAVE действительно происходит, когда вы отпускаете снаружи.

подробнее здесь http://blog.zupko.info/?p=3 см. JIMISAACS комментарий.

person SketchBookGames    schedule 14.10.2009

Вот правильный ответ. Пользовательский класс, которому вы передаете DisplayObject, и перетаскиваете его, пока курсор мыши не выйдет за пределы сцены. Настроить по желанию:

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);
        }
    }

}
person Bill Kotsias    schedule 20.11.2014
comment
Может ли мышь x / y быть меньше 0 и больше ширины / высоты сцены? - person Adam Harte; 21.11.2014
comment
Ага, если нажата кнопка мыши - person Bill Kotsias; 21.11.2014

person    schedule
comment
Это не совсем ответ на заданный вопрос. - person Adam Harte; 29.09.2011
comment
Постарайтесь не публиковать ответы, содержащие только код. Объясните, что делает ваш код и как. Опишите, как он решает проблему, поставленную в вопросе, на который вы отвечаете. - person toniedzwiedz; 11.11.2012
comment
Это говорит само за себя о дрожи бога - person Bill Kotsias; 20.11.2014
comment
Это даже лучше, чем мое решение, но у меня есть то преимущество, что его можно использовать с любым экранным объектом (например, с растровыми изображениями), где startDrag - это функция только для Sprite. - person Bill Kotsias; 20.11.2014