Map
为提供给应用层的一个类,代码不多,主要是继承自PluggableMap
,在Map
中只是设置了controls
和interactions
,重写createRenderer
渲染方法。然后再使用super
方法进行初始化。所有初始化相关的操作都是在PluggableMap
中进行的,这里主要对PluggableMap
进行分析。
1.地图初始化
PluggableMap
的作用主要是进行初始化相关的操作,主要包括地图初始化、添加地图事件,加载队列初化,layers
,controls
,overlays
等初始化,以及一些公共方法的实现。
1.1内部初始化
在执行PluggableMap
构造函数时,首先调用了createOptionsInternal
方法进行初始化,主要是对controls
,interactions
,keyboardEventTarget
,overlays
,values
等参数的初始化。
function createOptionsInternal(options) {
//初始化键盘事件的目标元素
let keyboardEventTarget = null;
if (options.keyboardEventTarget !== undefined) {
keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ?
document.getElementById(options.keyboardEventTarget) :
options.keyboardEventTarget;
}
//用于存储属性值
const values = {};
//图层组
const layerGroup = options.layers && typeof (options.layers).getLayers === 'function' ?
(options.layers) : new LayerGroup({layers: (options.layers)});
values[MapProperty.LAYERGROUP] = layerGroup;
values[MapProperty.TARGET] = options.target;
values[MapProperty.VIEW] = options.view !== undefined ?
options.view : new View();
//初始化控件
let controls;
if (options.controls !== undefined) {
if (Array.isArray(options.controls)) {
controls = new Collection(options.controls.slice());
} else {
assert(typeof (options.controls).getArray === 'function',
47);
controls = (options.controls);
}
}
//初始化地图交互
let interactions;
if (options.interactions !== undefined) {
if (Array.isArray(options.interactions)) {
interactions = new Collection(options.interactions.slice());
} else {
assert(typeof /** @type {?} */ (options.interactions).getArray === 'function',
48);
interactions = /** @type {Collection} */ (options.interactions);
}
}
//初始化覆盖物
let overlays;
if (options.overlays !== undefined) {
if (Array.isArray(options.overlays)) {
overlays = new Collection(options.overlays.slice());
} else {
assert(typeof (options.overlays).getArray === 'function',
49);
overlays = options.overlays;
}
} else {
overlays = new Collection();
}
return {
controls: controls,
interactions: interactions,
keyboardEventTarget: keyboardEventTarget,
overlays: overlays,
values: values
};
}
1.2Collection
在执行初始化操作时,用到了一个数组Collection
,该类是对array
的一个封装,主要是对更新的操作进行一个事件派发,当往Collection
对象中执行新增、修改、删除等操作时,可以派发事件,方便应用层做一些处理。如下所示为添加元素的一个方法:
insertAt(index, elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
this.array_.splice(index, 0, elem);//添加数据
this.updateLength_();//更新长度
//添加数据后派发事件
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem, index));
}
1.3添加元素
在地图控件上,除了地图元素外,还需要添加了一些比较重要的div
元素,在构造函数中将看到一些如下所示的代码:
this.viewport_ = document.createElement('div');
this.viewport_.className = 'ol-viewport' + ('ontouchstart' in window ? ' ol-touch' : '');
this.viewport_.style.position = 'relative';
this.viewport_.style.overflow = 'hidden';
this.viewport_.style.width = '100%';
this.viewport_.style.height = '100%';
this.overlayContainer_ = document.createElement('div');
this.overlayContainer_.style.position = 'absolute';
this.overlayContainer_.style.zIndex = '0';
this.overlayContainer_.style.width = '100%';
this.overlayContainer_.style.height = '100%';
this.overlayContainer_.className = 'ol-overlaycontainer';
this.viewport_.appendChild(this.overlayContainer_);
this.overlayContainerStopEvent_ = document.createElement('div');
this.overlayContainerStopEvent_.style.position = 'absolute';
this.overlayContainerStopEvent_.style.zIndex = '0';
this.overlayContainerStopEvent_.style.width = '100%';
this.overlayContainerStopEvent_.style.height = '100%';
this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';
this.viewport_.appendChild(this.overlayContainerStopEvent_);
其中viewport_
为一个比较重要的元素,是将它添加在地图上的,后面讲到的一些事件都是基于viewport_
来处理的,可以通过MapBrowserEventHandler
的构造函数可以看到,事件是添加在viewport_
上的,如下所示:
//获取viewport_
const element = this.map_.getViewport();
this.activePointers_ = 0;
this.trackedTouches_ = {};
this.element_ = element;
this.pointerdownListenerKey_ = listen(element,
PointerEventType.POINTERDOWN,
this.handlePointerDown_, this);//给viewport_添加鼠标按下的事件
this.originalPointerMoveEvent_;
this.relayedListenerKey_ = listen(element,
PointerEventType.POINTERMOVE,
this.relayEvent_, this);//给viewport_添加鼠标移开的事件
this.boundHandleTouchMove_ = this.handleTouchMove_.bind(this);
this.element_.addEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_,
PASSIVE_EVENT_LISTENERS ? {passive: false} : false);//给viewport_添加手势移开的事件
1.4优先队列
使用TileQueue
可以进行切片队列的初始化,如下所示:
this.tileQueue_ = new TileQueue(
this.getTilePriority.bind(this),
this.handleTileChange_.bind(this));
TileQueue
继承于PriorityQueue
,相关的队列逻辑都是在PriorityQueue
中完成的。使用的是一个优先队列的算法。离中心点最近的切片最先获取。
2.地图事件
在初始化PluggableMap
的时候,会添加地图事件,先看下包括哪些事件,进入MapBrowserEventType
文件中,可以看到所有的事件,如下所示:
export default {
SINGLECLICK: 'singleclick',
CLICK: EventType.CLICK,
DBLCLICK: EventType.DBLCLICK,
POINTERDRAG: 'pointerdrag',
POINTERMOVE: 'pointermove',
POINTERDOWN: 'pointerdown',
POINTERUP: 'pointerup',
POINTEROVER: 'pointerover',
POINTEROUT: 'pointerout',
POINTERENTER: 'pointerenter',
POINTERLEAVE: 'pointerleave',
POINTERCANCEL: 'pointercancel'
};
地图事件的处理主要都是在MapBrowserEventHandler
中完成,主要是实现了一些点击事件、鼠标移动事件,手势操作事件,对于鼠标弹起事件主要用于判断是否为单击事件还是双击事件,鼠标按钮事件来判断是否为拖动事件,可以看下一些事件的实现方式:
//鼠标弹起事件
handlePointerUp_(pointerEvent) {
//更新点击事件
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERUP, this.map_, pointerEvent);
this.dispatchEvent(newEvent);//创建一个新事件
if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) {
this.emulateClick_(this.down_);//执行点击事件
}
//移除事件
if (this.activePointers_ === 0) {
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
this.dragging_ = false;
this.down_ = null;
}
}
//模拟点击事件,通过250ms来判断是否为双击事件
emulateClick_(pointerEvent) {
let newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.CLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
if (this.clickTimeoutId_ !== undefined) {
// 双击
clearTimeout(this.clickTimeoutId_);
this.clickTimeoutId_ = undefined;
newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
} else {
// 单击
this.clickTimeoutId_ = setTimeout(function() {
this.clickTimeoutId_ = undefined;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
}.bind(this), 250);
}
}
//鼠标按下事件
handlePointerDown_(pointerEvent) {
this.updateActivePointers_(pointerEvent);
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
this.down_ = pointerEvent;
if (this.dragListenerKeys_.length === 0) {
//给document元素添加移动,弹起、取消等事件
this.dragListenerKeys_.push(
listen(document,
MapBrowserEventType.POINTERMOVE,
this.handlePointerMove_, this),
listen(document,
MapBrowserEventType.POINTERUP,
this.handlePointerUp_, this),
listen(this.element_,
MapBrowserEventType.POINTERCANCEL,
this.handlePointerUp_, this)
);
}
}
//鼠标移动事件
handlePointerMove_(pointerEvent) {
if (this.isMoving_(pointerEvent)) {
this.dragging_ = true;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent,
this.dragging_);
this.dispatchEvent(newEvent);
}
}
//处理获取viewport_鼠标移动的事件
relayEvent_(pointerEvent) {
this.originalPointerMoveEvent_ = pointerEvent;
const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
this.dispatchEvent(new MapBrowserPointerEvent(
pointerEvent.type, this.map_, pointerEvent, dragging));
}
//判断是否在拖拽
isMoving_(pointerEvent) {
return this.dragging_ ||
Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ ||
Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_;
}