实现原理
不管是上一节中的哪一种交互方式,本质上都是通过监听事件,来处理相应的业务的,在ol.Map
的实现代码中,有下面的代码:
/**
* @private
* @type {ol.MapBrowserEventHandler}
*/
this.mapBrowserEventHandler_ = new ol.MapBrowserEventHandler(this);
for (var key in ol.MapBrowserEvent.EventType) { // 遍历所有的事件类型
ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEvent.EventType[key],
this.handleMapBrowserEvent, this); // 监听事件,函数handleMapBrowserEvent为事件响应函数
}
......
/**
* @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle.
*/
ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
if (!this.frameState_) {
// With no view defined, we cannot translate pixels into geographical
// coordinates so interactions cannot be used.
return;
}
this.focus_ = mapBrowserEvent.coordinate;
mapBrowserEvent.frameState = this.frameState_;
var interactions = this.getInteractions();
goog.asserts.assert(interactions !== undefined,
'interactions should be defined');
var interactionsArray = interactions.getArray();
var i;
if (this.dispatchEvent(mapBrowserEvent) !== false) { // 注意:如果事件处理返回false,交互类就不起作用了
for (i = interactionsArray.length - 1; i >= 0; i--) { // 遍历所有的交互方式
var interaction = interactionsArray[i];
if (!interaction.getActive()) {
continue;
}
var cont = interaction.handleEvent(mapBrowserEvent); // 让各个交互类处理响应的事件
if (!cont) {
break;
}
}
}
};
至于每一个交互类如何处理,就和业务相关了,再此之前,先看一下具体有哪些事件:
/**
* Constants for event names.
* @enum {string}
*/
ol.MapBrowserEvent.EventType = {
/**
* A true single click with no dragging and no double click. Note that this
* event is delayed by 250 ms to ensure that it is not a double click.
* @event ol.MapBrowserEvent#singleclick
* @api stable
*/
SINGLECLICK: 'singleclick',
/**
* A click with no dragging. A double click will fire two of this.
* @event ol.MapBrowserEvent#click
* @api stable
*/
CLICK: ol.events.EventType.CLICK,
/**
* A true double click, with no dragging.
* @event ol.MapBrowserEvent#dblclick
* @api stable
*/
DBLCLICK: ol.events.EventType.DBLCLICK,
/**
* Triggered when a pointer is dragged.
* @event ol.MapBrowserEvent#pointerdrag
* @api
*/
POINTERDRAG: 'pointerdrag',
/**
* Triggered when a pointer is moved. Note that on touch devices this is
* triggered when the map is panned, so is not the same as mousemove.
* @event ol.MapBrowserEvent#pointermove
* @api stable
*/
POINTERMOVE: 'pointermove',
POINTERDOWN: 'pointerdown',
POINTERUP: 'pointerup',
POINTEROVER: 'pointerover',
POINTEROUT: 'pointerout',
POINTERENTER: 'pointerenter',
POINTERLEAVE: 'pointerleave',
POINTERCANCEL: 'pointercancel'
};
这些事件在ol.Map
的API文档里面,有些是能看到的,就是注释里面有@api
的部分,其他的是暂时内部使用的。 按照这个逻辑,我们也可以实现自己的交互方式。 但有一点需要注意,ol.Map
处理事件是有先后顺序的,注意看最前面的那段代码的注释,ol.Map
会先派发事件给自己的监听器,然后才会把事件给interaction
类处理。 如果前面的事件监听器返回false
,那么后面的交互类就不会起作用。 比如像下面这样,用鼠标左键双击地图,并不能放大地图:
为什么? 注意看代码:
<div id="map" style="width: 100%"></div>
<script type="text/javascript">
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
view: new ol.View({
center: ol.proj.transform(
[104, 30], 'EPSG:4326', 'EPSG:3857'),
zoom: 10
})
});
// 监听了dblclick事件,并返回了false
map.on('dblclick', function(event){
return false;
});
</script>
这样做了后,后果非常的严重,出问题后,可能还不知道为什么? 所以建议不要轻易在MapBrowserEvent
事件的监听器里面返回false
。
到此,我们可以进一步分析一下ol.interaction
相关交互类的内部实现了,以ol.interaction.DoubleClickZoom
为例,其核心必然是处理事件:
ol.interaction.DoubleClickZoom.handleEvent = function(mapBrowserEvent) {
var stopEvent = false;
var browserEvent = mapBrowserEvent.originalEvent;
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK) { // 事件类型过滤
var map = mapBrowserEvent.map;
var anchor = mapBrowserEvent.coordinate;
var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_; // 按住shift键,就缩小,否则就放大
var view = map.getView();
goog.asserts.assert(view, 'map must have a view');
ol.interaction.Interaction.zoomByDelta(
map, view, delta, anchor, this.duration_); // 调用 ol.interaction.Interaction.zoomByDelta函数实现放大缩小
mapBrowserEvent.preventDefault();
stopEvent = true;
}
return !stopEvent;
};
代码其实很简单,如果要自己实现一种交互方式,对照ol.interaction.DoubleClickZoom
这个学习,再加以应用就可以了。