前言
为解决项目中的 点击穿透 遇到的各种问题,自己参考网上的文章,做点击的测试,在此记录一下。
捕获&冒泡
节点中的每一次点击都是由根节点一级一级向下传播,匹配到目标节点(target) 后,然后再一级一级的向上冒泡。在向下一级一级传播时,称为 捕获阶段 ,向上则称为 冒泡阶段。
如果 注册事件 是在 捕获阶段,那么在向下传播的过程中就会触发:
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this, true);
如果 注册事件 是在 冒泡阶段,那么则会在向上一级一级冒泡过程中触发:
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this, false);
如图,钩子是注册的监听函数(也称为钩子函数)
任何一个点击,都会以这样一个“U型”传播一遍。
监听函数,就是我们在这个路径上挂的钩子。你在路径的哪个位置挂钩子,事件传播到这儿时,就会触发哪个位置监听函数。
测试
节点目录结构:

测试代码:
const {ccclass, property} = cc._decorator;
@ccclass
export default class OnClick extends cc.Component {
@property({displayName: "停止冒泡事件"}) stopPropagation: boolean = false;
@property({displayName: "停止吞没事件"}) stopSwallowEvent: boolean = false;
start () {
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart_UseCapture, this, true);
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
if (this.stopSwallowEvent) {
this.node["_touchListener"].setSwallowTouches(false);
}
}
onTouchStart_UseCapture(event: cc.Event.EventTouch) {
console.log(`%c${this.node.name}`, `color: #${this.node.color.toHEX()};`, ` 捕获阶段`, );
console.log(
`target:%c ${event.target.name}, %c currentTarget: %c${event.currentTarget.name}`,
`color: #${event.target.color.toHEX()};`,
``,
`color: #${event.currentTarget.color.toHEX()};`
);
}
onTouchStart(event: cc.Event.EventTouch) {
console.log(`%c${this.node.name}`, `color: #${this.node.color.toHEX()};`, ` 冒泡阶段`);
if (this.stopPropagation) {
event.stopPropagation();
}
console.log(
`target:%c ${event.target.name}, %c currentTarget: %c${event.currentTarget.name}`,
`color: #${event.target.color.toHEX()};`,
``,
`color: #${event.currentTarget.color.toHEX()};`
);
}
}
点击任意子字节
可以看到,无论是点击哪个子节点,都会是从父节点传播下来,再冒泡上去的:
那么,如果点击的是子节点重叠部分呢?
就算点击三个节点重叠的位置,结果是一样的,会从父节点传播到点击位置上层的子节点,再由该子节点冒泡上去。
但是,如果我们希望同级节点 child1、child2 点击共同区域可以都触发各自的点击事件呢?
吞没事件
我们勾选上 child2 节点的 停止吞没事件

这时再次点击 三节点重叠的部分:

发现事件传播了两轮,分别处理了 child2、child1 的点击事件
如果再把 child1 的 停止吞没事件 勾选上,点击 三节点重叠的部分:

发现点击部分的三个节点都分别处理了点击的事件
至此,同级节点点击穿透的问题的可以解决了。
停止冒泡
但是还有一个问题,如果我们这时勾选上 father 节点的 停止冒泡 后,再次点击 三节点重叠的部分:

发现和之前不同,这次在 father 节点 冒泡阶段就结束了。为了验证我们再把 child2 节点的 停止冒泡 勾选,点击 三节点重叠的部分:

发现 child2 节点 冒泡阶段就已经结束。
那么看来,想要 通过阻止吞没来 触发共同区域同级节点的点击,那么需要 event 事件走完完整的一轮才行。
BlockInputEvents 组件
顺带一提,如果在 child2 节点上挂载上 BlockInputEvents 组件,和勾选 child2 节点 停止冒泡 的 log 信息是一样的。
看来 BlockInputEvents 组件,也是通过阻止冒泡的方式来,阻止点击穿透的。


