leaflet之扩展leaflet类,定制功能

简介:leaflet提供了操作地图的基本类,提供了扩展功能。用户可以自由个性化定制扩展功能,开发自己的插件。

扩展leaflet

Leaflet有几百个插件。这些扩展了Leaflet的功能:有时以通用方式,有时以特定于用例的方式。

有这么多插件的部分原因是Leaflet很容易扩展。本教程将介绍最常用的方法。

请注意,本教程需要已经掌握了以下基础:

  • JavaScript
  • DOM处理
  • 面向对象的编程(理解类,实例,继承,方法和属性等概念)

leaflet架构

让我们看一下Leaflet 1.0.0的简化UML类图。有超过60个JavaScript类,所以图表有点大。幸运的是,我们可以制作一个可缩放的图像L.ImageOverlay:

单独查看此示例。

从技术角度来看,Leaflet可以以不同的方式扩展:

  • 最常见的:创建一个新的子类L.Layer,L.Handler或L.Control与L.Class.extend()
    • 移动/缩放地图时图层会移动
    • 处理程序是不可见的并解释浏览器事件
    • 控件是固定的界面元素
  • 在现有类中包含更多功能 L.Class.include()
    • 添加新方法和选项
    • 改变一些方法
    • 使用addInitHook运行额外的构造函数代码。
  • 更改现有类的部分(替换类方法的工作方式)L.Class.include()。

本教程介绍了仅在Leaflet 1.0.0中可用的一些类和方法。如果您正在为以前的版本开发插件,请谨慎使用。

L.Class

JavaScript是一种奇怪的语言。它不是一种面向对象的语言,而是一种面向原型的语言。这使得JavaScript在历史上难以在术语的经典OOP含义中使用类继承。

Leaflet通过使用来解决这个问题L.Class,从而简化了类的继承。

尽管现代JavaScript可以使用ES6类,但Leaflet并不是围绕它们设计的。

L.Class.extend()

要在Leaflet中创建任何子类,请使用该.extend()方法。这接受一个参数:具有键值对的普通对象,每个键是属性或方法的名称,每个值是属性的初始值,或方法的实现:

var MyDemoClass = L.Class.extend({

    // A property with initial value = 42
    myDemoProperty: 42,   

    // A method 
    myDemoMethod: function() { return this.myDemoProperty; }
    
});

var myDemoInstance = new MyDemoClass();

// This will output "42" to the development console
console.log( myDemoInstance.myDemoMethod() );   

命名类,方法和属性时,请遵循以下约定:

  • 功能,方法,属性和工厂名称应该在lowerCamelCase
  • 班级名称应该在UpperCamelCase
  • 私有属性和方法以下划线(_)开头。这不会使它们变得私密,只是建议开发人员不要直接使用它们。

L.Class.include()

如果已定义类,则可以重新定义现有属性/方法,或者可以使用.include()以下方法添加新属性/方法:

MyDemoClass.include({

    // Adding a new property to the class
    _myPrivateProperty: 78,
    
    // Redefining a method
    myDemoMethod: function() { return this._myPrivateProperty; }

});

var mySecondDemoInstance = new MyDemoClass();

// This will output "78"
console.log( mySecondDemoInstance.myDemoMethod() );

// However, properties and methods from before still exist
// This will output "42"
console.log( mySecondDemoInstance.myDemoProperty );

L.Class.initialize()

在OOP中,类具有构造函数方法。在Leaflet中L.Class,构造函数方法总是被命名initialize。

如果你的类有一些特定的options,那么L.setOptions()在构造函数中初始化它们是个好主意。此实用程序函数将提供的选项与类的默认选项合并。

var MyBoxClass = L.Class.extend({

    options: {
        width: 1,
        height: 1
    },

    initialize: function(name, options) {
        this.name = name;
        L.setOptions(this, options);
    }
    
});

var instance = new MyBoxClass('Red', {width: 10});

console.log(instance.name); // Outputs "Red"
console.log(instance.options.width); // Outputs "10"
console.log(instance.options.height); // Outputs "1", the default

Leaflet options以特殊方式处理属性:父类可用的选项将由子类继承:

var MyCubeClass = MyBoxClass.extend({
    options: {
        depth: 1
    }
});

var instance = new MyCubeClass('Blue');

console.log(instance.options.width); // Outputs "1", parent class default
console.log(instance.options.height); // Outputs "1", parent class default
console.log(instance.options.depth); // Outputs "1"

子类运行父的构造函数,然后运行自己的构造函数是很常见的。在传单中,这是使用L.Class.addInitHook()。此方法可用于“挂钩”在类’之后运行的初始化函数,initialize()例如:

MyBoxClass.addInitHook(function(){
    this._area = this.options.width * this.options.length;
});

这将在initialize()调用之后运行(调用setOptions())。这意味着this.options存在并且在init钩子运行时有效。

addInitHook 有一个替代语法,它使用方法名称并可以填充方法参数:

MyCubeClass.include({
    _calculateVolume: function(arg1, arg2) {
        this._volume = this.options.width * this.options.length * this.options.depth;
    }
});

MyCubeClass.addInitHook('_calculateVolume', argValue1, argValue2);

父类的方法

调用父类的方法是通过到达父类的原型并使用来实现的Function.call(…)。例如,可以在以下代码中看到L.FeatureGroup:

L.FeatureGroup = L.LayerGroup.extend({

    addLayer: function (layer) {
        …
        L.LayerGroup.prototype.addLayer.call(this, layer);
    },
    
    removeLayer: function (layer) {
        …
        L.LayerGroup.prototype.removeLayer.call(this, layer);
    },

    …
});

调用父的构造函数是以类似的方式完成的,而是使用ParentClass.prototype.initialize.call(this, …)。

工厂

大多数Leaflet类具有相应的工厂功能。工厂函数与类具有相同的名称,但lowerCamelCase代替UpperCamelCase:

function myBoxClass(name, options) {
    return new MyBoxClass(name, options);
}

命名规范

为Leaflet插件命名类时,请遵循以下命名约定:

  • 永远不要在插件中公开全局变量。
  • 如果您有一个新类,请将其直接放在L命名空间(L.MyPlugin)中。
  • 如果继承其中一个现有类,请将其设为子属性(L.TileLayer.Banana)。

 

发表评论

您的电子邮箱地址不会被公开。

CAPTCHAis initialing...