前言
在之前的博文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,这个方法实现如下
这个方法是核心,它做了几件事情
- 根据target参数使用ol.events.getListenerMap_方法来获取listeners
- 根据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,让地图进行渲染,至此,分析结束。最后,总结一下
- ol.source.Vector和ol.layer.Layer都是ol.Observable的子孙类,具备ol.Observable的事件能力。
- 调用ol.source.Vector#addFeature时,会调用this.changed(),其实际意义是触发ol.source.Vector实例对象上的ol.events.EventType.CHANGE事件。
- 实例化ol.layer.Vector时,会调用其父类ol.layer.Layer的构造方法,在ol.layer.Layer的构造方法里监听了新增或者修改ol.source.Vector,其事件类型为ol.layer.Property.SOURCE。
- 在对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方法的目标对象。