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));
 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鼠标事件阅读的更多相关文章

  1. DuiLib事件分析(一)——鼠标事件响应

    最近在处理DuiLib中自定义列表行元素事件,因为处理方案得不到较好的效果,于是只好一层一层的去剥离DuiLib事件是怎么来的,看能否在某一层截取消息,自己重写. 我这里使用CListContaine ...

  2. Python游戏引擎开发(五):Sprite精灵类和鼠标事件

    本次来实现Sprite类和鼠标事件. 说起这个Sprite啊,涉及过2D游戏研究领域的看官应该都听说过它. 它中文原意是"精灵",只是在不同人的眼中,它所表示的意义不同. 比方说在 ...

  3. win10 支持默认把触摸提升鼠标事件 打开 Pointer 消息

    原文:win10 支持默认把触摸提升鼠标事件 打开 Pointer 消息 在 WPF 经常需要重写一套触摸事件,没有UWP的Pointer那么好用. 如果一直都觉得 WPF 的触摸做的不好,或想解决 ...

  4. 7.JAVA之GUI编程鼠标事件

    鼠标事件: 功能: 1.基本窗体功能实现 2.鼠标移动监听,当鼠标移动到按钮上时,触发打印事件. 3.按钮活动监听,当按钮活动时,触发打印事件. 4.按钮被单击时触发打印事件. 源码如下: impor ...

  5. 手持设备点击响应速度,鼠标事件与touch事件的那些事

    前言 现在一直在做移动端的开发,这次将单页应用的网页内嵌入了app,于是老大反映了一个问题:app应用点击响应慢!我开始不以为然,于是拿着网页版的试了试,好像确实有一定延迟,于是开始了研究,最后选择了 ...

  6. css屏蔽元素的鼠标事件pointer-events

    // 屏蔽点击 $('body').css('pointer-events', 'none'); //恢复默认 $('body').css('pointer-events', 'auto');   用 ...

  7. 深入学习jQuery鼠标事件

    × 目录 [1]类型 [2]写法 [3]合成事件[4]鼠标按键[5]修改键[6]坐标位置 前面的话 鼠标事件是DOM事件中最常用的事件,jQuery对鼠标事件进行了封装和扩展.本文将详细介绍jQuer ...

  8. 深入理解DOM事件类型系列第一篇——鼠标事件

    × 目录 [1]类型 [2]顺序 [3]坐标位置[4]修改键[5]相关元素[6]鼠标按键[7]滚轮事件[8]移动设备 前面的话 鼠标事件是web开发中最常用的一类事件,毕竟鼠标是最主要的定位设备.本文 ...

  9. winform/窗体鼠标事件编程中的几个问题

    1.进行.net窗体的开发,经常用到鼠标事件,如MouseDown/MouseUp/MouseMove/MouseClick等.可是有时候给控件添加鼠标事件,就是不响应,怎么办呢! 答案:1.控件是否 ...

随机推荐

  1. Chrome 无法加载Shockwave Flash

    遇到的问题 Chrome经常出现上图的提示,把Adobe Flash重装了N多次也是无法解决此问题,经多次尝试终于解决此问题. 解决方法 1.在Chrome地址栏输入:chrome://plugins ...

  2. wc 命令使用说明

    wc 命令 使用说明 wc 命令还是很是简单的,通过 man 命令,可以见到可以选择的选项: wc option file 并且 wc 命令支持 管道操作 其中较为常用的命令选项 -c 字符的个数 - ...

  3. Hadoop 命令 && Web UI

    0. 说明 Hadoop 命令合集 && Web UI 1. HDFS 命令 [1.0 启动所有 && 关闭所有进程] 相当于启动 HDFS 和 YARN # 启动所有 ...

  4. 【洛谷】【单调栈】P1823 音乐会的等待

    [题目描述:] N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见 ...

  5. Jmeter遇到线程链接被重置(Connection reset by peer: socket write error)的解决方法

    做性能测试的时候遇到一个很奇怪的问题,多线程的计划,有一个线程第一次能跑过,第二次确跑不过,单独跑这个线程跑多少次都没有问题,把思考时间改短也没有问题,唯独出现在特定的状态下,特定状态是啥,也不得而知 ...

  6. python is、==区别;with;gil;python中tuple和list的区别;Python 中的迭代器、生成器、装饰器

    1. is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同 == 比较的是两个对象的内容是否相等 2. with语句时用于对try except finally 的优 ...

  7. 20165302Exp0 Kali安装 Week1

    一,下载 下载网址https://www.kali.org/downloads/ 二,安装(安装过程中有一部分没有截图,因此没有贴上) 创建虚拟机 选择Linux,版本Ubuntu 一直下一步,最后点 ...

  8. std::lexicographical_compare函数的使用

    按照词典序比较前者是否小于后者. 当序列<first1, last1>按照字典序比较小于后者序列<first2, last2>,则返回true.否则,返回false. 所谓字典 ...

  9. PAT B1002 写出这个数

    读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值.这里保证n小于10100. 输出格式:在一行内输出n的各位数字之和的每 ...

  10. jqgrid 单击行启用行编辑,切换行保存原编辑行

    为了加速表格互动编辑,我们往往希望通过选中行就触发了行编辑,完成行编辑后,再选中另一个行做编辑,同时上一个编辑行被自动保存,直至完成需要的编辑内容. 页面效果可能如下: 1)设置需要编辑的列 edit ...