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   }

 

posted @ 2018-08-16 22:15  chiguozi  阅读(6170)  评论(0编辑  收藏  举报