美文网首页
Laya鼠标事件阅读

Laya鼠标事件阅读

作者: chiguozi | 来源:发表于2018-08-29 09:49 被阅读162次
    1. 点击事件核心类:MouseManagerTouchManager
      MouseManager负责收集相关事件,进行捕获阶段和目标阶段。
      TouchManger负责处理和分发事件,进行冒泡阶段。
      • 捕获阶段:此阶段引擎会从stage开始递归检测stage及其子对象,直到找到命中的目标对象或者未命中任何对象;
      • 目标阶段:找到命中的目标对象;
      • 冒泡阶段:事件离开目标对象,按节点层级向上逐层通知,直到到达舞台的过程。</p>
    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));
    MouseManager:
    private function check(sp:Sprite, mouseX:Number, mouseY:Number, callBack:Function):Boolean {
       this._point.setTo(mouseX, mouseY);
       sp.fromParentPoint(this._point);
       mouseX = this._point.x;
       mouseY = this._point.y;
       
       //如果有裁剪,则先判断是否在裁剪范围内
       var scrollRect:Rectangle = sp.scrollRect;
       if (scrollRect) {
        _rect.setTo(scrollRect.x, scrollRect.y, scrollRect.width, scrollRect.height);
        if (!_rect.contains(mouseX, mouseY)) return false;
       }
       
       //先判定子对象是否命中
       if (!disableMouseEvent) {
        //优先判断父对象
        //默认情况下,hitTestPrior=mouseThrough=false,也就是优先check子对象
        //$NEXTBIG:下个重大版本将sp.mouseThrough从此逻辑中去除,从而使得sp.mouseThrough只负责目标对象的穿透
        if (sp.hitTestPrior && !sp.mouseThrough && !hitTest(sp, mouseX, mouseY)) {
         return false;
        }
        for (var i:int = sp._childs.length - 1; i > -1; i--) {  //倒叙遍历,从外向内检测,如果检测到则跳过内部检测
         var child:Sprite = sp._childs[i];
         //只有接受交互事件的,才进行处理
         if (!child.destroyed && child.mouseEnabled && child.visible) {
          if (check(child, mouseX, mouseY, callBack)) return true;
         }
        }
       }
       
       //避免重复进行碰撞检测,考虑了判断条件的命中率。
       var isHit:Boolean = (sp.hitTestPrior && !sp.mouseThrough && !disableMouseEvent) ? true : hitTest(sp, mouseX, mouseY);
       
       if (isHit) {
        _target = sp;
        callBack.call(this, sp);
       } else if (callBack === onMouseUp && sp === _stage) {
        //如果stage外mouseUP
        _target = _stage;
        callBack.call(this, _target);
       }
       
       return isHit;
      }
      
      private function hitTest(sp:Sprite, mouseX:Number, mouseY:Number):Boolean {
       var isHit:Boolean = false;
       if (sp.scrollRect) {
        mouseX -= sp.scrollRect.x;
        mouseY -= sp.scrollRect.y;
       }
       if (sp.hitArea is HitArea) {
        return sp.hitArea.isHit(mouseX, mouseY);
       }
       if (sp.width > 0 && sp.height > 0 || sp.mouseThrough || sp.hitArea) {
        //判断是否在矩形区域内
        if (!sp.mouseThrough) {
         var hitRect:Rectangle = this._rect;
         if (sp.hitArea) hitRect = sp.hitArea;
         else hitRect.setTo(0, 0, sp.width, sp.height); //坐标已转换为本地坐标系
         isHit = hitRect.contains(mouseX, mouseY);
        } else {
         //如果可穿透,则根据子对象实际大小进行碰撞
         isHit = sp.getGraphicBounds().contains(mouseX, mouseY);
        }
       }
       return isHit;
      }
      
      /**
       * 执行事件处理。
       */
      public function runEvent():void {
       var len:int = _eventList.length;
       if (!len) return;
       
       var _this:MouseManager = this;
       var i:int = 0,j:int,n:int,touch:*;
       while (i < len) {
        var evt:* = _eventList[i];
        
        if (evt.type !== 'mousemove') _prePoint.x = _prePoint.y = -1000000;
        
        switch (evt.type) {
        case 'mousedown': 
         _touchIDs[0] = _id++;
         if (!_isTouchRespond) {
          _this._isLeftMouse = evt.button === 0;
          _this.initEvent(evt);
          _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseDown);
         } else
          _isTouchRespond = false;
         break;
        case 'mouseup': 
         _this._isLeftMouse = evt.button === 0;
         _this.initEvent(evt);
         _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseUp);
         break;
        case 'mousemove': 
         if ((Math.abs(_prePoint.x - evt.clientX) + Math.abs(_prePoint.y - evt.clientY)) >= mouseMoveAccuracy) {
          _prePoint.x = evt.clientX;
          _prePoint.y = evt.clientY;
          _this.initEvent(evt);
          _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseMove);
           //   _this.checkMouseOut();
         }
         break;
        case "touchstart": 
         _isTouchRespond = true;
         _this._isLeftMouse = true;
         var touches:Array = evt.changedTouches;
         for (j = 0, n = touches.length; j < n; j++) {
          touch = touches[j];
          //是否禁用多点触控
          if (multiTouchEnabled || isNaN(_curTouchID)) {
           _curTouchID = touch.identifier;
           //200次点击清理一下id资源
           if (_id % 200 === 0) _touchIDs = {};
           _touchIDs[touch.identifier] = _id++;
           _this.initEvent(touch, evt);
           _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseDown);
          }
         }
         
         break;
        case "touchend": 
        case "touchcancel":
         _isTouchRespond = true;
         _this._isLeftMouse = true;
         var touchends:Array = evt.changedTouches;
         for (j = 0, n = touchends.length; j < n; j++) {
          touch = touchends[j];
          //是否禁用多点触控
          if (multiTouchEnabled || touch.identifier == _curTouchID) {
           _curTouchID = NaN;
           _this.initEvent(touch, evt);
           var isChecked:Boolean;
           isChecked = _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseUp);
           if (!isChecked)
           {
            _this.onMouseUp(null);
           }
          }
         }
         
         break;
        case "touchmove": 
         var touchemoves:Array = evt.changedTouches;
         for (j = 0, n = touchemoves.length; j < n; j++) {
          touch = touchemoves[j];
          //是否禁用多点触控
          if (multiTouchEnabled || touch.identifier == _curTouchID) {
           _this.initEvent(touch, evt);
           _this.check(_this._stage, _this.mouseX, _this.mouseY, _this.onMouseMove);
          }
         }
         break;
        case "wheel": 
        case "mousewheel": 
        case "DOMMouseScroll": 
         _this.checkMouseWheel(evt);
         break;
        case "mouseout": 
         //_this._stage.event(Event.MOUSE_OUT, _this._event.setTo(Event.MOUSE_OUT, _this._stage, _this._stage));
         TouchManager.I.stageMouseOut();
         break;
        case "mouseover": 
         _this._stage.event(Event.MOUSE_OVER, _this._event.setTo(Event.MOUSE_OVER, _this._stage, _this._stage));
         break;
        }
        i++;
       }
       _eventList.length = 0;
      }
     }
    
    TouchManager
    /**
       * 派发事件。
       * @param eles    对象列表。
       * @param type    事件类型。
       * @param touchID (可选)touchID,默认为0。
       */
      private function sendEvents(eles:Array, type:String, touchID:int = 0):void {
       var i:int, len:int;
       len = eles.length;
       _event._stoped = false;
       var _target:*;
       _target = eles[0];
       var tE:Sprite;
       for (i = 0; i < len; i++) {
        tE = eles[i];
        if (tE.destroyed) return;
        tE.event(type, _event.setTo(type, tE, _target));
        if (_event._stoped)
         break;
       }
      }
      
      /**
       * 获取对象列表。
       * @param start   起始节点。
       * @param end 结束节点。
       * @param rst 返回值。如果此值不为空,则将其赋值为计算结果,从而避免创建新数组;如果此值为空,则创建新数组返回。
       * @return Array 返回节点列表。
       */
      private function getEles(start:Node, end:Node = null, rst:Array = null):Array {
       if (!rst) {
        rst = [];
       } else {
        rst.length = 0;
       }
       while (start && start != end) {
        rst.push(start);
        start = start.parent;
       }
       return rst;
      }
      
    

    相关文章

      网友评论

          本文标题:Laya鼠标事件阅读

          本文链接:https://www.haomeiwen.com/subject/ahkhwftx.html