openlayers源码阅读笔记(一)—— 地图ol.Map

openlayers源码阅读笔记(一)—— 地图ol.Map

2. ol/Map类
class Map extends PluggableMap {
  /**
   * @param {import("./PluggableMap.js").MapOptions} options Map options.
   */
  constructor(options) {
    options = assign({}, options);
    if (!options.controls) {
      options.controls = defaultControls();
    }
    if (!options.interactions) {
      options.interactions = defaultInteractions({
        onFocusOnly: true,
      });
    }

    super(options);
  }

  createRenderer() {
    return new CompositeMapRenderer(this);
  }
}

Map类继承自PluggableMap类并且实现了的父类的createRenderer方法,该方法的作用是返回一个地图渲染器,接下来看PluggableMap类。

3. ol/PluggableMap类
3.1 创建DOM节点
class PluggableMap extends BaseObject {
    /**
   * @param {MapOptions} options Map options.
   */
  constructor(options) {
    super();
    ... // 305行到341行代码
          /**
     * @private
     * @type {!HTMLElement}
     */
    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%';

    /**
     * @private
     * @type {!HTMLElement}
     */
    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_.style.pointerEvents = 'none';
    this.overlayContainer_.className = 'ol-overlaycontainer';
    this.viewport_.appendChild(this.overlayContainer_);

    /**
     * @private
     * @type {!HTMLElement}
     */
    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_.style.pointerEvents = 'none';
    this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent';
    this.viewport_.appendChild(this.overlayContainerStopEvent_);
    ...
  }
}

openlayers中的DOM结构如下,其中div.ol-viewport、div.ol-overlaycontainer、div.ol-overlaycontainer-stopevent便是在这里定义的;div.ol-layers则是在渲染图层的时候创建。

<div class="map">
  <div class="ol-viewport">
    <div class="ol-unselectable ol-layers"></div>
    <div class="ol-overlaycontainer"></div>
    <div class="ol-overlaycontainer-stopevent"></div>
  </div>
</div>
3.2 监听事件

class PluggableMap extends BaseObject {
    /**
   * @param {MapOptions} options Map options.
   */
  constructor(options) {
    ... // 413行到423行代码
    this.addChangeListener(
      MapProperty.LAYERGROUP,
      this.handleLayerGroupChanged_
    );
    this.addChangeListener(MapProperty.VIEW, this.handleViewChanged_);
    this.addChangeListener(MapProperty.SIZE, this.handleSizeChanged_);
    this.addChangeListener(MapProperty.TARGET, this.handleTargetChanged_);

    // setProperties will trigger the rendering of the map if the map
    // is "defined" already.
    this.setProperties(optionsInternal.values);
    ...
  }
}

这里监听了图层组LAYERGROUP、视图VIEW、地图尺寸SIZE、地图关联的HTML元素TARGET的改变事假,紧跟着的setProperties会触发事件渲染地图。

3.3 地图渲染

我们先看图层改变的事件,我们new Map(option)时option里面的layers属性,以及调用map.addLayer,map.removeLayer时都会改变layerGroup,触发此事件。

  /**
   * @private
   */
  handleLayerGroupChanged_() {
    if (this.layerGroupPropertyListenerKeys_) {
      this.layerGroupPropertyListenerKeys_.forEach(unlistenByKey);
      this.layerGroupPropertyListenerKeys_ = null;
    }
    const layerGroup = this.getLayerGroup();
    if (layerGroup) {
      this.handleLayerAdd_(new GroupEvent('addlayer', layerGroup));
      this.layerGroupPropertyListenerKeys_ = [
        listen(layerGroup, ObjectEventType.PROPERTYCHANGE, this.render, this),
        listen(layerGroup, EventType.CHANGE, this.render, this),
        listen(layerGroup, 'addlayer', this.handleLayerAdd_, this),
        listen(layerGroup, 'removelayer', this.handleLayerRemove_, this),
      ];
    }
    this.render();
  }

前面几行代码就是解绑和监听LayerGroup状态改变相关的事件,最后执行了地图的渲染函数,接下来我们看render方法。

  /**
   * Request a map rendering (at the next animation frame).
   * @api
   */
  render() {
    if (this.renderer_ && this.animationDelayKey_ === undefined) {
      this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_);
    }
  }
    /**
     * @private
     */
    this.animationDelay_ = /** @this {PluggableMap} */ function () {
      this.animationDelayKey_ = undefined;
      this.renderFrame_(Date.now());
    }.bind(this);

requestAnimationFrame要求浏览器在下次重绘之前调用指定的回调函数更新动画,也就是说在render函数中执行了一个方法this.animationDelay_,而在animationDelay_调用了方法this.renderFrame_,接下来我们看renderFrame_方法。

3.4 renderFrame方法

虽然renderFrame有100行代码,但很多都是对framestate的配置以及地图含有动画的情况的处理。framestate我理解是帧状态,也就是地图接下来需要渲染的那一帧的状态信息,包括地图的范围extent、坐标与像素相互转换矩阵、像素比pixelRatio、地图尺寸size、请求渲染的时间time以及视图状态、图层状态等。如果地图正处于动画中,则会在次调用地图的render函数并且在动画开始和结束时给地图派发MOVESTART和MOVEEND事件。

  renderFrame_(time) {
    ......
    this.frameState_ = frameState;
    this.renderer_.renderFrame(frameState);

    if (frameState) {
      if (frameState.animate) {
        this.render();
      }
      Array.prototype.push.apply(
        this.postRenderFunctions_,
        frameState.postRenderFunctions
      )
      ......
    }

    this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState));

	......
  }
      if (!this.renderer_) {
        this.renderer_ = this.createRenderer();
      }

在配置好framestate之后,调用了this.renderer_.renderFrame方法,最后派发的地图的POSTRENDER事件,而this.renderer_便是上次提到Map类实现的createRenderer创建的,它的代码我们会在下一节渲染器中讲。

4 小结

Map类主要做的事包括定义地图的Dom节点、监听地图改变事件,调用渲染器来渲染地图。其实还有一些在博客中没有说到,比如添加交互Interaction、叠加层Overlay、空间Control以及其添加、移除事件。下面是交互的一些相关代码。

    this.interactions.addEventListener(
      CollectionEventType.ADD,
      /**
       * @param {import("./Collection.js").CollectionEvent} event CollectionEvent.
       */
      function (event) {
        event.element.setMap(this);
      }.bind(this)
    );

    this.interactions.addEventListener(
      CollectionEventType.REMOVE,
      /**
       * @param {import("./Collection.js").CollectionEvent} event CollectionEvent.
       */
      function (event) {
        event.element.setMap(null);
      }.bind(this)
    );
    ......
     this.interactions.forEach(
      /**
       * @param {import("./interaction/Interaction.js").default} interaction Interaction.
       * @this {PluggableMap}
       */
      function (interaction) {
        interaction.setMap(this);
      }.bind(this)
    );
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值