【ol】事件型別

看見了發表於2022-11-28
本文主要學習openlayers的Event模組相關原始碼

BaseEvent

Openlayers根據W3C DOM Level 2 Event介面簡化實現了自己的事件類,它只提供了typetarget屬性以及preventDefaultstopPropagation方法。

class BaseEvent {
  /**
   * @param {string} type Type.
   */
  constructor(type) {
    /**
     * @type {boolean}
     */
    this.propagationStopped;

    /**
     * @type {boolean}
     */
    this.defaultPrevented;

    /**
     * The event type.
     * @type {string}
     * @api
     */
    this.type = type;

    /**
     * The event target.
     * @type {Object}
     * @api
     */
    this.target = null;
  }

  /**
   * Prevent default. This means that no emulated `click`, `singleclick` or `doubleclick` events
   * will be fired.
   * @api
   */
  preventDefault() {
    this.defaultPrevented = true;
  }

  /**
   * Stop event propagation.
   * @api
   */
  stopPropagation() {
    this.propagationStopped = true;
  }
}

EventType

EventType物件儲存了所有觸發地圖事件的事件名稱。

/**
 * @enum {string}
 * @const
 */
export default {
  /**
   * Generic change event. Triggered when the revision counter is increased.
   * @event module:ol/events/Event~BaseEvent#change
   * @api
   */
  CHANGE: 'change',

  /**
   * Generic error event. Triggered when an error occurs.
   * @event module:ol/events/Event~BaseEvent#error
   * @api
   */
  ERROR: 'error',

  BLUR: 'blur',
  CLEAR: 'clear',
  CONTEXTMENU: 'contextmenu',
  CLICK: 'click',
  DBLCLICK: 'dblclick',
  DRAGENTER: 'dragenter',
  DRAGOVER: 'dragover',
  DROP: 'drop',
  FOCUS: 'focus',
  KEYDOWN: 'keydown',
  KEYPRESS: 'keypress',
  LOAD: 'load',
  RESIZE: 'resize',
  TOUCHMOVE: 'touchmove',
  WHEEL: 'wheel',
};

KeyCode

KeyCode物件只記錄了上下左右鍵盤方向鍵的keycode,便於事件監聽。

export default {
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
};

Target

openlayers也簡化實現了Target類,注意想useCapture是沒有效果的,因為addEventListenerremoveEventListener都是模擬實現事件監聽,並不是真實的事件捕獲。大部分時間也用不到,瞭解下如何寫事件監聽即可。

class Target extends Disposable {
  /**
   * @param {*} [opt_target] Default event target for dispatched events.
   */
  constructor(opt_target) {
    super();

    /**
     * @private
     * @type {*}
     */
    this.eventTarget_ = opt_target;

    /**
     * @private
     * @type {Object<string, number>}
     */
    this.pendingRemovals_ = null;

    /**
     * @private
     * @type {Object<string, number>}
     */
    this.dispatching_ = null;

    /**
     * @private
     * @type {Object<string, Array<import("../events.js").Listener>>}
     */
    this.listeners_ = null;
  }

  /**
   * @param {string} type Type.
   * @param {import("../events.js").Listener} listener Listener.
   */
  addEventListener(type, listener) {
    if (!type || !listener) {
      return;
    }
    const listeners = this.listeners_ || (this.listeners_ = {});
    const listenersForType = listeners[type] || (listeners[type] = []);
    if (listenersForType.indexOf(listener) === -1) {
      listenersForType.push(listener);
    }
  }

  /**
   * Dispatches an event and calls all listeners listening for events
   * of this type. The event parameter can either be a string or an
   * Object with a `type` property.
   *
   * @param {import("./Event.js").default|string} event Event object.
   * @return {boolean|undefined} `false` if anyone called preventDefault on the
   *     event object or if any of the listeners returned false.
   * @api
   */
  dispatchEvent(event) {
    /** @type {import("./Event.js").default|Event} */
    const evt = typeof event === 'string' ? new Event(event) : event;
    const type = evt.type;
    if (!evt.target) {
      evt.target = this.eventTarget_ || this;
    }
    const listeners = this.listeners_ && this.listeners_[type];
    let propagate;
    if (listeners) {
      const dispatching = this.dispatching_ || (this.dispatching_ = {});
      const pendingRemovals =
        this.pendingRemovals_ || (this.pendingRemovals_ = {});
      if (!(type in dispatching)) {
        dispatching[type] = 0;
        pendingRemovals[type] = 0;
      }
      ++dispatching[type];
      for (let i = 0, ii = listeners.length; i < ii; ++i) {
        if ('handleEvent' in listeners[i]) {
          propagate = /** @type {import("../events.js").ListenerObject} */ (
            listeners[i]
          ).handleEvent(evt);
        } else {
          propagate = /** @type {import("../events.js").ListenerFunction} */ (
            listeners[i]
          ).call(this, evt);
        }
        if (propagate === false || evt.propagationStopped) {
          propagate = false;
          break;
        }
      }
      --dispatching[type];
      if (dispatching[type] === 0) {
        let pr = pendingRemovals[type];
        delete pendingRemovals[type];
        while (pr--) {
          this.removeEventListener(type, VOID);
        }
        delete dispatching[type];
      }
      return propagate;
    }
  }

  /**
   * Clean up.
   */
  disposeInternal() {
    this.listeners_ && clear(this.listeners_);
  }

  /**
   * Get the listeners for a specified event type. Listeners are returned in the
   * order that they will be called in.
   *
   * @param {string} type Type.
   * @return {Array<import("../events.js").Listener>|undefined} Listeners.
   */
  getListeners(type) {
    return (this.listeners_ && this.listeners_[type]) || undefined;
  }

  /**
   * @param {string} [opt_type] Type. If not provided,
   *     `true` will be returned if this event target has any listeners.
   * @return {boolean} Has listeners.
   */
  hasListener(opt_type) {
    if (!this.listeners_) {
      return false;
    }
    return opt_type
      ? opt_type in this.listeners_
      : Object.keys(this.listeners_).length > 0;
  }

  /**
   * @param {string} type Type.
   * @param {import("../events.js").Listener} listener Listener.
   */
  removeEventListener(type, listener) {
    const listeners = this.listeners_ && this.listeners_[type];
    if (listeners) {
      const index = listeners.indexOf(listener);
      if (index !== -1) {
        if (this.pendingRemovals_ && type in this.pendingRemovals_) {
          // make listener a no-op, and remove later in #dispatchEvent()
          listeners[index] = VOID;
          ++this.pendingRemovals_[type];
        } else {
          listeners.splice(index, 1);
          if (listeners.length === 0) {
            delete this.listeners_[type];
          }
        }
      }
    }
  }
}

export default Target;

相關文章