1. 开篇

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

2.准备

2.1 Copy模板

先打开百度地图,按下f12吧BubblePopup的HTML代码和CSS代码拷贝下来,这里我无耻的把类名改了,大家不要在意细节。

HTML模板

 <div class="dextra-bubble-pop-center" style="z-index: 3; position: relative; height: 50px; width: 160px;">
<div class="dextra-bubble-pop-content"
style="display: block; width: 160px; height: 50px; overflow-x: auto; overflow-y: hidden;">
<div id="poi_info_window" class="dextra-poi-info-window">
<div class="left name-wrap"><span class="name"></span></div>
</div>
</div>
</div>
<div class="dextra-bubble-pop-bottom" style="display: block; z-index: 2; width: 160px; left: 72px;">
<span></span>
</div>

CSS代码

 .dextra-bubble-pop {
position: absolute;
z-index: ;
box-sizing: border-box;
box-shadow: 1px 2px 1px rgba(0, 0, 0, .15);
background-color: #FFF;
} .dextra-poi-info-window {
padding: 4px 0;
} .dextra-poi-info-window .left {
padding-left: 10px;
padding-right: 10px;
height: 40px;
line-height: 40px;
display: table;
table-layout: fixed;
width: 140px;
text-align: center;
} .dextra-poi-info-window .name-wrap .name {
vertical-align: middle;
font-size: 14px;
font-weight: ;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
} .dextra-bubble-pop-bottom span {
position: absolute;
left:72px;
width: 16px;
height: 10px;
background-image: url("../images/tail_shadow.png");
}

2.2 编写BubblePopup

要实现BubblePopup,实际上就是自定义一个InfoWindow,我们可以通过继承InfoWindowBase来实现。要实现自定义的InfoWindow。我们可以先参考一下官方的例子Custom info window,注意,这个例子是有缺陷的,如果当infowindow超出当前视图边界就会出现滚动条。下载官方的实例,我们打开infoWindow.js文件。

 define([
"dojo/Evented",
"dojo/parser",
"dojo/on",
"dojo/_base/declare",
"dojo/dom-construct",
"dojo/_base/array",
"dojo/dom-style",
"dojo/_base/lang",
"dojo/dom-class",
"dojo/fx/Toggler",
"dojo/fx",
"dojo/Deferred",
"esri/domUtils",
"esri/InfoWindowBase" ],
function(
Evented,
parser,
on,
declare,
domConstruct,
array,
domStyle,
lang,
domClass,
Toggler,
coreFx,
Deferred,
domUtils,
InfoWindowBase
) {
return declare([InfoWindowBase, Evented], { isContentShowing :false, constructor: function(parameters) { lang.mixin(this, parameters); domClass.add(this.domNode, "myInfoWindow"); this._closeButton = domConstruct.create("div",{"class": "close", "title": "Close"}, this.domNode);
this._title = domConstruct.create("div",{"class": "title"}, this.domNode);
this._content = domConstruct.create("div",{"class": "content"}, this.domNode); this._toggleButton = domConstruct.create("div",{"class": "toggleOpen", "title": "Toggle"}, this.domNode); var toggler = new Toggler({
"node": this._content,
showFunc: coreFx.wipeIn,
hideFunc: coreFx.wipeOut
});
toggler.hide(); on(this._closeButton, "click", lang.hitch(this, function(){
//hide the content when the info window is toggled close.
this.hide();
if(this.isContentShowing){
toggler.hide();
this.isContentShowing = false;
domClass.remove(this._toggleButton);
domClass.add(this._toggleButton, "toggleOpen");
}
}));
on(this._toggleButton, "click", lang.hitch(this, function(){
//animate the content display
if(this.isContentShowing){ toggler.hide();
this.isContentShowing = false;
domClass.remove(this._toggleButton);
domClass.add(this._toggleButton,"toggleOpen"); }else{
toggler.show();
this.isContentShowing=true;
domClass.remove(this._toggleButton);
domClass.add(this._toggleButton,"toggleClose");
} }));
//hide initial display
domUtils.hide(this.domNode);
this.isShowing = false; },
setMap: function(map){
this.inherited(arguments);
map.on("pan-start", lang.hitch(this, function(){
this.hide();
}));
map.on("zoom-start", lang.hitch(this, function(){
this.hide();
}));
// map.on("zoom-start", //this, this.hide); },
setTitle: function(title){
this.place(title, this._title); },
setContent: function(content){
this.place(content, this._content);
},
show: function(location){
if(location.spatialReference){
location = this.map.toScreen(location);
} //Position 10x10 pixels away from the specified location
domStyle.set(this.domNode,{
"left": (location.x + 10) + "px",
"top": (location.y + 10) + "px"
}); //display the info window
domUtils.show(this.domNode);
this.isShowing = true;
this.onShow();
},
hide: function(){
domUtils.hide(this.domNode);
this.isShowing = false;
this.onHide(); },
resize: function(width, height){
domStyle.set(this._content,{
"width": width + "px",
"height": height + "px"
});
domStyle.set(this._title,{
"width": width + "px"
}); },
destroy: function(){
domConstruct.destroy(this.domNode);
this._closeButton = this._title = this._content = null; } }); });

我们就在此基础上进行改造,不但要实现需求还要解决缺陷。infoWindowBase是继承自_WidgetBase的,我们先来看一下infoWindowBase的官方描述.

我们可以重写infoWindowBase的一些方法,来实现自己的infoWindow。

首先我们先引入我们要用到的模块

 define([
"dojo/Evented",
"dojo/on",
"dojo/query",
"dojo/_base/declare",
"dojo/dom-construct",
"dojo/dom-attr",
"dojo/_base/array",
"dojo/dom-style",
"dojo/_base/lang",
"dojo/dom-class",
"dijit/_TemplatedMixin",
"esri/domUtils",
"esri/InfoWindowBase",
"esri/geometry/ScreenPoint",
"esri/geometry/screenUtils",
"esri/geometry/webMercatorUtils",
"dojo/text!./templates/dextraPopup.html"
],
function (Evented,
on,
query,
declare,
domConstruct,
domAttr,
array,
domStyle,
lang,
domClass,
_TemplatedMixin,
domUtils,
InfoWindowBase, ScreenPoint, screenUtils, webMercatorUtils, template) {
var showMapPoint = null;
return declare([InfoWindowBase, Evented, _TemplatedMixin], {
isContentShowing: false,
templateString: template,
_events: [],
constructor: function (parameters) {
lang.mixin(this, parameters);
},
...
});

对比官方的例子,我去掉了部分模块(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节点,可以通过在构造函数里用第二个参数来进行传入,或者在内部自己定义。

   _createInfoWindowInstance: function (map) {
this.domNode = domConstruct.create("div", null, map.id + "_root");
domClass.add(this.domNode, "dextra-bubble-pop");
domStyle.set(this.domNode, {
width: "160px",
}); this.domNode.innerHTML = this.templateString; this._content = query("div.name-wrap span.name");
this._title=query("div.name-wrap");
//hide initial display
domUtils.hide(this.domNode);
this.isShowing = false;
},

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

   _showInfoWindow: function (extent) {
if (showMapPoint == null)return;
var showScreenPoint = screenUtils.toScreenGeometry(extent, this.map.width, this.map.height, showMapPoint);
domStyle.set(this.domNode, {
"left": (showScreenPoint.x - 80) + "px",
"top": (showScreenPoint.y - 76 ) + "px"
}); domUtils.show(this.domNode);
this.isShowing = true;
this.onShow();
}, show: function (location) {
showMapPoint = location;
if (webMercatorUtils.canProject(location, this.map)) {
showMapPoint = webMercatorUtils.project(location, this.map);
}
if (showMapPoint.spatialReference) {
var screenPoint = this.map.toScreen(showMapPoint);
domStyle.set(this.domNode, {
"left": (screenPoint.x - 80) + "px",
"top": (screenPoint.y - 76) + "px"
});
} //display the info window
domUtils.show(this.domNode);
this.isShowing = true;
this.onShow();
},

_showInfoWindow方法是一个私有方法,用于在地图事件触发时调用。当地图平移,缩放时根据地理坐标从新计算BubblePopup的屏幕坐标。用screenUtils.toScreenGeometry(extent, width, height, mapGeometry)根据地图的范围,宽度,高度,和点计算出相应的屏幕坐标。

show方法是一个公有方法,用于在外部进行调用。在这里利用了arcgis js 提供webMercatorUtils模块,来进行坐标的转换。一般而言,我们都会用经纬度坐标,但是当地图是webMercator投影时,就需要先把经纬度坐标转化成米制坐标,才能在正确的位置显示出来来。

关键的部分已经完成,下面贴出全部代码

 define([
"dojo/Evented",
"dojo/on",
"dojo/query",
"dojo/_base/declare",
"dojo/dom-construct",
"dojo/dom-attr",
"dojo/_base/array",
"dojo/dom-style",
"dojo/_base/lang",
"dojo/dom-class",
"dijit/_TemplatedMixin",
"esri/domUtils",
"esri/InfoWindowBase",
"esri/geometry/ScreenPoint",
"esri/geometry/screenUtils",
"esri/geometry/webMercatorUtils",
"dojo/text!./templates/dextraPopup.html"
],
function (Evented,
on,
query,
declare,
domConstruct,
domAttr,
array,
domStyle,
lang,
domClass,
_TemplatedMixin,
domUtils,
InfoWindowBase, ScreenPoint, screenUtils, webMercatorUtils, template) {
var showMapPoint = null;
return declare([InfoWindowBase, Evented, _TemplatedMixin], { templateString: template,
_events: [],
constructor: function (parameters) {
lang.mixin(this, parameters);
},
_createInfoWindowInstance: function (map) {
this.domNode = domConstruct.create("div", null, map.id + "_root");
domClass.add(this.domNode, "dextra-bubble-pop");
domStyle.set(this.domNode, {
width: "160px",
}); this.domNode.innerHTML = this.templateString; this._content = query("div.name-wrap span.name");
this._title=query("div.name-wrap");
//hide initial display
domUtils.hide(this.domNode);
this.isShowing = false;
}, setMap: function (map) {
this.inherited(arguments);
this._events = [];
this._createInfoWindowInstance(map);
this._events.push(map.on("pan", lang.hitch(this, function (evt) {
if (this.isShowing) {
this._showInfoWindow(evt.extent);
}
}))); this._events.push(map.on("zoom-start", lang.hitch(this, function (evt) {
this.hide();
}))); this._events.push(map.on("zoom-end", lang.hitch(this, function (evt) {
this._showInfoWindow(evt.extent);
})));
}, unsetMap: function (map) {
this.inherited(arguments);
array.forEach(this._events, function (event) {
event.remove();
});
},
setTitle: function (title) {
this._title.forEach(function (node) {
domAttr.set(node, "title", title);
});
}, setContent: function (content) {
this._content.forEach(function (node) {
node.innerHTML = content;
});
}, _showInfoWindow: function (extent) {
if (showMapPoint == null)return;
var showScreenPoint = screenUtils.toScreenGeometry(extent, this.map.width, this.map.height, showMapPoint);
domStyle.set(this.domNode, {
"left": (showScreenPoint.x - 80) + "px",
"top": (showScreenPoint.y - 76 ) + "px"
}); domUtils.show(this.domNode);
this.isShowing = true;
this.onShow();
}, show: function (location) {
showMapPoint = location;
if (webMercatorUtils.canProject(location, this.map)) {
showMapPoint = webMercatorUtils.project(location, this.map);
}
if (showMapPoint.spatialReference) {
var screenPoint = this.map.toScreen(showMapPoint);
domStyle.set(this.domNode, {
"left": (screenPoint.x - 80) + "px",
"top": (screenPoint.y - 76) + "px"
});
} //display the info window
domUtils.show(this.domNode);
this.isShowing = true;
this.onShow();
},
hide: function () {
if (this.isShowing) {
domUtils.hide(this.domNode);
this.isShowing = false;
this.onHide();
}
},
resize: function (width, height) {
domStyle.set(this._content, {
"width": width + "px",
"height": height + "px"
});
},
remove: function () {
this.hide();
showMapPoint = null;
},
destroy: function () {
domConstruct.destroy(this.domNode);
}
});
});

DEMO:

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DExtra-BubublPoopup</title>
<link rel="stylesheet" href="https://js.arcgis.com/3.16/esri/css/esri.css">
<link rel="stylesheet" href="../dist/dijit/css/dextraPopup.css">
<link rel="stylesheet" href="css/mainApp.css">
<script>
var dojoConfig = {
parseOnLoad:true,
packages: [{
name: 'custom',
location: location.pathname.replace(/\/[^/]+$/, '') + '/custom'//从cdn加载自己定义的模块方法
},
{
name: 'dextra',
location: '/extra.arcgis.3.x/dist/'//从cdn加载自己定义的模块方法
}]
};
</script>
<script src="https://js.arcgis.com/3.16/"></script>
<script>
require([
"dojo/dom",
"dojo/on",
"esri/map",
"esri/symbols/SimpleMarkerSymbol",
"esri/InfoTemplate",
"esri/layers/GraphicsLayer",
"dextra/layers/GoogleVectorLayer",
"dextra/dijit/DEBubblePopup",
"dojo/domReady!"],
function (dom, on,
Map, Graphic, SimpleMarkerSymbol, InfoTemplate, GraphicsLayer,
GoogleVectorLayer,DEBubblePopup) { var infoWindow = new DEBubblePopup();
var map = new Map("map", {
showAttribution: false,
center: [102.3, 24.6],
autoResize: true,
sliderPosition: "bottom-right",
logo: false,
infoWindow:infoWindow,
zoom:12
}); var googleVect = new GoogleVectorLayer();
map.addLayer(googleVect); var measureLayer = new GraphicsLayer({id: "infoWindowTest"});
map.addLayer(measureLayer);
on(dom.byId("infowindow"), "click", function (e) {
on.once(map, "click", function (evt) {
console.log(map._container);
var sms = new SimpleMarkerSymbol({
"color": [255, 0, 0],
"size": 12,
"xoffset": 0,
"yoffset": 0,
"type": "esriSMS",
"style": "esriSMSCircle",
"outline": {
"color": [0, 0, 0, 255],
"width": 1,
"type": "esriSLS",
"style": "esriSLSSolid"
}
}); var point = map.toMap(evt.screenPoint);
var attr = {"Xcoord": point.x, "Ycoord": point.y, "Plant": "Mesa Mint"};
var infoTemplate = new InfoTemplate("Locations", "Latitude: ${Ycoord} Longitude: ${Xcoord}Plant Name:${Plant}");
var graphic=new Graphic(point, sms,attr,infoTemplate);
measureLayer.add(graphic);
});
});
});
</script>
<style>
#measureTools {
position: absolute;
top: 50px;
left: 50px;
z-index: 1000;
}
</style>
</head>
<body>
<div id="measureTools">
<button id="infowindow">弹出框</button>
</div> <div id="map" ></div>
</body>
</html>

效果截图:

3.1 小结

可以看到,通过继承InfoWindowBase我们完全可以实现自己的的infoWindow,编写更具个性化的插件。最后像新手玩家推荐一下

esri的github,这里有很多有用的东西,非常值得学习http://esri.github.io/

本文参考了 http://blog.csdn.net/gisshixisheng/article/details/26132921 谢谢lzugis的分享。

欢迎转载 http://www.cnblogs.com/deliciousExtra/p/5565787.html

ArcGIS JS 学习笔记3 实现百度风格的BubblePopup的更多相关文章

  1. ArcGIS JS 学习笔记1 用ArcGIS JS 实现仿百度地图的距离量测和面积量测

    一.开篇 在博客注册了三年,今天才决定写第一篇博客,警告自己不要懒!!! 二.关于ArcGIS JS 版本选择 在写这篇博客时ArcGIS JS 4.0正式版已经发布.它和3.x版本的不同是,Map不 ...

  2. ArcGIS JS 学习笔记2 实现仿百度的拖拽画圆

    一.前言 吐槽一下,百度在国内除了百度地图是良心产品外,其他的真的不敢恭维.在上一篇笔记里,我已经实现了自定义的地图测量模块.在百度地图里面(其他地图)都有一个周边搜索的功能,拖拽画一个圆,然后以圆半 ...

  3. ArcGIS JS 学习笔记4 实现地图联动

    1.开篇 守望屁股实在太好玩了,所以最近有点懒,这次就先写个简单的来凑一下数.这次我的模仿目标是天地图的地图联动. 天地的地图联动不仅地图有联动,而且鼠标也有联动,我就照着这个目标进行山寨. 2.准备 ...

  4. WebGL three.js学习笔记 加载外部模型以及Tween.js动画

    WebGL three.js学习笔记 加载外部模型以及Tween.js动画 本文的程序实现了加载外部stl格式的模型,以及学习了如何把加载的模型变为一个粒子系统,并使用Tween.js对该粒子系统进行 ...

  5. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  6. Vue.js学习笔记(2)vue-router

    vue中vue-router的使用:

  7. JS 学习笔记--9---变量-作用域-内存相关

    JS 中变量和其它语言中变量最大的区别就是,JS 是松散型语言,决定了它只是在某一个特定时间保存某一特定的值的一个名字而已.由于在定义变量的时候不需要显示规定必须保存某种类型的值,故变量的值以及保存的 ...

  8. WebGL three.js学习笔记 使用粒子系统模拟时空隧道(虫洞)

    WebGL three.js学习笔记 使用粒子系统模拟时空隧道 本例的运行结果如图: 时空隧道demo演示 Demo地址:https://nsytsqdtn.github.io/demo/sprite ...

  9. WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法

    WebGL学习----Three.js学习笔记(5) 点击查看demo演示 Demo地址:https://nsytsqdtn.github.io/demo/360/360 简单网格材质 MeshNor ...

随机推荐

  1. 【Win10应用开发】自适应磁贴中的分组

    老周在上一篇文章中介绍过了自适应磁贴的基本结构,以及给大家演示了一些例子. 本文老周给大伙伴们说说自适应磁贴的另一个特点——分组呈现. 当磁贴的内容被分组后,每个组中的内容就会被视为一个整体.比如某磁 ...

  2. Android 圆形头像 自己动手

    圆形头像DIY 现在大部分app使用的都是圆形头像,网上开源的也很多,但是有没有考虑过DIY圆形头像呢?下面就自己实现一个,先看下demo展示 第一步:原理解释(图片很丑,原理很真) 1.画外框圆形, ...

  3. 从零开始编写自己的C#框架(3)——开发规范

    由于是业余时间编写,而且为了保证质量,对写出来的东西也会反复斟酌,所以每周只能更新两章左右,请大家谅解,也请大家耐心等待,谢谢大家的支持. 初学者应该怎样学习本系列内容呢?根据我自己的学习经验,一般直 ...

  4. Ubuntu杂记——Ubuntu自带拼音输入发杂乱不堪

    打开终端,用管理员权限输入ibus-daemon -drx,重启即可

  5. Kubernetes集群搭建过程中遇到的问题

    1. 创建Nginx Pod过程中报如下错误: #kubectlcreate -f nginx-pod.yaml Error from server: error when creating &quo ...

  6. hibernate笔记--组合主键映射方法

    一个数据库表中其主键有可能不止一个属性,同样映射到实体类中,可能有两个或多个属性共同配置成为一个主键,假设一个实体类Score,其主键有两个属性stuId(学生编号)和subjectId(科目编号), ...

  7. 在线预览Office文件【效果类似百度文库】

    引言 结合上个项目和目前做的这个项目,其中都用到了Office文件在线预览,目前项目中是用到公司购买的Ntko控件,该控件每次浏览文件时则会提示安装信任插件,很繁琐,而且浏览效果不好. 提到Offic ...

  8. 给ubuntu中的软件设置desktop快捷方式(以android studio为例)

    ubuntu的快捷方式都在/usr/share/applications/路径下有很多*.desktop(eclipse的快捷方式也可以类似设置) 下面就建立我们的studio sudo gedit ...

  9. JavaScript代码模块化的正规方法

    RequireJS-CommonJS-AMD-ES6 Import/Export详解 为什么起了一个这个抽象的名字呢,一下子提了四个名词分别是:RequireJS,CommonJS,AMD,ES6,答 ...

  10. 微信公众账号 token 验证失败 解决办法

    问题:微信公众账号 开发过程中配置  token 提示 验证失败 如下图: 点击修改配置: 填写相关url与token(自定义):点击提交,会出现 出现这种情况,主要是对相关参数不熟悉,要了解url与 ...