码迷,mamicode.com
首页 > Web开发 > 详细

ArcGIS JS 学习笔记3 实现百度风格的BubblePopup

时间:2016-09-01 16:13:41      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:

1. 开篇

模仿是最好的学习,这次我们继续山寨百度,通过自定义Infowindow来实现百度风格的BubblePopup

技术分享

 

2.准备

2.1 Copy模板

先打开百度地图,按下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>
View Code

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 }
View Code

2.2 编写BubblePopup

    要实现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 });
View Code

我们就在此基础上进行改造,不但要实现需求还要解决缺陷。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               });
View Code

      对比官方的例子,我去掉了部分模块(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             },
View Code

     注意,我在这里创建了一个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             },
View Code

      _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
View Code

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>
View Code
 

效果截图:

技术分享

ArcGIS JS 学习笔记3 实现百度风格的BubblePopup

标签:

原文地址:http://www.cnblogs.com/ssk-bak/p/5830159.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!