OpenLayers 3 之 擴充套件自定義控制元件--以切換圖層控制元件為例
openlayers 中的控制元件,是一個固定在頁面某個位置的可見的DOM元素,它們可能包含可操作的按鈕,也可能只是單純的展示資訊,它們的位置及其樣式是由與其關聯的CSS樣式決定的。預設情況下,它們都位於一個CSS 類(class)為ol.overlaycontainer-stopevent
的元素內,當然也可以使用其他的自定義的DOM容器元素。
在openlayers 的結構中,控制元件有很多,比如左上角的縮放控制元件、右下角的屬性控制元件,這些控制元件都是繼承一個基類 ol.control.Control
,基類主要為它的子類提供統一的“阻止事件傳播機制”,並且 ol.control.Control
ol.Object
,並且控制元件都放於一個統一的容器 ol.Collection
,所以如果繼承ol.control.Control
基類,自定義的控制元件不僅可以得益於其“阻止事件傳播機制”,也可以使用 ol.Object
和ol.Collection
的方法和事件。
ol.control.Control 簡介
ol.control.Control
是openlayers的控制元件基類,我們的自定義控制元件可以繼承該基類(在上邊我們也講了繼承該基類的好處),在類中使用 javascript 動態建立DOM元素,指定DOM元素對應的CSS樣式類(class),並對DOM元素繫結相應的事件,來完成自定義控制元件。openlayers3 使用了google 的 closure 庫來進行開發,要使用 closure 的語法來進行繼承,比如我們定義我們的切換圖層控制元件類為 ol. control.LayerSwitcher
ol.inherits(ol.control.LayerSwitcher, ol.control.Control);
還要注意一點就是,我們要在控制元件類的‘建構函式’(加引號是因為javascript建構函式的概念不明顯)中呼叫基類的建構函式,我們可以使用 call 或者 apply 方法(因為本質上javascript的類是函式模擬的),或者直接使用:new ol.control.Control({element: myElement})
。
當然,我們完全可以不用繼承ol.control.Control
,但最好不要這樣做,首先,繼承基類有助於保持 openlayers 的組織結構清晰明瞭;其次,我們可以使用 ol.control.Control
ol.control.Control
繼承了ol.Object
,所以我們也可以使用它的方法。
具體實現–以GitHub中的專案為例
原本在 openlayers 2 中,圖層切換控制元件是“標配”,而在 openlayers 3 中預設沒有這個控制元件,那麼我們可以著手擴充套件實現一個。在GitHub中就有一個現成的專案,專案地址為:https://github.com/walkermatt/ol3-layerswitcher ,感興趣的可以看看,在我們瞭解了具體思想和實現思路的時候,就是具體的程式碼實現,並在實踐中檢驗,不斷的修正,然而相對於使用現存的經過了這些過程的開源專案,我們為何不適用現存的‘輪子’呢?當然前提是我們懂得它的實現,可以針對與自己的需求進行改進,當然其遵循的開源協議要允許我們這樣做。
做一件事情,必須要明確目的和具體需求,並針對需求進行設計,不要急著實現,其次才是尋找實現的方法,所以首先明確我們的功能需求:
- 地圖上一個可見的切換圖層DOM元素,當滑鼠懸浮在元素之上時,以列表形式展開所有圖層;
- 每個圖層前面有一個複選框,當勾選時,顯示該圖層,沒有勾選時,隱藏該圖層;
- 宣告為底圖的圖層,列表項前面載入單選框(radio),不能取消選擇。
經過比較粗糙的需求分析,我們著手實現。首先使用建構函式模式
定義我們的基類屬性和事件,然後定義對外的方法。
定義基類
ol.control.LayerSwitcher = function(opt_options) {
var options = opt_options || {};
var tipLabel = options.tipLabel ?
options.tipLabel : 'Legend';
this.mapListeners = [];
this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
this.shownClassName = this.hiddenClassName + ' shown';
var element = document.createElement('div');
element.className = this.hiddenClassName;
var button = document.createElement('button');
button.setAttribute('title', tipLabel);
element.appendChild(button);
this.panel = document.createElement('div');
this.panel.className = 'panel';
element.appendChild(this.panel);
var this_ = this;
element.onmouseover = function(e) {
this_.showPanel();
};
button.onclick = function(e) {
this_.showPanel();
e.preventDefault();
};
element.onmouseout = function(e) {
e = e || window.event;
if (!element.contains(e.toElement)) {
this_.hidePanel();
}
};
ol.control.Control.call(this, {
element: element,
target: options.target
});
};
ol.inherits(ol.control.LayerSwitcher, ol.control.Control);
從程式碼中可以看出,從第六行開始,便是定義了兩個CSS類,分別為控制元件收縮和展開時的樣式,接著便開始定義DOM元素,一個按鈕,兩個DIV元素,分別問容器DIV和麵板(panel,展示內容的地方)DIV,接著便給按鈕綁定了滑鼠點選、懸浮和離開的事件處理。最後呼叫了基類的建構函式,並使用closure的語法,繼承了基類ol.control.Control
。
在定義的事件處理機制中,我們看到 showPanel
和 hidePanel
方法,這都是在原型鏈中定義的方法,下面我們看都定義了那些方法。
定義對外的方法
定義的方法主要如下,其核心思想就是針對使用者與控制元件的互動行為,來完成對地圖的互動,也就是對地圖各個圖層的可見性的設定,涉及的物件包含 map 和 layers,當然還有定義的DOM元素。因為這些方法的具體實現與我們本文要將的實現自定義控制元件的思想關係不大,這裡就知識列出其主要的功能:
共有方法
- hidePanel,隱藏顯示圖層列表的面板DIV;
- showPanel,顯示圖層列表的面板DIV;
- renderPanel,重新繪製面板DIV;
- setMap,設定與之關聯的 map 物件。
私有方法
- ensureTopVisibleBaseLayerShown_,當有多個圖層是可見(勾選)狀態,確保只有最上層的圖層是可見的;
- setVisible_,設定圖層的可見性;
- renderLayer_,渲染一個layerGroup包含的所有圖層;
- renderLayers_,
以及一個靜態的方法:ol.control.LayerSwitcher.forEachRecursive
,其作用是針對一個 layerGroup 中每個圖層呼叫一個回撥函式。
總結
本文主要介紹了在 openlayers 基礎上擴充套件切換圖層控制元件的原理和需要注意的問題,要自己寫出擴充套件,首先你要對web前端知識的理解達到一個比較高的程度,還要對 openlayers 有比較好的認識,對面向物件的思想在 javascript 中的實現,也要比較瞭解,才能寫出質量比較高的擴充套件。
好的,就寫到這裡,有什麼問題,可以在文章下面留言或者給我發郵件。