zoukankan      html  css  js  c++  java
  • 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   }
  • 相关阅读:
    第十五篇:在SOUI中消息通讯
    为GDI函数增加透明度处理
    第十四篇:在SOUI中使用定时器
    第十三篇:在SOUI中使用有窗口句柄的子窗口
    第十二篇:SOUI的utilities模块为什么要用DLL编译?
    第十一篇:SOUI系统资源管理
    第十篇:扩展SOUI的控件及绘图对象(ISkinObj)
    第九篇:在SOUI中使用多语言翻译
    第八篇:SOUI中控件事件的响应
    Linked List Cycle
  • 原文地址:https://www.cnblogs.com/chiguozi/p/9490474.html
Copyright © 2011-2022 走看看