点击穿透 问题记录 同级&父级

前言

为解决项目中的 点击穿透 遇到的各种问题,自己参考网上的文章,做点击的测试,在此记录一下。


捕获&冒泡

节点中的每一次点击都是由根节点一级一级向下传播,匹配到目标节点(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型”传播一遍。

监听函数,就是我们在这个路径上挂的钩子。你在路径的哪个位置挂钩子,事件传播到这儿时,就会触发哪个位置监听函数。


测试

节点目录结构:

image

测试代码:

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()};`
        );  
    }
}

点击任意子字节

可以看到,无论是点击哪个子节点,都会是从父节点传播下来,再冒泡上去的:

那么,如果点击的是子节点重叠部分呢?

就算点击三个节点重叠的位置,结果是一样的,会从父节点传播到点击位置上层的子节点,再由该子节点冒泡上去。

但是,如果我们希望同级节点 child1child2 点击共同区域可以都触发各自的点击事件呢?

吞没事件

我们勾选上 child2 节点的 停止吞没事件

02

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

03

发现事件传播了两轮,分别处理了 child2child1 的点击事件

如果再把 child1停止吞没事件 勾选上,点击 三节点重叠的部分:

04

发现点击部分的三个节点都分别处理了点击的事件

至此,同级节点点击穿透的问题的可以解决了。

停止冒泡

但是还有一个问题,如果我们这时勾选上 father 节点的 停止冒泡 后,再次点击 三节点重叠的部分:

05

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

05

发现 child2 节点 冒泡阶段就已经结束。

那么看来,想要 通过阻止吞没来 触发共同区域同级节点的点击,那么需要 event 事件走完完整的一轮才行。

BlockInputEvents 组件

顺带一提,如果在 child2 节点上挂载上 BlockInputEvents 组件,和勾选 child2 节点 停止冒泡log 信息是一样的。

看来 BlockInputEvents 组件,也是通过阻止冒泡的方式来,阻止点击穿透的。


参考

彻底弄明白事件的捕获和冒泡 - 路泽宇 - 博客园

6赞

点击穿透,我遇到过一个问题是:

// 设置了点击穿透
node._touchListener.setSwallowTouches(false)
  • node 穿透节点的父节点一定不能是 cc.Button 和 cc.BlockInput,不然会被阻挡穿透
1赞

后面有提到,确实是有这个问题

结论是直接上一层 on touchstart 来代替cc.BlockInputEvents?

结论是:如果要通过 阻止吞没 的方式来实现 同级节点穿透 的话, 需要 event 事件走完完整的一轮才行。

若上级节点中存在 cc.BlockInputEvents 或者其他 阻止冒泡 的情况下,会导致 event 走不完一轮。

学习了!:+1: