地图作为信息的载体和呈现方式,是GIS的重要组成部分,它是一个浏览信息的窗口,在信息日益发达的今天 ,各种地图应用如雨后春笋一般出现在大众眼前,而不是像以往一样太过局限于专业的领域。而弹窗,是作为地图信息的补充说明和描述的重要呈现方式,也广泛应用于各种地图应用中。一个好的前端界面的设计要灵活地使用空间,也要生动地完成与用户的交互,而在地图应用中,弹窗使用得好,不但会让人感觉舒适也会方便和增加与用户的交互,提升用户体验。如何使用和如何更好地使用iClient for JavaScript的弹窗(Popup)类,便是本文要探讨的话题。
一、 基础
1. 分类
普通弹窗(SuperMap.Popup):同时也是弹窗类的基类,不仅可以直接new出来,还能被子类继承其公开的属性和方法,JS API手册列举的属性和方法均可被其子类继承。其默认出现位置是,弹窗左上角在给定坐标点。
其默认效果如下↓:
固定锚点位置弹窗(SuperMap.Popup.Anchored):固定锚点位置的的浮动弹窗,可以围绕指定位置四周自适应显示。即当弹窗定位点(lonlat)在当前地图可视范围边缘时,会自动调整弹窗哪个角停靠在定位点。
其默认效果如下↓:
带小尾巴的弹窗(SuperMap.Popup.FramedCloud):具有指向和边框的浮动弹窗。它虽然没有边缘自适应位置的效果,但具有边缘时移动地图使弹窗显示完整的效果和阴影效果。
其默认效果如下↓:
2. 使用
弹窗类的使用很简单,new一个加到地图里就好了,这里以FramedCloud为例,上代码:
1 var map = new SuperMap.Map("map"); //引号内map为页面div元素ID 2 3 //初始化FramedCloud类 4 var framedCloud = new SuperMap.Popup.FramedCloud( 5 "chicken", //弹窗的唯一标识ID,即加载在地图上的弹窗div的ID 6 new SuperMap.LonLat(11339634, 4588716), //当前地图上的坐标点,用于定位弹窗位置 7 new SuperMap.Size(120,60), //弹窗内容的大小,可以为null 8 '弹窗内容,相当于div元素的innerHTML 9', 10 null, //锚点,即弹窗怎么定位到坐标点 11 true, //bool,是否显示关闭按钮 12 null, //Function,关闭弹窗触发该回调函数 13 false //是否显示阴影,默认为true 14 ); 15 framedCloud.fixedRelativePosition=true; // 是否固定相对位置,默认为false。 16 var layer = new SuperMap.Layer.CloudLayer(); //初始化超图云图层 17 map.addLayers([layer]); //添加超图云图层 18 map.setCenter(new SuperMap.LonLat(11339634, 4588716), 4); //设置显示的中心点及比例尺级别 19 map.addPopup(framedCloud); //将弹窗加载到map控件上,即放在地图上
就这样?就这样。
JS API类参考里有更多的属性和方法,可以自己去调整、设置。上面的代码只是初始化时就给放个弹窗,显然不符合我们的需要,但是已经足够说明它的用法了。弹窗基础的用法就是这么用,弹窗内容的部分,你可以自由发挥了,可以使用第三方插件,JQuery UI(教程)、BootStrap等等,你可以自由定制弹窗的内容。
一般我们怎么用?最常见的就是点击地图上的图标,任何可以注册点击事件的地方来添加弹窗。下面举个例子:
点击点对象,显示弹窗:
这里使用SuperMap.Layer.Vector图层上的点对象(其他对象也一样),点对象怎么得到,怎么风格化处理就不多说了,我们来看看它需要什么参数:
首先,最重要的参数,定位点,它是SuperMap.LonLat类型的,所以我们得想办法得到所点击的点的坐标。SuperMap.Layer.Vector图层上要素的点击事件可以用SuperMap.Control.SelectFeature来注册,设置onSelect属性并且设置其repeat属性为true,或者使用callbacks属性(JS API类参考可以看到支持的事件)来实现点击事件:
1 var selectFeature = new SuperMap.Control.SelectFeature(VectorLayer,{onSelect:onFeatureSelect,repeat:true,selectStyle:style}); 2 //onSelect:{Function},当要素被选中时调用该方法,要求用户定义具体方法,该方法接收当前选中要素作为参数。 3 //function onFeatureSelect(e){};
顺便说下,设置VectorLayer的要素选中状态样式的方法有很多,上面就是其中之一。
我们来看看点击(这里是重复触发的选中事件)事件触发后,传给回调函数的参数里有没有我们想要的坐标参数,用console.log(e)
看看:
虽然没有直接的lonlat对象,但好在有geometry对象,它就是这个点对象的信息,可是类参考里,SuperMap.Geometry.Point点对象没有直接获取lonlat对象的方法和属性,一种方法是new一个SuperMap.LonLat对象,我这里使用另外一种Geometry对象通用的方法,先用geometry.getBounds()
方法获取Geometry对象的Bounds,再使用SuperMap.Bounds对象的Bounds.getCenterLonLat()
方法获取其中心坐标点lonlat对象:
1 var lonlat= e.geometry.getBounds().getCenterLonLat();
有了定位点,接下来要怎么做就不用我多说了吧?这里再补充一下锚点属性的使用,它是用来定位弹窗相对于定位点位置用的,一般使用SuperMap.Icon对象,它不会在弹窗任何位置添加一个图标,使用它是因为不需要再构造一个包含大小信息和偏移量信息的对象,使用它可以使你的弹窗停靠在点图标图片的上方而不至于让小尾巴与图标重叠,效果对比如下↓:
有了上面的例子,那么其他Geometry对象就都ok了,只要参数给对了就行。
另外,有的朋友不想使用客户端图层再弹窗,那怎么办呢?iServer 7C开始支持属性瓦片,同时客户端也有相关对接的类,即:SuperMap.Layer.UTFGrid。JS API有这两个图层使用的相关示例,这里稍稍提示下:
UTFGrid使用SuperMap.Control.UTFGrid控件注册事件,支持的事件类型有点击(click)、悬停(hover)和拖拽(move)。
再补充个,为处理海量数据而生的麻点图,使用SuperMap.Control.GOIs控件注册其事件。
3.其他
- 视具体弹窗类型不同,其构造函数的参数数量有所不同,FramedCloud的构造函数已包含了别的类型弹窗的所有参数,所以其余弹窗类型不再赘述。
- 了解一下它们的继承关系,子类继承父类的属性和方法。
二、 进阶
类参考里的方法和属性及其组合不能做到你想要做的效果?总之就是想做通过iClient for JavaScript做不到的弹窗效果?看这里就对了,之所以是进阶而不是高级,是因为我下面要说的其实很简单,也不需要去对JS API的源码进行修改、重写和扩展。好了我就不卖关子了,一句话就可以说清楚:
添加到地图上的弹窗是div。
好了,我说完了,下课。
就这样?就这样!早该想到的是不是?这句话说完其实就已经很好的总结了接下来的内容了,那么接下来就简单的介绍下,然后小伙伴们就可以愉快地玩耍、尽情地发挥想象力了。
1. 弹窗内边距、滚动条、边框、阴影、半透明等等效果和动画效果
看图↓:
想起什么了吗?弹窗这个div的ID就是我们new这个弹窗时第一个参数给的,而知道了整个弹窗的div及其嵌套关系后这第一个问题就算解决了是吧?虽然类参考里有部分比如边框、透明度样式设置的方法,但根据这个来设置显然更灵活。
另外,通过iClient JS主题包(theme文件夹)中的style.css文件,可以找到部分弹窗类样式的定义:
1 .smFramedCloudPopupContent { 2 padding: 5px; 3 overflow: auto; 4 }
不建议在原文件修改(实在要改记得备份),你需要的只是定义个CSS样式覆盖或追加到默认样式,为保证覆盖掉默认样式,可以在样式后面加 !important,比如:
1 .smFramedCloudPopupContent{ 2 overflow:hidden !important; 3 padding:0 !important; 4 }
上面的效果就是弹窗内容(你写的那部分的外层div)不显示滚动条,内边距为0。
当然,你可以在style.css(在\theme\default下)文件中搜索Popup相关的样式来设置,但是通过浏览器查看元素更直观方便,之所以要去查看下默认样式的定义是为了避免自定义样式不生效,现在你知道了,加个!important 覆盖掉默认的就好了。
到此,你可以使用各种UI、动画库(推荐animate.css)了,可以尽情发挥了。
2. 实现弹窗随鼠标移动或随地图上点移动的思路
map上加上弹窗后,如何实现移动弹窗?每次位置更改(onlat属性)先移除弹窗再添加可不可以呢?当然是可以的,如果你不需要和用户交互也不考虑弹窗多的情况下的性能的话。这是一种方法,但是不用考虑。
首先,收集必要信息:
1. 我们已经知道弹窗的ID(new 弹窗时自己给的,也可以在浏览器里查看),要用要改变它的位置,只需要设置它样式的left和top值,怎么获取呢?
2.看看popup对象除了是个div外,有没有在map对象里
先看看map控件下有什么属性和方法:
属性 | 描述 |
---|---|
popups | {Array(SuperMap.Popup)} 地图上的弹窗列表。 |
方法 | 描述 |
---|---|
getViewPortPxFromLayerPx | 根据图层像素点坐标获取视图窗口像素点坐标 |
getViewPortPxFromLonLat | 根据指定地理位置,返回其相对于当前地图窗口左上角的像素位置 |
getPixelFromLonLat | 获取地图上的像素坐标。依照当前baselayer,将指定的地理点位置坐标, 转换成其相对于地图窗口左上角点的像素坐标 |
getLonLatFromPixel | 根据相对于地图窗口左上角的像素位置,返回其在地图上的地理位置。依据当前baselayer转换成 lon/lat (经度/纬度)形。 |
getLayerPxFromLonLat | 根据传入的大地坐标获取图层坐标对象 |
getLayerPxFromViewPortPx | 根据视图窗口像素点坐标获取图层像素点坐标 |
removeAllPopup | 移除所有弹出窗口。 |
removePopup | 移除指定的弹出窗口。 |
对于第一种思路要求,使用getLonLatFromPixel和getPixelFromLonLat方法就可以实现了,而且不需要计算地图容器在页面中的位置,不过这样改了后,弹窗对象的lonlat等属性不会变化,需要的话可以手动改下它的属性;另外,自带的阴影(多个div拼合而成)也不会跟着移动,也需要手动改,跟上面的方法一样,或者不使用自带的阴影,而使用用弹窗DIV的CSS阴影样式,这里用setInterval简单模拟了下:
1 function mouseClickHandler(e){ 2 closeInfoWin(); 3 4 var framedCloud= new SuperMap.Popup.FramedCloud( 5 "chicken", 6 e.object.lonlat, 7 null, 8 '这里相当于innerHTML部分', 9 new SuperMap.Icon('', new SuperMap.Size(21,25), new SuperMap.Pixel(-10, -20)), 10 true, 11 null, 12 true 13 ); 14 framedCloud.panMapIfOutOfView=true; 15 infowin = framedCloud; 16 map.addPopup(framedCloud); 17 //在一定范围内随机更改弹窗位置 18 setInterval(function(){ 19 var px=map.getPixelFromLonLat(new SuperMap.LonLat(e.object.lonlat.lon+Math.random()*100000,e.object.lonlat.lat+Math.random()*100000)); 20 document.getElementById("chicken" ).style.left=px.x+"px"; 21 document.getElementById("chicken" ).style.top=document.getElementById("chicken" ).style.height+px.y; 22 },100); 23 }
对于第二种思路,当然就是获取到popup对象,改它的lonlat属性,但是你会发现改了之后,弹窗并没有移动,因为你只是改了对象的属性,div并不受影响,除非刷新地图或重绘div。但是,还是有办法的,用SuperMap.Popup.updateSize()方法就可以了:
1 setInterval(function(){ 2 var lonlat=new SuperMap.LonLat(e.object.lonlat.lon+Math.random()*100000,e.object.lonlat.lat+Math.random()*100000); 3 map.popups[0].lonlat=lonlat; 4 map.popups[0].updateSize(); 5 },1000); 6 }
二者可以结合使用,第一种思路更为灵活。浏览器里多按f12,这很重要。
关于弹窗类,进阶的内容就到这里了,高级的内容大家就自己探索吧。希望本文能给你一些启发、开阔一些思路。