Laya鼠標事件閱讀


  1. 點擊事件核心類:MouseManagerTouchManager
    MouseManager負責收集相關事件,進行捕獲階段和目標階段。
    TouchManger負責處理和分發事件,進行冒泡階段。
    • 捕獲階段:此階段引擎會從stage開始遞歸檢測stage及其子對象,直到找到命中的目標對象或者未命中任何對象;
    • 目標階段:找到命中的目標對象;
    • 冒泡階段:事件離開目標對象,按節點層級向上逐層通知,直到到達舞台的過程。

       

  2. 事件是由Canvas(瀏覽器控件等)發起,在MouseManager中注冊處理。
  3. MouseManager在監聽到事件后,會將事件添加到隊列中,在stage進入下一幀時(Stage._loop),一次性處理完所有事件。
  4. 捕獲、目標階段核心邏輯:(從根節點開始,向子節點查找)
    1. 初始化Event。MouseManager維護一個唯一的Event對象,保留鼠標事件相關信息,如target、touchid等。
    2. 先判斷sp是否有scrollRect,如果則scrollRect外,直接判定為沒點到,返回false。
    3. 優先檢測(HitTestPrior),並且沒有點擊到,則直接返回false。(width>0並且沒有點擊穿透的View,默認會被設置為HitTestPrior = true
    4. 命中檢測邏輯(hitTest
      1. 參數為被判斷的sp和轉換為相對與sp的鼠標坐標(通過fromParentPoint方法)
      2. 如果有scrollRect,則先偏移鼠標點擊位置。
      3. 如果sp的hitArea字段不為空,則返回sp的hitArea.isHit結果。
      4. 如果mouseThrough為true,則檢測子對象的實際大小進行碰撞。否則就使用(0,0,width,height)的矩形檢測點是否在矩形內。
    5. 倒敘遍歷子對象,從外向內檢測,檢測到后,直接跳過內部檢測。
    6. 將目標對象和Event對象傳遞給TouchManager
  5. 冒泡階段核心邏輯:(從子節點開始,向根節點查找)
    1. 獲取或創建的點擊信息,用於檢測拖拽、雙擊等。
    2. 從當前sp開始,遞歸收集父節點,按順序放入數組中,子節點在前,父節點在后。
    3. 按照數組屬性,對節點發送相關事件,如果事件被阻斷(event.stopPropagation),則直接跳過所有父節點。事件的currentTarget為最先被點擊的sp。
  6. 鼠標事件觸發后,參數默認為MouseManager中的event對象。如果監聽事件時,自己設置參數,則會在自定義參數數組后,添加event事件。
    else if (args) result = method.apply(caller, args.concat(data));
  1 MouseManager:
  2 private function check(sp:Sprite, mouseX:Number, mouseY:Number, callBack:Function):Boolean {
  3    this._point.setTo(mouseX, mouseY);
  4    sp.fromParentPoint(this._point);
  5    mouseX = this._point.x;
  6    mouseY = this._point.y;
  7 
  8    //如果有裁剪,則先判斷是否在裁剪范圍內
  9    var scrollRect:Rectangle = sp.scrollRect;
 10    if (scrollRect) {
 11     _rect.setTo(scrollRect.x, scrollRect.y, scrollRect.width, scrollRect.height);
 12     if (!_rect.contains(mouseX, mouseY)) return false;
 13    }
 14 
 15    //先判定子對象是否命中
 16    if (!disableMouseEvent) {
 17     //優先判斷父對象
 18     //默認情況下,hitTestPrior=mouseThrough=false,也就是優先check子對象
 19     //$NEXTBIG:下個重大版本將sp.mouseThrough從此邏輯中去除,從而使得sp.mouseThrough只負責目標對象的穿透
 20     if (sp.hitTestPrior && !sp.mouseThrough && !hitTest(sp, mouseX, mouseY)) {
 21      return false;
 22     }
 23     for (var i:int = sp._childs.length - 1; i > -1; i--) {  //倒敘遍歷,從外向內檢測,如果檢測到則跳過內部檢測
 24      var child:Sprite = sp._childs[i];
 25      //只有接受交互事件的,才進行處理
 26      if (!child.destroyed && child.mouseEnabled && child.visible) {
 27       if (check(child, mouseX, mouseY, callBack)) return true;
 28      }
 29     }
 30    }
 31 
 32    //避免重復進行碰撞檢測,考慮了判斷條件的命中率。
 33    var isHit:Boolean = (sp.hitTestPrior && !sp.mouseThrough && !disableMouseEvent) ? true : hitTest(sp, mouseX, mouseY);
 34 
 35    if (isHit) {
 36     _target = sp;
 37     callBack.call(this, sp);
 38    } else if (callBack === onMouseUp && sp === _stage) {
 39     //如果stage外mouseUP
 40     _target = _stage;
 41     callBack.call(this, _target);
 42    }
 43 
 44    return isHit;
 45   }
 46 
 47   private function hitTest(sp:Sprite, mouseX:Number, mouseY:Number):Boolean {
 48    var isHit:Boolean = false;
 49    if (sp.scrollRect) {
 50     mouseX -= sp.scrollRect.x;
 51     mouseY -= sp.scrollRect.y;
 52    }
 53    if (sp.hitArea is HitArea) {
 54     return sp.hitArea.isHit(mouseX, mouseY);
 55    }
 56    if (sp.width > 0 && sp.height > 0 || sp.mouseThrough || sp.hitArea) {
 57     //判斷是否在矩形區域內
 58     if (!sp.mouseThrough) {
 59      var hitRect:Rectangle = this._rect;
 60      if (sp.hitArea) hitRect = sp.hitArea;
 61      else hitRect.setTo(0, 0, sp.width, sp.height); //坐標已轉換為本地坐標系
 62      isHit = hitRect.contains(mouseX, mouseY);
 63     } else {
 64      //如果可穿透,則根據子對象實際大小進行碰撞
 65      isHit = sp.getGraphicBounds().contains(mouseX, mouseY);
 66     }
 67    }
 68    return isHit;
 69   }
 70 
 71   /**
 72    * 執行事件處理。
 73    */
 74   public function runEvent():void {
 75    var len:int = _eventList.length;
 76    if (!len) return;
 77 
 78    var _this:MouseManager = this;
 79    var i:int = 0,j:int,n:int,touch:*;
 80    while (i < len) {
 81     var evt:* = _eventList[i];
 82 
 83     if (evt.type !== 'mousemove') _prePoint.x = _prePoint.y = -1000000;
 84 
 85     switch (evt.type) {
 86     case 'mousedown': 
 87      _touchIDs[0] = _id++;
 88      if (!_isTouchRespond) {
 89       _this._isLeftMouse = evt.button === 0;
 90       _this.initEvent(evt);
 91       _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseDown);
 92      } else
 93       _isTouchRespond = false;
 94      break;
 95     case 'mouseup': 
 96      _this._isLeftMouse = evt.button === 0;
 97      _this.initEvent(evt);
 98      _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseUp);
 99      break;
100     case 'mousemove': 
101      if ((Math.abs(_prePoint.x - evt.clientX) + Math.abs(_prePoint.y - evt.clientY)) >= mouseMoveAccuracy) {
102       _prePoint.x = evt.clientX;
103       _prePoint.y = evt.clientY;
104       _this.initEvent(evt);
105       _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseMove);
106        //   _this.checkMouseOut();
107      }
108      break;
109     case "touchstart": 
110      _isTouchRespond = true;
111      _this._isLeftMouse = true;
112      var touches:Array = evt.changedTouches;
113      for (j = 0, n = touches.length; j < n; j++) {
114       touch = touches[j];
115       //是否禁用多點觸控
116       if (multiTouchEnabled || isNaN(_curTouchID)) {
117        _curTouchID = touch.identifier;
118        //200次點擊清理一下id資源
119        if (_id % 200 === 0) _touchIDs = {};
120        _touchIDs[touch.identifier] = _id++;
121        _this.initEvent(touch, evt);
122        _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseDown);
123       }
124      }
125 
126      break;
127     case "touchend": 
128     case "touchcancel":
129      _isTouchRespond = true;
130      _this._isLeftMouse = true;
131      var touchends:Array = evt.changedTouches;
132      for (j = 0, n = touchends.length; j < n; j++) {
133       touch = touchends[j];
134       //是否禁用多點觸控
135       if (multiTouchEnabled || touch.identifier == _curTouchID) {
136        _curTouchID = NaN;
137        _this.initEvent(touch, evt);
138        var isChecked:Boolean;
139        isChecked = _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseUp);
140        if (!isChecked)
141        {
142         _this.onMouseUp(null);
143        }
144       }
145      }
146 
147      break;
148     case "touchmove": 
149      var touchemoves:Array = evt.changedTouches;
150      for (j = 0, n = touchemoves.length; j < n; j++) {
151       touch = touchemoves[j];
152       //是否禁用多點觸控
153       if (multiTouchEnabled || touch.identifier == _curTouchID) {
154        _this.initEvent(touch, evt);
155        _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseMove);
156       }
157      }
158      break;
159     case "wheel": 
160     case "mousewheel": 
161     case "DOMMouseScroll": 
162      _this.checkMouseWheel(evt);
163      break;
164     case "mouseout": 
165      //_this._stage.event(Event.MOUSE_OUT, _this._event.setTo(Event.MOUSE_OUT, _this._stage, _this._stage));
166      TouchManager.I.stageMouseOut();
167      break;
168     case "mouseover": 
169      _this._stage.event(Event.MOUSE_OVER, _this._event.setTo(Event.MOUSE_OVER, _this._stage, _this._stage));
170      break;
171     }
172     i++;
173    }
174    _eventList.length = 0;
175   }
176  }
177 TouchManager
178 /**
179    * 派發事件。
180    * @param eles    對象列表。
181    * @param type    事件類型。
182    * @param touchID (可選)touchID,默認為0。
183    */
184   private function sendEvents(eles:Array, type:String, touchID:int = 0):void {
185    var i:int, len:int;
186    len = eles.length;
187    _event._stoped = false;
188    var _target:*;
189    _target = eles[0];
190    var tE:Sprite;
191    for (i = 0; i < len; i++) {
192     tE = eles[i];
193     if (tE.destroyed) return;
194     tE.event(type, _event.setTo(type, tE, _target));
195     if (_event._stoped)
196      break;
197    }
198   }
199 
200   /**
201    * 獲取對象列表。
202    * @param start   起始節點。
203    * @param end 結束節點。
204    * @param rst 返回值。如果此值不為空,則將其賦值為計算結果,從而避免創建新數組;如果此值為空,則創建新數組返回。
205    * @return Array 返回節點列表。
206    */
207   private function getEles(start:Node, end:Node = null, rst:Array = null):Array {
208    if (!rst) {
209     rst = [];
210    } else {
211     rst.length = 0;
212    }
213    while (start && start != end) {
214     rst.push(start);
215     start = start.parent;
216    }
217    return rst;
218   }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM