这两天正在愁公众号写点什么,打开微信看到uikiller老用户「悦雨」遇到了一个问题:
地图拖动与子节点触摸事件产生冲突,表现为在子节点上拖动,地图不能动
一句话不太好描述问题,在征得「悦雨」同意后,将这次交流的内容截图出来:
在与「悦雨」的交流过程中,我用ScrollView + TileMap + Button花了五分钟做了一个小测试,将TiledMap放在ScrollView中,在TiledMap中又放值了一个按钮,验证了一下曾经的经验是否任然有效,结果是OK的,于是将测试场景发给了「悦雨」。
有了这个案例今天就以这个地图场景为例,看看不写代码,利用引擎内置组件,如何快速实现一个原型或组件功能测试 ,请看下面视频:
【插入视频】
为什么在ScrollView中拖动,不会触发子节点的TOUCH事件呢?先看下ScrollView上的一个关键属性:
有了Cancel Inner Event这个线索,我们直接从ScrollView组件源码入手,看看它是什么实现的。
以cc.ScrollView组件为例,看如何定位组件源码:
选择CCScrollView.js文件,自动跳转到Sources标签,打开文件内容,键入ctrl + f 或 cmd + f 在当前文件中搜索:cancelInnerEvents,找到关键代码:
this.cancelInnerEvents
变量为真可能会执行到下面的代码,设置成员变量this._touchMoved = true
this._stopPropagationIfTargetIsMe(event)
它是在有条件地停止TOUCH_MOVE事件的传播。通过上面的分析,再通过断点跟踪,在ScrollView和Button组件中分别打上断点,我们在Button组件上做点击,ScrollView组件的_onTouchEnded居然先被断下来,它是怎么做到的呢?
在CCScrollView.js源码中搜“TOUCH_END”关键字,找到TOUCH事件注册的代码:
看看这里有与自己平时注册TOUCH事件有什么不同?相信你已经发现了,关键在最后一个参数:useCapture,用于是否捕获子节点事件,又称之为向下冒泡(默认是向上冒泡),下面以TOUC_END事件为例,简单说明一下:
this.node.on(
cc.Node.EventType.TOUCH_END, //触摸事件类型
this._onTouchEnded, //事件处理函数
this, //事件处理函数的this上下文(使用箭头函数时通常被省略)
true //是否捕获子节点Touch事件
);
为了帮助大家更好地理解,我做了个简单的小组件,请看代码:
cc.Class({
extends: cc.Component,
properties: {
useCapture: false, //是否启用捕获
},
onLoad () {
this.node.on(
cc.Node.EventType.TOUCH_END,
() => cc.log('touchend', this.node.name), //测试时观察日志输入出
this,
this.useCapture
);
}
});
把这个组件挂到两个父子关系的节点上,在父节点上开启捕获,看下面截图:
运行点击红色节点,看看日志输出:
从日志中看到白色节点先响应,然后是红色节点,我们把白色父节点的UseCapture关闭,再看看日志输出:
这次是红色子节点先响应,白色父节点后响应,更多细节可以参考Cocos Creator官方文档:https://docs.cocos.com/creator/manual/zh/scripting/internal-events.html?h=冒泡
还有对应的官方范例:TouchPropagation
这次除了教程,还想再聊一个事情,经常会有同学通过微信、QQ、公众号向Shawn咨询问题,首先感谢大家对shawn的信任,如果是在自己的能力范围内且对大家帮助的内容,Shawn一定真诚对待,这也正是「奎特尔星球」内容的重要来源。但是因为个人能力和时间有限,不是每一个人的问题Shawn都能解答,还望大家见谅。
微信、QQ很容易让人在工作时分心,一般我在做事的时候会将手机静音或离远一点,公众号上偶尔也收有留言,但有时会忘记去公众号上查看,超过24小时的留言,看到了想回复也无没办法,很是无奈。为了能把公众号做好,Shawn特地定制了一个邮箱:[email protected],如有问题讨论或投稿欢邮件联系,公众号需要大家的支持和帮助,愿我们一起成长!