新增右鍵選單,首先我們要監聽滑鼠右鍵點擊的操作,我們知道滑鼠右鍵事件名稱是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 事件绑定并无二致,所以没有展开。