1 准备
// 码云fork,速度快
git clone https://gitee.com/solfKwolf/openlayers.git
// 以v5.3版本为准
git checkout v5.3.0
2 入口文件
src/ol/index.js
/**
* @module ol
*/
export {default as AssertionError} from './AssertionError.js';
export {default as Collection} from './Collection.js';
export {default as Disposable} from './Disposable.js';
export {default as Feature} from './Feature.js';
export {default as Geolocation} from './Geolocation.js';
export {default as Graticule} from './Graticule.js';
export {default as Image} from './Image.js';
export {default as ImageBase} from './ImageBase.js';
export {default as ImageCanvas} from './ImageCanvas.js';
export {default as ImageTile} from './ImageTile.js';
export {default as Kinetic} from './Kinetic.js';
export {default as Map} from './Map.js';
export {default as MapBrowserEvent} from './MapBrowserEvent.js';
export {default as MapBrowserEventHandler} from './MapBrowserEventHandler.js';
export {default as MapBrowserPointerEvent} from './MapBrowserPointerEvent.js';
export {default as MapEvent} from './MapEvent.js';
export {default as Object} from './Object.js';
export {default as Observable} from './Observable.js';
export {default as Overlay} from './Overlay.js';
export {default as PluggableMap} from './PluggableMap.js';
export {default as Tile} from './Tile.js';
export {default as TileCache} from './TileCache.js';
export {default as TileQueue} from './TileQueue.js';
export {default as TileRange} from './TileRange.js';
export {default as VectorImageTile} from './VectorImageTile.js';
export {default as VectorTile} from './VectorTile.js';
export {default as View} from './View.js';
export {default as WebGLMap} from './WebGLMap.js';
export {getUid, inherits, VERSION} from './util.js';
ol是将不同的模块拆分成不同的文件集中放置在ol文件下,再统一通过index.js入口文件暴露出去。
将default暴露的模块重命名
// xxx.js
export default function() {
// statement
}
// index.js
export {default as xxx} from './xxx.js';
// 相当于
import { default as xxx } from './xxx.js'
3 AssertionError
openlayer自定义错误类(继承Error),目的是方便自定义错误描述与错误码相对应。
- 错误码文档
示例截图
class AssertionError extends Error {
/**
* @param {number} code Error code.
*/
constructor(code) {
const path = VERSION === 'latest' ? VERSION : 'v' + VERSION.split('-')[0];
const message = 'Assertion failed. See https://openlayers.org/en/' + path +
'/doc/errors/#' + code + ' for details.';
super(message);
/**
* Error code. The meaning of the code can be found on
* https://openlayers.org/en/latest/doc/errors/ (replace `latest` with
* the version found in the OpenLayers script's header comment if a version
* other than the latest is used).
* @type {number}
* @api
*/
this.code = code;
/**
* @type {string}
*/
this.name = 'AssertionError';
// Re-assign message, see https://github.com/Rich-Harris/buble/issues/40
this.message = message;
}
}
如果需要自己开发类库,需要认真研读Error类,非常简单。
5 Event
openlayers库中定义的事件基类,只提供type
和target
属性以及stopPropagation
和preventDefault
方法。方便后面的自定义事件。
class Event {
/**
* @param {string} type Type.
*/
constructor(type) {
/**
* @type {boolean}
*/
this.propagationStopped;
/**
* The event type.
* @type {string}
* @api
*/
this.type = type;
/**
* The event target.
* @type {Object}
* @api
*/
this.target = null;
}
/**
* Stop event propagation.
* @api
*/
preventDefault() {
this.propagationStopped = true;
}
/**
* Stop event propagation.
* @api
*/
stopPropagation() {
this.propagationStopped = true;
}
}
/**
* @param {Event|import("./Event.js").default} evt Event
*/
export function stopPropagation(evt) {
evt.stopPropagation();
}
/**
* @param {Event|import("./Event.js").default} evt Event
*/
export function preventDefault(evt) {
evt.preventDefault();
}
export default Event;
6 Target
对象类是配合事件类的。
class Target extends Disposable {
constructor() {
super();
/**
* @private
* @type {!Object}
*/
this.pendingRemovals_ = {};
/**
* @private
* @type {!Object}
*/
this.dispatching_ = {};
/**
* @private
* @type {!Object>}
*/
this.listeners_ = {};
}
/**
* @param {string} type Type.
* @param {import("../events.js").ListenerFunction} listener Listener.
*/
addEventListener(type, listener) {
let listeners = this.listeners_[type];
if (!listeners) {
listeners = this.listeners_[type] = [];
}
if (listeners.indexOf(listener) === -1) {
listeners.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 {{type: string,
* target: (EventTargetLike|undefined),
* propagationStopped: (boolean|undefined)}|
* 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) {
const evt = typeof event === 'string' ? new Event(event) : event;
const type = evt.type;
evt.target = this;
const listeners = this.listeners_[type];
let propagate;
if (listeners) {
if (!(type in this.dispatching_)) {
this.dispatching_[type] = 0;
this.pendingRemovals_[type] = 0;
}
++this.dispatching_[type];
for (let i = 0, ii = listeners.length; i < ii; ++i) {
if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
propagate = false;
break;
}
}
--this.dispatching_[type];
if (this.dispatching_[type] === 0) {
let pendingRemovals = this.pendingRemovals_[type];
delete this.pendingRemovals_[type];
while (pendingRemovals--) {
this.removeEventListener(type, VOID);
}
delete this.dispatching_[type];
}
return propagate;
}
}
/**
* @inheritDoc
*/
disposeInternal() {
unlistenAll(this);
}
/**
* 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} Listeners.
*/
getListeners(type) {
return this.listeners_[type];
}
/**
* @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) {
return opt_type ?
opt_type in this.listeners_ :
Object.keys(this.listeners_).length > 0;
}
/**
* @param {string} type Type.
* @param {import("../events.js").ListenerFunction} listener Listener.
*/
removeEventListener(type, listener) {
const listeners = this.listeners_[type];
if (listeners) {
const index = listeners.indexOf(listener);
if (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;
7 EventType
事件类型字典表
export default {
/**
* Generic change event. Triggered when the revision counter is increased.
* @event module:ol/events/Event~Event#change
* @api
*/
CHANGE: 'change',
CLEAR: 'clear',
CONTEXTMENU: 'contextmenu',
CLICK: 'click',
DBLCLICK: 'dblclick',
DRAGENTER: 'dragenter',
DRAGOVER: 'dragover',
DROP: 'drop',
ERROR: 'error',
KEYDOWN: 'keydown',
KEYPRESS: 'keypress',
LOAD: 'load',
MOUSEDOWN: 'mousedown',
MOUSEMOVE: 'mousemove',
MOUSEOUT: 'mouseout',
MOUSEUP: 'mouseup',
MOUSEWHEEL: 'mousewheel',
MSPOINTERDOWN: 'MSPointerDown',
RESIZE: 'resize',
TOUCHSTART: 'touchstart',
TOUCHMOVE: 'touchmove',
TOUCHEND: 'touchend',
WHEEL: 'wheel'
};
7 BaseObject
src/BaseObject.js
这个对象其实使用频率最高,为啥,因为经常用到它定义的get
的方法
8 Collection.js
这里定义了Collection
和 CollectionEvent
2个类
CollectionEvent
只在Event的基础上增加了element
属性
/**
* @classdesc
* Events emitted by {@link module:ol/Collection~Collection} instances are instances of this
* type.
*/
export class CollectionEvent extends Event {
/**
* @param {CollectionEventType} type Type.
* @param {*=} opt_element Element.
*/
constructor(type, opt_element) {
super(type);
/**
* The element that is added to or removed from the collection.
* @type {*}
* @api
*/
this.element = opt_element;
}
}
Collection
对js数组的扩展,简单的一个数据接口,可以参考下。
class Collection extends BaseObject {
/**
* @param {Array=} opt_array Array.
* @param {Options=} opt_options Collection options.
*/
constructor(opt_array, opt_options) {
super();
const options = opt_options || {};
/**
* @private
* @type {boolean}
*/
this.unique_ = !!options.unique;
/**
* @private
* @type {!Array}
*/
this.array_ = opt_array ? opt_array : [];
if (this.unique_) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
this.assertUnique_(this.array_[i], i);
}
}
this.updateLength_();
}
/**
* Remove all elements from the collection.
* @api
*/
clear() {
while (this.getLength() > 0) {
this.pop();
}
}
/**
* Add elements to the collection. This pushes each item in the provided array
* to the end of the collection.
* @param {!Array} arr Array.
* @return {Collection} This collection.
* @api
*/
extend(arr) {
for (let i = 0, ii = arr.length; i < ii; ++i) {
this.push(arr[i]);
}
return this;
}
/**
* Iterate over each element, calling the provided callback.
* @param {function(T, number, Array): *} f The function to call
* for every element. This function takes 3 arguments (the element, the
* index and the array). The return value is ignored.
* @api
*/
forEach(f) {
const array = this.array_;
for (let i = 0, ii = array.length; i < ii; ++i) {
f(array[i], i, array);
}
}
/**
* Get a reference to the underlying Array object. Warning: if the array
* is mutated, no events will be dispatched by the collection, and the
* collection's "length" property won't be in sync with the actual length
* of the array.
* @return {!Array} Array.
* @api
*/
getArray() {
return this.array_;
}
/**
* Get the element at the provided index.
* @param {number} index Index.
* @return {T} Element.
* @api
*/
item(index) {
return this.array_[index];
}
/**
* Get the length of this collection.
* @return {number} The length of the array.
* @observable
* @api
*/
getLength() {
return this.get(Property.LENGTH);
}
/**
* Insert an element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
insertAt(index, elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
this.array_.splice(index, 0, elem);
this.updateLength_();
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
}
/**
* Remove the last element of the collection and return it.
* Return `undefined` if the collection is empty.
* @return {T|undefined} Element.
* @api
*/
pop() {
return this.removeAt(this.getLength() - 1);
}
/**
* Insert the provided element at the end of the collection.
* @param {T} elem Element.
* @return {number} New length of the collection.
* @api
*/
push(elem) {
if (this.unique_) {
this.assertUnique_(elem);
}
const n = this.getLength();
this.insertAt(n, elem);
return this.getLength();
}
/**
* Remove the first occurrence of an element from the collection.
* @param {T} elem Element.
* @return {T|undefined} The removed element or undefined if none found.
* @api
*/
remove(elem) {
const arr = this.array_;
for (let i = 0, ii = arr.length; i < ii; ++i) {
if (arr[i] === elem) {
return this.removeAt(i);
}
}
return undefined;
}
/**
* Remove the element at the provided index and return it.
* Return `undefined` if the collection does not contain this index.
* @param {number} index Index.
* @return {T|undefined} Value.
* @api
*/
removeAt(index) {
const prev = this.array_[index];
this.array_.splice(index, 1);
this.updateLength_();
this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev));
return prev;
}
/**
* Set the element at the provided index.
* @param {number} index Index.
* @param {T} elem Element.
* @api
*/
setAt(index, elem) {
const n = this.getLength();
if (index < n) {
if (this.unique_) {
this.assertUnique_(elem, index);
}
const prev = this.array_[index];
this.array_[index] = elem;
this.dispatchEvent(
new CollectionEvent(CollectionEventType.REMOVE, prev));
this.dispatchEvent(
new CollectionEvent(CollectionEventType.ADD, elem));
} else {
for (let j = n; j < index; ++j) {
this.insertAt(j, undefined);
}
this.insertAt(index, elem);
}
}
/**
* @private
*/
updateLength_() {
this.set(Property.LENGTH, this.array_.length);
}
/**
* @private
* @param {T} elem Element.
* @param {number=} opt_except Optional index to ignore.
*/
assertUnique_(elem, opt_except) {
for (let i = 0, ii = this.array_.length; i < ii; ++i) {
if (this.array_[i] === elem && i !== opt_except) {
throw new AssertionError(58);
}
}
}
}
Disposable
需要被清理的对象
class Disposable {
constructor() {
/**
* The object has already been disposed.
* @type {boolean}
* @private
*/
this.disposed_ = false;
}
/**
* Clean up.
*/
dispose() {
if (!this.disposed_) {
this.disposed_ = true;
this.disposeInternal();
}
}
/**
* Extension point for disposable objects.
* @protected
*/
disposeInternal() {}
}