实现原理

不管是上一节中的哪一种交互方式,本质上都是通过监听事件,来处理相应的业务的,在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这个学习,再加以应用就可以了。