本文主要學習openlayers的Event模組相關原始碼
BaseEvent
Openlayers根據W3C DOM Level 2 Event介面簡化實現了自己的事件類,它只提供了type
和target
屬性以及preventDefault
和stopPropagation
方法。
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
是沒有效果的,因為addEventListener
與removeEventListener
都是模擬實現事件監聽,並不是真實的事件捕獲。大部分時間也用不到,瞭解下如何寫事件監聽即可。
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;