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. gitlab配置push -f 关闭

    默认路径是/var/opt/gitlab/git-data/repositories/组/库 修改conf 文件 [core] repositoryformatversion = 0 filemode ...

  2. (转)glew的安装

    http://blog.sina.com.cn/s/blog_858820890100vbys.html 下载链接: https://sourceforge.net/project/downloadi ...

  3. ansible 增加本机/etc/hosts 下hostsname 与IP

    --- - hosts: all vars: IP: "{{ ansible_eth0['ipv4']['address'] }}" tasks: - name: 将原有的host ...

  4. 团队作业7——第二次项目冲刺(Beta版本)day1

    项目成员:  曾海明(组长):201421122036 于波(组员):201421122058 蓝朝浩(组员):201421122048 王珏 (组员):201421122057 叶赐红(组员):20 ...

  5. Redis系列九:redis集群高可用

    Redis集群的概念: RedisCluster是redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当一个服务挂了可以快速的切换到另外一个服务,当遇到单机内存. ...

  6. java使用纯命令行打包项目

    1: javac -d 编译之后的class文件输出目录   指定源文件位置即可.例如 对于多个包下面的源码编译,貌似javac不支持迭代编译,可能需要一次传入多个源码位置进行编译.一种便捷方法就是使 ...

  7. Odoo作为App后端时如何调试App

    转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/9307340.html  一:Odoo可以作为app后台+后台管理系统使用 Odoo作为一个可供二次开发的框架, ...

  8. java通过http服务执行shell命令

    服务端代码/** * 执行shell命令 * @param command 执行命令 * @return */public String exeCommandByPath( String comman ...

  9. shiro实战系列(九)之Web

    一.Configuration(配置) 将 Shiro 集成到任何 Web 应用程序的最简单的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何读取 Shir ...

  10. PAT B1033 旧键盘打字 (20 分)

    旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现.现在给出应该输入的一段文字.以及坏掉的那些键,打出的结果文字会是怎样? 输入格式: 输入在 2 行中分别给出坏掉的那些键.以及应该输入 ...