
图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(),该函数完成处理坐标转换的实际工作。
<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);总结