egret事件流(捕获,目标,冒泡)的实现

场景

假设group1是group2的父级,group2是group3的父级,我点击group3区域,那按html事件流应该是
group1->group2->group3->group3->group2->group1
egret事件流(捕获,目标,冒泡)的实现
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;
                }
            }
        }
上一篇:Egret*开发者—李昌平


下一篇:Egret使用 VSCode 作为集成开发环境下的部署与安装