1. 程式人生 > >OpenLayers 3 之 新增地圖滑鼠右鍵選單

OpenLayers 3 之 新增地圖滑鼠右鍵選單

      新增右鍵選單,首先我們要監聽滑鼠右鍵點選的操作,我們知道滑鼠右鍵事件名是 contextmenu,當滑鼠在 html 元素之上,點選滑鼠右鍵,便會觸發 contextmenu 事件,在 contextmenu 事件的回撥函式中實現相應的顯示選單功能即可。

      那麼在 openlayers 中,在地圖中新增這個事件,我們從哪裡下手呢?首先我們得了解 openlayers 的初始化頁面的過程。

openlayers 初始化頁面過程

      openlayers 也是一個前端庫,那麼它肯定離不開 html 的運用,比如,我們首先需要在頁面放置一個顯示地圖的 html 元素,一個 div 元素(假設其 id 屬性設定為 “map”,後面簡稱為 map div),然後在地圖初始化的時候指定這個元素,openlayers 會首先在這個元素中建立一個 class 為 ol-viewport

的 div 元素,其尺寸保持與 map div 相同,然後在 ol-viewport div 中建立一個 canvas 元素,在這個 canvas 元素中渲染請求到的地圖;其次,還會新增一個 class 為 ol-overlaycontainer 的 div 元素,用來放置 overlay;最後,新增一個 class 為 ol-overlaycontainer-stopevent 的 div 元素,主要是放置 openlayers 的控制元件,上一篇新增 自定義擴充套件控制元件 的文章開篇有講過,這裡不是重點,我們不詳細介紹了。

最後形成的 html dom 結構如下圖:

形成的DOM結構
圖1 形成的DOM結構

      我們會想到在這個 map div 元素新增事件,然後右鍵彈出選單,這個想法很自然,也確實可以實現,然而我們要想到後面的事情,至少對事情有一個全域性的認識再下手,我們顯示出選單後,往往是要根據相應的地圖所在位置進行一定的操作,那麼我們的 contextmenu 的事件物件包含發生點選的螢幕座標,但是如何根據螢幕座標獲得地圖中的相應座標系下的座標將會比較困難。

困難在哪裡呢?主要有以下的三點:

  • 首先,事件物件所含的座標是相對於整個瀏覽器的視口、頁面或者整個螢幕的;
  • 其次,而顯示地圖的元素往往又是隨意的大小和位置;
  • 最後,螢幕的座標系和地圖的座標系又往往完全不同,如何將相對與地圖元素的座標再轉化為地圖座標系下的座標?

      首先,我們需要獲得事件座標相對於 map div (包含地圖的元素)的座標,然後將相對於 map div 的座標轉化為地圖中的實際座標。第一步中,我們可以通過計算獲得,但是第二步必須通過 openlayers 來完成,因為只有 openlayers 對地圖的座標系最清楚,這在 openlayers 中也有相關的功能。慶幸的是,openlayers 中我們可以一步完成上述操作,只需要一個函式:map.getEventCoordinate(event),在下面的具體實現中,我會詳細講到這個函式。

下面我們看看具體如何實現吧。

滑鼠右鍵選單具體實現

  • 為了方便,文章中的程式碼使用了 JQuery。
  • 文章中的例項完整程式碼可以到我的 GitHub 中檢視或者下載,有用的話別忘了點一下 star。

下面我們一步一步地新增右鍵選單功能,我們分為三步:

  1. 對 html 元素新增 contextmenu 事件;
  2. 獲取地圖相應的點選座標;
  3. 地圖相應位置新增選單 。

對 html 元素新增 contextmenu 事件

      html 元素的滑鼠右鍵事件名為 contextmenu,這個事件所有主流瀏覽器都支援,這裡不要混淆 html 新增的屬性 contextmenu,這個屬性目前只有 firefox 支援,我們只是使用 oncontextmenu 這個事件。對包含地圖的任何 html 元素繫結這個事件都可以,openlayers 會處理座標轉換這些問題。如下,map.getViewport() 會返回 openlayers 初始化頁面時建立的 class 為 ol-viewport 的 div 元素,也就是直接包含地圖的元素。因為瀏覽器都有預設的右鍵選單,所以我們要取消預設的選單,只要呼叫 e.preventDefault(); 即可:

$(map.getViewport()).on("contextmenu", function(event){
    e.preventDefault();
    // 書寫事件觸發後的函式
});

獲取地圖相應的點選座標

獲取地圖相應的點選座標只需要一句即可,如下,

var coordinate = map.getEventCoordinate(event);

      函式引數是 oncontextmenu 對應的事件物件,該函式包含對 map.getCoordinateFromPixel() 的呼叫,map.getCoordinateFromPixel() 引數為 ol.pixel,是一個座標,陣列格式[x, y],其實現中又呼叫了 ol.vec.Mat4.multVec2(),該函式完成處理座標轉換的實際工作。

地圖相應位置新增選單

      這裡我們結合 overlay 新增選單,之前的文章介紹過 overlay,這裡就不再具體展開了。首先,我們在 html 頁面新增一個目錄,具體的 css 樣式可以自己設定,想看完整原始碼的可以到我的 GitHub 中檢視或者下載完整的程式碼:

<div id="contextmenu_container" class="contextmenu">
    <ul>
        <li><a href="#">設定中心</a></li>
        <li><a href="#">新增地標</a></li>
        <li><a href="#">距離丈量</a></li>
    </ul>
</div>

使用這個 html 元素初始化一個 overlay,並將 overlay 新增到地圖中:

var menu_overlay = new ol.Overlay({
    element: document.getElementById("contextmenu_container"),
    positioning: 'center-center'
});
menu_overlay.setMap(map);

接下來,我們就可以在滑鼠右鍵選單的事件回撥函式中,根據獲取的地圖座標位置,設定 overlay 的顯示位置:

menu_overlay.setPosition(coordinate);

選單隱藏

      當我們滑鼠點選右鍵,選單出現,但是我們不能讓選單總是顯示在地圖中,這時我們可以新增滑鼠左鍵單擊,選單消失功能,或者當選擇某項功能時,選單消失。這個比較容易實現,只要一句便可以實現,放在滑鼠左鍵事件的回撥函式或者選單功能執行函式中就行,如下:

menu_overlay.setPosition(undefined);

總結

      這篇文章中,主要講了 openlayers 初始化頁面地圖元素的過程,並介紹了在地圖上實現“滑鼠右鍵選單功能”,和隱藏選單的實現。我們並沒有對選單中的條目繫結事件,因為我們的重點在於實現右鍵選單,對於選單的條目要繫結什麼功能,和普通的 javascript 事件繫結並無二致,所以沒有展開。

好的,就寫到這裡,有什麼問題,可以在文章下面留言或者給我發郵件。

文章中的例項完整程式碼可以到我的 GitHub 中檢視或者下載,有用的話別忘了點一下 star。