Laya鼠标事件阅读
- 点击事件核心类:
MouseManager和TouchManager。
MouseManager负责收集相关事件,进行捕获阶段和目标阶段。
TouchManger负责处理和分发事件,进行冒泡阶段。- 捕获阶段:此阶段引擎会从stage开始递归检测stage及其子对象,直到找到命中的目标对象或者未命中任何对象;
- 目标阶段:找到命中的目标对象;
- 冒泡阶段:事件离开目标对象,按节点层级向上逐层通知,直到到达舞台的过程。
- 事件是由Canvas(浏览器控件等)发起,在
MouseManager中注册处理。 MouseManager在监听到事件后,会将事件添加到队列中,在stage进入下一帧时(Stage._loop),一次性处理完所有事件。- 捕获、目标阶段核心逻辑:(从根节点开始,向子节点查找)
- 初始化Event。MouseManager维护一个唯一的Event对象,保留鼠标事件相关信息,如target、touchid等。
- 先判断sp是否有scrollRect,如果则scrollRect外,直接判定为没点到,返回false。
- 优先检测(
HitTestPrior),并且没有点击到,则直接返回false。(width>0并且没有点击穿透的View,默认会被设置为HitTestPrior = true) - 命中检测逻辑(
hitTest)- 参数为被判断的sp和转换为相对与sp的鼠标坐标(通过
fromParentPoint方法) - 如果有scrollRect,则先偏移鼠标点击位置。
- 如果sp的
hitArea字段不为空,则返回sp的hitArea.isHit结果。 - 如果
mouseThrough为true,则检测子对象的实际大小进行碰撞。否则就使用(0,0,width,height)的矩形检测点是否在矩形内。
- 参数为被判断的sp和转换为相对与sp的鼠标坐标(通过
- 倒叙遍历子对象,从外向内检测,检测到后,直接跳过内部检测。
- 将目标对象和Event对象传递给
TouchManager。
- 冒泡阶段核心逻辑:(从子节点开始,向根节点查找)
- 获取或创建的点击信息,用于检测拖拽、双击等。
- 从当前sp开始,递归收集父节点,按顺序放入数组中,子节点在前,父节点在后。
- 按照数组属性,对节点发送相关事件,如果事件被阻断(
event.stopPropagation),则直接跳过所有父节点。事件的currentTarget为最先被点击的sp。
- 鼠标事件触发后,参数默认为
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鼠标事件阅读的更多相关文章
- DuiLib事件分析(一)——鼠标事件响应
最近在处理DuiLib中自定义列表行元素事件,因为处理方案得不到较好的效果,于是只好一层一层的去剥离DuiLib事件是怎么来的,看能否在某一层截取消息,自己重写. 我这里使用CListContaine ...
- Python游戏引擎开发(五):Sprite精灵类和鼠标事件
本次来实现Sprite类和鼠标事件. 说起这个Sprite啊,涉及过2D游戏研究领域的看官应该都听说过它. 它中文原意是"精灵",只是在不同人的眼中,它所表示的意义不同. 比方说在 ...
- win10 支持默认把触摸提升鼠标事件 打开 Pointer 消息
原文:win10 支持默认把触摸提升鼠标事件 打开 Pointer 消息 在 WPF 经常需要重写一套触摸事件,没有UWP的Pointer那么好用. 如果一直都觉得 WPF 的触摸做的不好,或想解决 ...
- 7.JAVA之GUI编程鼠标事件
鼠标事件: 功能: 1.基本窗体功能实现 2.鼠标移动监听,当鼠标移动到按钮上时,触发打印事件. 3.按钮活动监听,当按钮活动时,触发打印事件. 4.按钮被单击时触发打印事件. 源码如下: impor ...
- 手持设备点击响应速度,鼠标事件与touch事件的那些事
前言 现在一直在做移动端的开发,这次将单页应用的网页内嵌入了app,于是老大反映了一个问题:app应用点击响应慢!我开始不以为然,于是拿着网页版的试了试,好像确实有一定延迟,于是开始了研究,最后选择了 ...
- css屏蔽元素的鼠标事件pointer-events
// 屏蔽点击 $('body').css('pointer-events', 'none'); //恢复默认 $('body').css('pointer-events', 'auto'); 用 ...
- 深入学习jQuery鼠标事件
× 目录 [1]类型 [2]写法 [3]合成事件[4]鼠标按键[5]修改键[6]坐标位置 前面的话 鼠标事件是DOM事件中最常用的事件,jQuery对鼠标事件进行了封装和扩展.本文将详细介绍jQuer ...
- 深入理解DOM事件类型系列第一篇——鼠标事件
× 目录 [1]类型 [2]顺序 [3]坐标位置[4]修改键[5]相关元素[6]鼠标按键[7]滚轮事件[8]移动设备 前面的话 鼠标事件是web开发中最常用的一类事件,毕竟鼠标是最主要的定位设备.本文 ...
- winform/窗体鼠标事件编程中的几个问题
1.进行.net窗体的开发,经常用到鼠标事件,如MouseDown/MouseUp/MouseMove/MouseClick等.可是有时候给控件添加鼠标事件,就是不响应,怎么办呢! 答案:1.控件是否 ...
随机推荐
- 如何在linux centos 环境下运行.exe文件
linux是不能运行window下的可执行文件的,必须借助于wine.百度了以下wine如下: Wine (“Wine Is Not an Emulator” 的递归缩写)是一个能够在多种 POS ...
- SQL SERVER解析Json
外包的项目,有很多信息存储在JSON中,无论是查询还是修改信息都十分麻烦.找了一些实用的SQL Function去解析,并附修改例子. 使用过程: 1. 需要在SQL新建自定义类型 table: Hi ...
- django复习-3-请求与响应
一.请求request 前端向后端传递参数有几种方式? 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取: "http://127. ...
- myeclipse10配置maven
一:Maven的下载安装 准备工作: 1)安装环境 Windows xp 2)需安装JDK ,并配置环境变量(略) 3) Maven版本3.0.5 4)下载地址:http://mirror.bit.e ...
- Effective MySQL之SQL语句最优化——读书笔记之二
第二章,基本的分析命令 本章简单介绍了如下几个基本的MySQL分析命令: EXPLAIN命令 生成QEP不是确定的 QEP不会绑定给一个SQL或者存储过程,而是在执行的时候根据实际情况生成 可以通过Q ...
- CentOS 7的安装
一.引导系统之后 界面说明: Install CentOS 7 安装CentOS 7 Test this media & install CentOS 7 测试安装文件并安装CentOS ...
- ceph kubernetes中使用
1.在管理节点上,进入刚创建的放置配置文件的目录,用 ceph-deploy 执行如下步骤 mkdir /opt/cluster-ceph cd /opt/cluster-ceph ceph-depl ...
- rest_framework源码分析
CBV&APIView '''原生django as_view方法''' class View(object): http_method_names = ['get', 'post', 'pu ...
- Java使用线程并发库模拟弹夹装弹以及发射子弹的过程
同样是从网上看到的一个需求,需求描述都在代码中. 不多说了,直接贴代码了.相信大家都能够看得懂的! package cn.yw.bore; import java.util.ArrayList; im ...
- HDU 2647 Reward(拓扑排序,vector实现邻接表)
Reward Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...