粗探Openlayers的事件系统

前言

        在之前的博文openlayer, 由一个图标遮盖线段需求引发的思考里,为了研究如何在地图渲染的时候对feature做一些自己的处理,我们对openlayers的地图渲染流程有了一些初步的梳理。但是对于其中的事件系统没有详细梳理。

        有没有思考过,当使用ol.source.Vector的addFeature方法向source添加feature时,layer是如何知道的,地图又是如何知道的并将新添加的feature绘制在地图上,这中间发生了什么呢?这就要说到本次的主题了,openlayers的事件系统。

        openlayers是一个庞大的工程项目,各种类看的人是眼花缭乱,类与类之间的通信交互也是异常复杂,openlayers使用事件系统(发布订阅)来进行通信。

一、ol.Observable类,openlayers事件系统的核心

        首先从类ol.Observable说起,openlayers里只要是需要使用事件的类都继承自这个类,我们来看看它究竟定义了什么

        查看官网的api,解释是:抽象基类;通常仅用于创建子类,而不在应用程序中实例化。一个事件目标,为监听器注册和注销提供了方便的方法。通用change事件始终可通过ol.Observable#changed方法触发。好了,它的作用就是注册、注销监听器,并提供触发的方法。

1.1 如何触发事件,触发事件内部是怎么运作的

        来看看ol.Observable#changed方法做了什么事情

        change方法内部逻辑是增加了内部计数并调用了dispatchEvent方法,dispatchEvent这个方法来自哪里呢?ol.Observable继承自ol.events.EventTarget类, ol.events.EventTarget类中定义了dispatchEvent方法。我们看看这个方法做了什么

        ol.events.EventTarget#dispatchEvent方法核心就是取出本类所有符合type的listeners并执行listener方法(还有事件冒泡的处理),这样触发事件时,所有符合的监听器就会执行。

1.2 如何注册监听器、监听器是如何工作的

        ol.Observable类可以用on, once来注册监听器。下面是on方法的具体实现

        又衍生出了一个辅助类ol.events,使用ol.events.listen来实现注册监听器的具体逻辑。

1.2.1 ol.events,一个辅助监听器注册,注销的工具类

        上面说的ol.events.listen,这个方法实现如下

        这个方法是核心,它做了几件事情

  1. 根据target参数使用ol.events.getListenerMap_方法来获取listeners
  2. 根据listenser参数来查找是否已经存在,如果不存在则将listener注册到target上:target.addEventListener(type, ol.events.bindListener_(listenerObj))

        其中可能会让人疑惑的是这个target,这个target就是继承自ol.Observable的类,addEventListener方法是ol.events.EventTarget.prototype.addEventListener(之前说过ol.Observable继承自ol.events.EventTarget),又绕回来了,下面是ol.events.EventTarget.prototype.addEventListener方法实现

        addEventListener方法是将listener注册到target类的内部listeners_里面,它是一个map,其key就是type(事件类型),value就是一个listener数组。

        

二、ol.source.Vector里的事件流转

        接下来我们来看看ol.source.Vector类的继承关系。为了简单表示,使用 -> 表示继承自哪个类。


ol.source.Vector  ->  ol.source.Source  ->  ol.Object  ->  ol.Observable  ->  ol.events.EventTarget


        这么看来ol.source.Vector也具备了ol.Observable事件能力。切回正题,调用ol.source.Vector的addFeature方法发生了什么

        原来是内部添加feature和触发changed事件,首先,this.addFeatureInternal(feature) 干了什么

        addFeatureInternal将添加的feature放到自己内部的Rtree数据结构中,然后触发了下addfeature事件,所以如果你用你的vectorSource来监听addfeature事件就能监听到了,比如这样 vectorSource.on('addfeature', evt => { handleAddFeature(evt.feature) }),这个方法和本次讨论的无太大关系,先跳过。

        this.changed()是什么?上文说过ol.source.Vector继承自ol.Observable,其实这个就是是调用的ol.Observable#changed方法,上文说的changed方法内部其实也是调用this.dispatchEvent(ol.events.EventType.CHANGE),触发了一个ol.events.EventType.CHANGE事件,这和layer有什么关系呢,接着往下看。

三、ol.layer.Vector是如何监听ol.source.Vector里的CHANGE事件的,之后又做了什么?

        首先,分析下ol.layer.Vector的继承关系。为了简单表示,使用 -> 表示继承自哪个类。


        ol.layer.Vector  ->  ol.layer.Layer  ->  ol.layer.Base  ->  ol.Object  ->  ol.Observable  ->  ol.events.EventTarget


        在ol.layer.Layer的构造方法里监听了事件ol.layer.Property.SOURCE

         ol.layer.Property.SOURCE事件又是又谁在什么时候去触发的呢?可以看到ol.layer.Layer的构造方法里将构造方法参数option里的source使用了this.setSource(source)来将传入的source设置到内部

        里面调用了this.set,这个方法又是哪来的?上面的类的继承关系里标红ol.Object的就是,ol.Object类里有一个set方法,在这个方法里,将传入的value保存到内部的values_里(是一个map)

               

        在确保set的value是一个新的后,就调用了this.notify(key, oldValue),看看里面做了什么

        原来是将key做为事件类型,触发了一个事件。想一下,对于 setSource方法,事件类型是什么,没错,就是ol.layer.Property.SOURCE。这样就对应上了ol.layer.Layer的构造方法里监听了事件ol.layer.Property.SOURCE。也就是说给layer添加或者修改其Source时,都会触发ol.layer.Property.SOURCE事件

        搞清楚了是如何触发的,看看监听器是怎么工作的,在添加或修改source事会执行监听器方法ol.layer.Layer.prototype.handleSourcePropertyChange_

        里面非常重要的代码

if (source) {
    this.sourceChangeKey_ = ol.events.listen(source,
        ol.events.EventType.CHANGE, this.handleSourceChange_, this);
}

         使用了 ol.events.listen监听了ol.source.Vector类对象的的CHANGE事件,这个事件是如何触发的?对,就是ol.source.Vector类里调用this.changed()的时候。忘了的可以回头看下第二节,是在调用addFeature方法内部调用了this.changed()。也就是说当ol.source.Vector类添加feature时,ol.layer.Vector监听到source的CHANGE事件,并且执行监听器ol.layer.Layer.prototype.handleSourceChange_,接下来看看这个方法里做了什么

        很简单,又是this.changed(),不要绕晕了,这次是ol.layer.Layer的CHANGE事件了,我们看看是怎么监听的,上面ol.layer.Layer的构造方法里有一句this.setMap(option.map),忘了的可以回头看下

if (options.map) {
    this.setMap(options.map);
}

        就在setMap方法里监听了本layer对象的CHANGE事件

        看上面红框里的代码 ,

ol.events.listen( this, ol.events.EventType.CHANGE, map.render, map),监听事件的target就是this,也就是本layer对象。执行的方法是map.render,执行map.render方法的context上下文就是map。也就是执行map.render,让地图进行渲染,至此,分析结束。最后,总结一下

  1. ol.source.Vector和ol.layer.Layer都是ol.Observable的子孙类,具备ol.Observable的事件能力。
  2. 调用ol.source.Vector#addFeature时,会调用this.changed(),其实际意义是触发ol.source.Vector实例对象上的ol.events.EventType.CHANGE事件。
  3. 实例化ol.layer.Vector时,会调用其父类ol.layer.Layer的构造方法,在ol.layer.Layer的构造方法里监听了新增或者修改ol.source.Vector,其事件类型为ol.layer.Property.SOURCE。
  4. 在对ol.layer.Layer新增或修改ol.source.Vector时,ol.layer.Layer会监听ol.source.Vector的ol.events.EventType.CHANGE事件,结果是执行map.render方法,触发地图重绘。所以添加一个feature到ol.source.Vector里时,会触发地图的重绘逻辑,整个流程分析完毕。

四、小结

        openlayers里的事件系统的核心能力由ol.Observable类提供,需要事件能力的类都是直接或者间接继承自这个类,比较让人晕的是,每个类里都在调用this.changed()触发CHANGE事件。要区分它们,可以看ol.events.listen方法,看第一个参数target是谁,target就是监听的CHANGE方法的目标对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值