标签:
模仿是最好的学习,这次我们继续山寨百度,通过自定义Infowindow来实现百度风格的BubblePopup
先打开百度地图,按下f12吧BubblePopup的HTML代码和CSS代码拷贝下来,这里我无耻的把类名改了,大家不要在意细节。
HTML模板
1<divclass="dextra-bubble-pop-center"style="z-index: 3; position: relative; height: 50px; width: 160px;"> 2<divclass="dextra-bubble-pop-content" 3style="display: block; width: 160px; height: 50px; overflow-x: auto; overflow-y: hidden;"> 4<divid="poi_info_window"class="dextra-poi-info-window"> 5<divclass="left name-wrap"><spanclass="name"></span></div> 6</div> 7</div> 8</div> 9<divclass="dextra-bubble-pop-bottom"style="display: block; z-index: 2; width: 160px; left: 72px;"> 10<span></span> 11</div>
CSS代码
1 2 .dextra-bubble-pop {
3position: absolute;
4z-index: 100;
5box-sizing: border-box;
6box-shadow: 1px 2px 1px rgba(0, 0, 0, .15);
7background-color: #FFF;
8 }
9 10 .dextra-poi-info-window {
11padding: 4px 0;
12 }
13 14 .dextra-poi-info-window .left {
15padding-left: 10px;
16padding-right: 10px;
17height: 40px;
18line-height: 40px;
19display: table;
20table-layout: fixed;
21width: 140px;
22text-align: center;
23 }
24 25 .dextra-poi-info-window .name-wrap .name {
26vertical-align: middle;
27font-size: 14px;
28font-weight: 700;
29white-space: nowrap;
30overflow: hidden;
31text-overflow: ellipsis;
32display: block;
33 }
34 35 .dextra-bubble-pop-bottomspan {
36position: absolute;
37left:72px;
38width: 16px;
39height: 10px;
40background-image: url("../images/tail_shadow.png");
41 }
要实现BubblePopup,实际上就是自定义一个InfoWindow,我们可以通过继承InfoWindowBase来实现。要实现自定义的InfoWindow。我们可以先参考一下官方的例子Custom info window,注意,这个例子是有缺陷的,如果当infowindow超出当前视图边界就会出现滚动条。下载官方的实例,我们打开infoWindow.js文件。
1 define([
2 "dojo/Evented",
3 "dojo/parser",
4 "dojo/on",
5 "dojo/_base/declare",
6 "dojo/dom-construct",
7 "dojo/_base/array",
8 "dojo/dom-style",
9 "dojo/_base/lang",
10 "dojo/dom-class",
11 "dojo/fx/Toggler",
12 "dojo/fx",
13 "dojo/Deferred",
14 "esri/domUtils",
15 "esri/InfoWindowBase"
16 17 ],
18function(
19 Evented,
20 parser,
21 on,
22 declare,
23 domConstruct,
24 array,
25 domStyle,
26 lang,
27 domClass,
28 Toggler,
29 coreFx,
30 Deferred,
31 domUtils,
32 InfoWindowBase
33 ) {
34return declare([InfoWindowBase, Evented], {
35 36 isContentShowing :false,
37 38constructor: function(parameters) {
39 40 41 lang.mixin(this, parameters);
42 43 44 domClass.add(this.domNode, "myInfoWindow");
45 46this._closeButton = domConstruct.create("div",{"class": "close", "title": "Close"}, this.domNode);
47this._title = domConstruct.create("div",{"class": "title"}, this.domNode);
48this._content = domConstruct.create("div",{"class": "content"}, this.domNode);
49 50this._toggleButton = domConstruct.create("div",{"class": "toggleOpen", "title": "Toggle"}, this.domNode);
51 52var toggler = new Toggler({
53 "node": this._content,
54 showFunc: coreFx.wipeIn,
55 hideFunc: coreFx.wipeOut
56 });
57 toggler.hide();
58 59 on(this._closeButton, "click", lang.hitch(this, function(){
60//hide the content when the info window is toggled close. 61this.hide();
62if(this.isContentShowing){
63 toggler.hide();
64this.isContentShowing = false;
65 domClass.remove(this._toggleButton);
66 domClass.add(this._toggleButton, "toggleOpen");
67 }
68 }));
69 on(this._toggleButton, "click", lang.hitch(this, function(){
70//animate the content display 71if(this.isContentShowing){
72 73 toggler.hide();
74this.isContentShowing = false;
75 domClass.remove(this._toggleButton);
76 domClass.add(this._toggleButton,"toggleOpen");
77 78 }else{
79 toggler.show();
80this.isContentShowing=true;
81 domClass.remove(this._toggleButton);
82 domClass.add(this._toggleButton,"toggleClose");
83 }
84 85 }));
86//hide initial display 87 domUtils.hide(this.domNode);
88this.isShowing = false;
89 90 },
91 setMap: function(map){
92this.inherited(arguments);
93 map.on("pan-start", lang.hitch(this, function(){
94this.hide();
95 }));
96 map.on("zoom-start", lang.hitch(this, function(){
97this.hide();
98 }));
99// map.on("zoom-start", //this, this.hide);100101 },
102 setTitle: function(title){
103this.place(title, this._title);
104105 },
106 setContent: function(content){
107this.place(content, this._content);
108 },
109 show: function(location){
110if(location.spatialReference){
111location = this.map.toScreen(location);
112 }
113114//Position 10x10 pixels away from the specified location115 domStyle.set(this.domNode,{
116 "left": (location.x + 10) + "px",
117 "top": (location.y + 10) + "px"
118 });
119120//display the info window121 domUtils.show(this.domNode);
122this.isShowing = true;
123this.onShow();
124 },
125 hide: function(){
126 domUtils.hide(this.domNode);
127this.isShowing = false;
128this.onHide();
129130 },
131 resize: function(width, height){
132 domStyle.set(this._content,{
133 "width": width + "px",
134 "height": height + "px"
135 });
136 domStyle.set(this._title,{
137 "width": width + "px"
138 });
139140 },
141 destroy: function(){
142 domConstruct.destroy(this.domNode);
143this._closeButton = this._title = this._content = null;
144145 }
146147148 });
149150 });
我们就在此基础上进行改造,不但要实现需求还要解决缺陷。infoWindowBase是继承自_WidgetBase的,我们先来看一下infoWindowBase的官方描述.
我们可以重写infoWindowBase的一些方法,来实现自己的infoWindow。
首先我们先引入我们要用到的模块
1 define([
2 "dojo/Evented",
3 "dojo/on",
4 "dojo/query",
5 "dojo/_base/declare",
6 "dojo/dom-construct",
7 "dojo/dom-attr",
8 "dojo/_base/array",
9 "dojo/dom-style",
10 "dojo/_base/lang",
11 "dojo/dom-class",
12 "dijit/_TemplatedMixin",
13 "esri/domUtils",
14 "esri/InfoWindowBase",
15 "esri/geometry/ScreenPoint",
16 "esri/geometry/screenUtils",
17 "esri/geometry/webMercatorUtils",
18 "dojo/text!./templates/dextraPopup.html"
19 ],
20function (Evented,
21 on,
22 query,
23 declare,
24 domConstruct,
25 domAttr,
26 array,
27 domStyle,
28 lang,
29 domClass,
30 _TemplatedMixin,
31 domUtils,
32 InfoWindowBase, ScreenPoint, screenUtils, webMercatorUtils, template) {
33var showMapPoint = null;
34return declare([InfoWindowBase, Evented, _TemplatedMixin], {
35 isContentShowing: false,
36 templateString: template,
37 _events: [],
38constructor: function (parameters) {
39 lang.mixin(this, parameters);
40 },
41 ...
42 });
对比官方的例子,我去掉了部分模块(coreFx,Toggler),加入了dijit/_TemplateMixin,esri/geometry/webMecratorUtils,
esri/geomtry/srcreenUtils模块。_TemplateMixin是为了使用我在第一步拷贝下来的HTML模板,关于编写基于模板的widget可以到
dojo的官网进行查看;webMecratorUtils和srcreenUtils则是为了实现地理坐标和屏幕坐标的准确转换。
showMapPoint是一个全局的变量,用来记录popup的地理坐标位置。
templateString是_TemplateMixin模块的一个属性,用来保存HTML模板。
_events:是一个数组,用来存储相关的事件,在popup被释放时释放注册的事件。
先用一个私有方法来进行初始化。应为InfoWindowBase是继承自_WidgetBase的,domNode是_WidgetBase的一个属性,用于表示生成Widget的dom节点,可以通过在构造函数里用第二个参数来进行传入,或者在内部自己定义。
1 _createInfoWindowInstance: function (map) {
2this.domNode = domConstruct.create("div", null, map.id + "_root");
3 domClass.add(this.domNode, "dextra-bubble-pop");
4 domStyle.set(this.domNode, {
5 width: "160px",
6 });
7 8this.domNode.innerHTML = this.templateString;
9 10this._content = query("div.name-wrap span.name");
11this._title=query("div.name-wrap");
12//hide initial display 13 domUtils.hide(this.domNode);
14this.isShowing = false;
15 },
注意,我在这里创建了一个div节点,并把它添加到一个id为{map.id}_root({map.id}占位符,用于表示地图的id)的dom节点中,这一步就是解决当infowindow超出当前视图范围时会出现滚动条。我们可以先用arcgis提供的infowindow来试一试,在浏览器中按
f12,我们看一看infowindow是放在哪的。
利用arcgis自带的infowindow,我们可以看到这个infowindow的dom节点被添加到一个id为map_root的div中。在这里,我的map控件的id为“map”,所以它会生成一个id为“map_root”({map.id}_root)的div。所以我们只要把自定生成的popup放到这个节点中,当popup超出当前视图时,会被裁减了,而不是出现滚动条。这里最关键的部分已经完成了,接下来的操作就是如何在地图上展现这个popup。
1 _showInfoWindow: function (extent) {
2if (showMapPoint == null)return;
3var showScreenPoint = screenUtils.toScreenGeometry(extent, this.map.width, this.map.height, showMapPoint);
4 domStyle.set(this.domNode, {
5 "left": (showScreenPoint.x - 80) + "px",
6 "top": (showScreenPoint.y - 76 ) + "px"
7 });
8 9 domUtils.show(this.domNode);
10this.isShowing = true;
11this.onShow();
12 },
13 14 show: function (location) {
15 showMapPoint = location;
16if (webMercatorUtils.canProject(location, this.map)) {
17 showMapPoint = webMercatorUtils.project(location, this.map);
18 }
19if (showMapPoint.spatialReference) {
20var screenPoint = this.map.toScreen(showMapPoint);
21 domStyle.set(this.domNode, {
22 "left": (screenPoint.x - 80) + "px",
23 "top": (screenPoint.y - 76) + "px"
24 });
25 }
26 27//display the info window 28 domUtils.show(this.domNode);
29this.isShowing = true;
30this.onShow();
31 },
_showInfoWindow方法是一个私有方法,用于在地图事件触发时调用。当地图平移,缩放时根据地理坐标从新计算BubblePopup的屏幕坐标。用screenUtils.toScreenGeometry(extent, width, height, mapGeometry)根据地图的范围,宽度,高度,和点计算出相应的屏幕坐标。
show方法是一个公有方法,用于在外部进行调用。在这里利用了arcgis js 提供webMercatorUtils模块,来进行坐标的转换。一般而言,我们都会用经纬度坐标,但是当地图是webMercator投影时,就需要先把经纬度坐标转化成米制坐标,才能在正确的位置显示出来来。
关键的部分已经完成,下面贴出全部代码
1 define([
2 "dojo/Evented",
3 "dojo/on",
4 "dojo/query",
5 "dojo/_base/declare",
6 "dojo/dom-construct",
7 "dojo/dom-attr",
8 "dojo/_base/array",
9 "dojo/dom-style",
10 "dojo/_base/lang",
11 "dojo/dom-class",
12 "dijit/_TemplatedMixin",
13 "esri/domUtils",
14 "esri/InfoWindowBase",
15 "esri/geometry/ScreenPoint",
16 "esri/geometry/screenUtils",
17 "esri/geometry/webMercatorUtils",
18 "dojo/text!./templates/dextraPopup.html"
19 ],
20function (Evented,
21 on,
22 query,
23 declare,
24 domConstruct,
25 domAttr,
26 array,
27 domStyle,
28 lang,
29 domClass,
30 _TemplatedMixin,
31 domUtils,
32 InfoWindowBase, ScreenPoint, screenUtils, webMercatorUtils, template) {
33var showMapPoint = null;
34return declare([InfoWindowBase, Evented, _TemplatedMixin], {
35 36 templateString: template,
37 _events: [],
38constructor: function (parameters) {
39 lang.mixin(this, parameters);
40 },
41 _createInfoWindowInstance: function (map) {
42this.domNode = domConstruct.create("div", null, map.id + "_root");
43 domClass.add(this.domNode, "dextra-bubble-pop");
44 domStyle.set(this.domNode, {
45 width: "160px",
46 });
47 48this.domNode.innerHTML = this.templateString;
49 50this._content = query("div.name-wrap span.name");
51this._title=query("div.name-wrap");
52//hide initial display 53 domUtils.hide(this.domNode);
54this.isShowing = false;
55 },
56 57 setMap: function (map) {
58this.inherited(arguments);
59this._events = [];
60this._createInfoWindowInstance(map);
61this._events.push(map.on("pan", lang.hitch(this, function (evt) {
62if (this.isShowing) {
63this._showInfoWindow(evt.extent);
64 }
65 })));
66 67this._events.push(map.on("zoom-start", lang.hitch(this, function (evt) {
68this.hide();
69 })));
70 71this._events.push(map.on("zoom-end", lang.hitch(this, function (evt) {
72this._showInfoWindow(evt.extent);
73 })));
74 },
75 76 unsetMap: function (map) {
77this.inherited(arguments);
78 array.forEach(this._events, function (event) {
79 event.remove();
80 });
81 },
82 setTitle: function (title) {
83this._title.forEach(function (node) {
84 domAttr.set(node, "title", title);
85 });
86 },
87 88 setContent: function (content) {
89this._content.forEach(function (node) {
90 node.innerHTML = content;
91 });
92 },
93 94 _showInfoWindow: function (extent) {
95if (showMapPoint == null)return;
96var showScreenPoint = screenUtils.toScreenGeometry(extent, this.map.width, this.map.height, showMapPoint);
97 domStyle.set(this.domNode, {
98 "left": (showScreenPoint.x - 80) + "px",
99 "top": (showScreenPoint.y - 76 ) + "px"
100 });
101102 domUtils.show(this.domNode);
103this.isShowing = true;
104this.onShow();
105 },
106107 show: function (location) {
108 showMapPoint = location;
109if (webMercatorUtils.canProject(location, this.map)) {
110 showMapPoint = webMercatorUtils.project(location, this.map);
111 }
112if (showMapPoint.spatialReference) {
113var screenPoint = this.map.toScreen(showMapPoint);
114 domStyle.set(this.domNode, {
115 "left": (screenPoint.x - 80) + "px",
116 "top": (screenPoint.y - 76) + "px"
117 });
118 }
119120//display the info window121 domUtils.show(this.domNode);
122this.isShowing = true;
123this.onShow();
124 },
125 hide: function () {
126if (this.isShowing) {
127 domUtils.hide(this.domNode);
128this.isShowing = false;
129this.onHide();
130 }
131 },
132 resize: function (width, height) {
133 domStyle.set(this._content, {
134 "width": width + "px",
135 "height": height + "px"
136 });
137 },
138 remove: function () {
139this.hide();
140 showMapPoint = null;
141 },
142 destroy: function () {
143 domConstruct.destroy(this.domNode);
144 }
145 });
146 });
147
DEMO:
1<!DOCTYPE html> 2<htmllang="en"> 3<head> 4<metacharset="UTF-8"> 5<title>DExtra-BubublPoopup</title> 6<linkrel="stylesheet"href="https://js.arcgis.com/3.16/esri/css/esri.css"> 7<linkrel="stylesheet"href="../dist/dijit/css/dextraPopup.css"> 8<linkrel="stylesheet"href="css/mainApp.css"> 9<script> 10 var dojoConfig = {
11 parseOnLoad:true,
12 packages: [{
13 name: ‘custom‘,
14 location: location.pathname.replace(/\/[^/]+$/, ‘‘) + ‘/custom‘//从cdn加载自己定义的模块方法
15 },
16 {
17 name: ‘dextra‘,
18 location: ‘/extra.arcgis.3.x/dist/‘//从cdn加载自己定义的模块方法
19 }]
20 };
21</script> 22<scriptsrc="https://js.arcgis.com/3.16/"></script> 23<script> 24 require([
25 "dojo/dom",
26 "dojo/on",
27 "esri/map",
28 "esri/symbols/SimpleMarkerSymbol",
29 "esri/InfoTemplate",
30 "esri/layers/GraphicsLayer",
31 "dextra/layers/GoogleVectorLayer",
32 "dextra/dijit/DEBubblePopup",
33 "dojo/domReady!"],
34 function (dom, on,
35 Map, Graphic, SimpleMarkerSymbol, InfoTemplate, GraphicsLayer,
36 GoogleVectorLayer,DEBubblePopup) {
37 38 var infoWindow = new DEBubblePopup();
39 var map = new Map("map", {
40 showAttribution: false,
41 center: [102.3, 24.6],
42 autoResize: true,
43 sliderPosition: "bottom-right",
44 logo: false,
45 infoWindow:infoWindow,
46 zoom:12
47 });
48 49 var googleVect = new GoogleVectorLayer();
50 map.addLayer(googleVect);
51 52 var measureLayer = new GraphicsLayer({id: "infoWindowTest"});
53 map.addLayer(measureLayer);
54 on(dom.byId("infowindow"), "click", function (e) {
55 on.once(map, "click", function (evt) {
56 console.log(map._container);
57 var sms = new SimpleMarkerSymbol({
58 "color": [255, 0, 0],
59 "size": 12,
60 "xoffset": 0,
61 "yoffset": 0,
62 "type": "esriSMS",
63 "style": "esriSMSCircle",
64 "outline": {
65 "color": [0, 0, 0, 255],
66 "width": 1,
67 "type": "esriSLS",
68 "style": "esriSLSSolid"
69 }
70 });
71 72 var point = map.toMap(evt.screenPoint);
73 var attr = {"Xcoord": point.x, "Ycoord": point.y, "Plant": "Mesa Mint"};
74 var infoTemplate = new InfoTemplate("Locations", "Latitude: ${Ycoord} Longitude: ${Xcoord}Plant Name:${Plant}");
75 var graphic=new Graphic(point, sms,attr,infoTemplate);
76 measureLayer.add(graphic);
77 });
78 });
79 });
80</script> 81<style> 82 #measureTools {
83 position: absolute;
84 top: 50px;
85 left: 50px;
86 z-index: 1000;
87 }
88</style> 89</head> 90<body> 91<divid="measureTools"> 92<buttonid="infowindow">弹出框</button> 93</div> 94 95<divid="map"></div> 96</body> 97</html>
效果截图:
ArcGIS JS 学习笔记3 实现百度风格的BubblePopup
标签:
原文地址:http://www.cnblogs.com/ssk-bak/p/5830159.html