场景
假设group1是group2的父级,group2是group3的父级,我点击group3区域,那按html事件流应该是
group1->group2->group3->group3->group2->group1
egret基于canvas,不能使用html的事件流,egret的实现如下
1,先找到目标,此处为group3
2,遍历父级,存入列表,得到的list为【group3,group2,group1】
let list: DisplayObject[] = [];
while (target) {
list.push(target);
target = target.$parent;
}
3,对list反转得到captureList为【group1,group2,group3】
let captureList = list.concat();
captureList.reverse();//使用一次reverse()方法比多次调用unshift()性能高。
4.拼接到一起,得到【group1,group2,group3,group3,group2,group1】,此时事件流列表已经拿到了,只要依次触发就行
list = captureList.concat(list);
egret的源码如下
/**
* @private
* 获取事件流列表。注意:Egret框架的事件流与Flash实现并不一致。
*
* 事件流有三个阶段:捕获,目标,冒泡。
* Flash里默认的的事件监听若不开启useCapture将监听目标和冒泡阶段。若开始capture将只能监听捕获当不包括目标的事件。
* 可以在Flash中写一个简单的测试:实例化一个非容器显示对象,例如TextField。分别监听useCapture为true和false时的鼠标事件。
* 点击后将只有useCapture为false的回调函数输出信息。也就带来一个问题「Flash的捕获阶段不能监听到最内层对象本身,只在父级列表有效」。
*
* 而HTML里的事件流设置useCapture为true时是能监听到目标阶段的,也就是目标阶段会被触发两次,在捕获和冒泡过程各触发一次。这样可以避免
* 前面提到的监听捕获无法监听目标本身的问题。
*
* Egret最终采用了HTML里目标节点触发两次的事件流方式。
*/
$getPropagationList(target: DisplayObject): DisplayObject[] {
let list: DisplayObject[] = [];
while (target) {
list.push(target);
target = target.$parent;
}
let captureList = list.concat();
captureList.reverse();//使用一次reverse()方法比多次调用unshift()性能高。
list = captureList.concat(list);
return list;
}
/**
* @private
*/
$dispatchPropagationEvent(event: Event, list: DisplayObject[], targetIndex: number): void {
let length = list.length;
let captureIndex = targetIndex - 1;
for (let i = 0; i < length; i++) {
let currentTarget = list[i];
event.$currentTarget = currentTarget;
if (i < captureIndex)
event.$eventPhase = EventPhase.CAPTURING_PHASE;
else if (i == targetIndex || i == captureIndex)
event.$eventPhase = EventPhase.AT_TARGET;
else
event.$eventPhase = EventPhase.BUBBLING_PHASE;
currentTarget.$notifyListener(event, i < targetIndex);
if (event.$isPropagationStopped || event.$isPropagationImmediateStopped) {
return;
}
}
}