-
Notifications
You must be signed in to change notification settings - Fork 508
基础知识
本文内容包括插件开发的基础知识, 基础类方法, 和一个简单插件示例.
maptalks.js核心库没有的功能, 都能开发为插件.
将功能包装为插件的益处:
- 方便重用
- 与业务逻辑解耦
- 易于测试, 更健壮
插件可用于:
- 为已有类增加新的方法
- 创建新的图层
- 为图层创建新的渲染器
- 创建新的图形
- 创建新的地图工具
- 创建新的地图控件
- 创建新的地图UI组件
你可以在这里浏览maptalks已有的插件, 列表里的插件还在不断增加, 也期待你能与大家分享你的插件作品.
maptalks设计之初就将插件作为最重要的设计目标, 为其开发插件是一件轻松愉悦的工作, 那么让我们马上开始插件开发之旅.
在开始开发插件之前, 你需要了解:
maptalks是面向对象的开发库, 除少数工具方法, 所有功能均以类(class)的形式发布.
如果你对面向对象的javascript不熟悉, 可以阅读下面这篇文章.
受Leaflet启发, 我们创建了maptalks.Class
类, 定义了一些常用类操作方法, 开发插件时, 可以把maptalks.Class
作为根类(当然这不是必须的).
maptalks.Class
中定义的类操作方法(源代码)如下:
class Class {
// 构造函数
// 参数中的options将与类的默认options合并, 生成对象的options
constructor(options) { }
// 将参数中的options与对象已有的options合并
setOptions(options) { }
// 返回或修改对象的options
// * 如果key为空, 则返回对象当前的options, 例如
// const options = map.config();
// * 如果key不为空, 则修改对象的options
// * key可以是键值, 例如map.config('draggable', false);
// * key可以是对象, 例如
// map.config({
// 'draggable' : false,
// 'doubleClickZoom' : false
// });
config(key) { }
// 用config更新options时的回调函数
onConfig(key) { }
// 添加一个创建钩子函数, 创建对象时, 钩子函数会被调用
static addInitHook(fn, ...args) { }
// 在类上增加新的方法
static include(...sources)
// 定义类的默认options
// 如果默认options已存在, 则与其合并
static mergeOptions(options)
}
options是一个特殊属性, 当你设置options时, 它不会覆盖类或父类的options而是与其合并. options特别适合用来管理类的配置项与配置项默认值.
options的用法:
- 在类上定义默认的options:
class MyClass extends maptalks.Class { }
// 定义MyClass上的默认options
MyClass.mergeOptions({
option1: 'foo',
option2: 'bar'
});
class ChildClass extends MyClass { }
ChildClass.mergeOptions({
option1: 'blah',
option3: true
});
const a = new ChildClass();
a.options.option1; // 'blah', 覆盖了父类的option1
a.options.option2; // 'bar', 父类定义的option2
a.options.option3; // true
- 除默认options, 也能在类构造函数中传入options, 创建对象时, options将与类默认options合并, 例如:
class MyClass extends maptalks.Class {
constructor(options) {
super(options);
}
};
MyClass.mergeOptions({
foo: 'bar',
bla: 5
});
const a = new MyClass({bla: 10});
a.options; // {foo: 'bar', bla: 10}
- 对象创建后, 用
config
方法来设置options, 例如:
/*
class MyClass extends maptalks.Class {
constructor(options) {
super(options);
}
};
MyClass.mergeOptions({
foo: 'bar',
bla: 5
});
*/
const a = new MyClass({bla: 10});
a.config({
bla : 20
});
a.config('foo', 'barar');
a.options; // {foo: 'barar', bla: 20}
我们采用ES6标准语法来继承父类, 创建子类, 例如:
class Child extends maptalks.Class {
constructor(name, options) {
super(options);
this.name = name;
}
}
Mixin是多重继承的一种实现方式, 我们采用MDN推荐的方式实现Mixin
const calculatorMixin = Base => class extends Base {
calc() { }
};
const randomizerMixin = Base => class extends Base {
randomize() { }
};
class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }
maptalks中的Mixin有Eventable, JSONAble等
include
类似Mixin, 可以用来在类上定义新的类方法, 例如
class MyClass extends maptalks.Class {}
MyClass.include({
fooFunc() {
//...
}
});
const a = new MyClass();
a.fooFunc();
include
与Mixin不同的是, 一般用include
定义只在MyClass
中使用的方法, Mixin用于多重继承.
在插件开发时, 如果需要在创建类的对象时执行一些附加逻辑, 此时可以用addInitHook
方法添加钩子方法, 添加的钩子方法会在执行类的构造函数时被调用, 例如:
MyClass.addInitHook(function () {
this.foo = 'bar';
});
addInitHook
也支持下面的形式, methodNameOfMyClass
会在构造函数执行时被调用, arg1
, arg2
等会作为参数传递给methodNameOfMyClass
MyClass.include({
methodNameOfMyClass() { }
});
MyClass.addInitHook('methodNameOfMyClass', arg1, arg2, …);
以上即是maptalks插件开发所需的基础知识, 接下来, 我们来看一个实际的插件示例(github地址).
该插件用来判断多边形是否存在自相交(因自相交图形会增加复杂度, 大多数时候是不允许的). 插件在maptalks的Polygon
和MultiPolygon
图形类上增加了isects
方法, 该方法没有参数, 其返回值是个数组, 如果存在自相交, 数组中会包含相交点的坐标, 如果没有自相交, 数组即为空, 其调用示例:
const polygon = new maptalks.Polygon(...);
const isects = polygon.isects();
console.log(isects.length > 0 ? '有自相交' : '没有自相交');
插件基于2d-polygon-self-intersections实现. 我们通过上面提到的include
方法来给Polygon和MultiPolygon增加了新方法, 完整源代码如下:
import isect from '2d-polygon-self-intersections';
import * as maptalks from 'maptalks';
maptalks.Polygon.include({
isects() {
const coordinates = maptalks.Coordinate.toNumberArrays(this.getCoordinates());
const sects = [];
let r, ring;
for (let i = 0, l = coordinates.length; i < l; i++) {
ring = coordinates[i];
if (ring.length > 0) {
ring = ring.slice(0, ring.length - 1);
}
r = isect(ring);
if (r.length > 0) {
sects.push([i, r]);
}
}
return sects;
}
});
maptalks.MultiPolygon.include({
isects() {
const geometries = this.getGeometries();
let r;
const sects = [];
for (let i = 0, l = geometries.length; i < l; i++) {
r = geometries[i].isects();
if (r.length > 0) {
sects.push([i, r]);
}
}
return sects;
}
});
接下来请继续阅读其他文章, 你会学习到插件开发的更多技巧.