添加右鍵菜單,首先我們要監聽鼠標右鍵點擊的操作,我們知道鼠標右鍵事件名是 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 結構如下圖:
圖1 形成的DOM結構
我們會想到在這個 map div 元素添加事件,然後右鍵彈出菜單,這個想法很自然,也確實可以實現,然而我們要想到後面的事情,至少對事情有一個全局的認識再下手,我們顯示出菜單後,往往是要根據相應的地圖所在位置進行一定的操作,那麼我們的 contextmenu 的事件對象包含發生點擊的屏幕坐標,但是如何根據屏幕坐標獲得地圖中的相應坐標系下的坐標將會比較困難。
困難在哪裡呢?主要有以下的三點:
首先,事件對象所含的坐標是相對於整個浏覽器的視口、頁面或者整個屏幕的;
其次,而顯示地圖的元素往往又是隨意的大小和位置;
最後,屏幕的坐標系和地圖的坐標系又往往完全不同,如何將相對與地圖元素的坐標再轉化為地圖坐標系下的坐標?
首先,我們需要獲得事件坐標相對於 map div (包含地圖的元素)的坐標,然後將相對於 map div 的坐標轉化為地圖中的實際坐標。第一步中,我們可以通過計算獲得,但是第二步必須通過 openlayers 來完成,因為只有 openlayers 對地圖的坐標系最清楚,這在 openlayers 中也有相關的功能。慶幸的是,openlayers 中我們可以一步完成上述操作,只需要一個函數:map.getEventCoordinate(event),在下面的具體實現中,我會詳細講到這個函數。
下面我們看看具體如何實現吧。
鼠標右鍵菜單具體實現
為了方便,文章中的代碼使用了 JQuery。
文章中的實例完整代碼可以到我的 GitHub 中查看或者下載,有用的話別忘了點一下 star。
下面我們一步一步地添加右鍵菜單功能,我們分為三步:
對 html 元素添加 contextmenu 事件;
獲取地圖相應的點擊坐標;
地圖相應位置添加菜單 。
對 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 事件綁定並無二致,所以沒有展開。