1、我的需求
簡單說下為什麼我需要這麼一個氣泡類型的彈出視窗,甲方有一些智能裝置,裡面有經緯度資訊以及一些裝置采集的資訊,希望把這些裝置的經緯度落在地圖上,并且能夠直覺的檢視裝置狀态。
很多人馬上就說了,這個太簡單了,用ArcGIS API 4.12 for JS 自帶的API全都能實作,首先擷取經緯度,然後在地圖上添加Graphic即可,另外顯示裝置狀态就用自帶的PopupTemplate就完全可以實作。首先,第一條沒錯,實作的思路也是這樣,但是第二條,用PopupTemplate這個API,達不到我想要的效果:就是,網頁打開預設就是一種很多智能裝置的點,點邊上就是标注視窗,但是内容是省略的,根據裝置的狀态,比如線上或者離線标注窗體的顔色也是不一樣。然後滑鼠懸停到智能裝置點上的時候,開始顯示詳細的内容。下圖可以看到是比較經典的PopupTemplate的樣子,預設也不顯示,需要點選才能彈出,樣式也很平常,沒有動畫效果。

各位看官,下面是我開發的氣泡标注窗,是不是感覺不太一樣?我開發的tooltip預設顯示省略内容,懸停或者點選可以顯示詳細内容,實作了我的預想,如下圖所示:
這個就是利用html5tooltips這個開源插件開發出來的氣泡提示視窗,首先,html5tooltips預設氣泡tooltip是不顯示的,我這裡是修改了它的源碼,讓預設情況下顯示省略内容,懸停情況下顯示詳細内容。下面一步步教大家怎麼來操作。
2、html5tooltips的下載下傳和安裝
這裡有兩種下載下傳方式,首先開源下載下傳位址是:http://ytiurin.github.io/html5tooltipsjs/。另外就是通過VS Code開發環境中的npm指令進行加載,還是在VS Code的TERMINAL視窗中輸入:npm install html5tooltips,系統會自動把html5tooltips源碼下載下傳到你的工程目錄node_modules下面。
下載下傳完成後,我們需要在HTML裡面引入html5tooltips的樣式(注意這個插件對IE10以下就不支援了)和JS代碼。
<link rel="stylesheet" type="text/css" href="/node_modules/html5tooltipsjs/html5tooltips.css" target="_blank" rel="external nofollow" />
<link rel="stylesheet" type="text/css" href="/node_modules/html5tooltipsjs/html5tooltips.animation.css" target="_blank" rel="external nofollow" />
<script type="text/javascript" src="/node_modules/html5tooltipsjs/html5tooltips.js"></script>
這樣html5tooltips這個氣泡tooltip就被引入進來,可以在上面完成開發了。
3、氣泡窗體Tooltip附着對象
如果你用過PopupTemplate就會知道,這個PopupTemplate氣泡視窗是附着到Graphic或者GraphicLayer上的,也就是說PopupTemplate不能單獨存在,沒有Graphic或者GraphicLayer就不會有PopupTemplate氣泡彈出視窗。同樣的道理,html5tooltips這個氣泡tooltip也是附着在一個特定的對象上的,不過ESRI的Graphic不提供這樣的接口供html5tooltips附着,是以html5tooltips氣泡彈出窗隻能附着在HTML的DOM對象上,我這裡用的是DIV,也就是說動态的建立一個個DIV,然後讓html5tooltips氣泡彈出窗附着上去,這樣滑鼠點選或者懸停到這個DIV的時候就會觸發顯示html5tooltips氣泡彈出窗。
(1)動态建立DIV
動态建立DIV很簡單,網上有很多辦法,這裡我用的是
var dv=document.createElement("p");
然後給建立好的這個“dv”賦Id值,以及修改樣式:
dv.setAttribute("id","id"+popupInfo.id);
dv.setAttribute("style","width:20px;height:20px;position:absolute;z-index:999999;border-radius: 10px; background-color:rgb(0, 255, 64)");
這裡必須給你建立的每一個“dv”指派一個唯一的Id,我這裡用的是裝置的編碼,如果沒有則自己建立一個順序的Id也可以,對于html元素的Id值,不能是以數字開頭,必須以字母開頭(是以上面代碼中,我每一個Id值前面都加了字母“id”),否則後面你用getElementById方法的時候就會出錯,找不到你想找的html元素。我這裡把建立的“dv”的樣式(style)設定了20像素的圓形,你可以按自己的需求設定不同形狀。style屬性中,position:absolute;z-index:999999;這兩個值表示把建立好的DIV先放在視圖最頂層,随後根據裝置點的經緯度挪動DIV到對應的位置。
最後需要把建立好的DIV添加到HTML裡面:
document.body.append(dv);
上面代碼是chrome支援的用法,IE的話要用$("#myBody").append(dv);就是你需要給你HTML的body标簽指派一個Id,然後根據這個body的ID來添加動态建立的Div。
(2)根據經緯度來移動Div
首先確定你從背景來的經緯度坐标跟你目前地圖的坐标系一緻就可以,比如你的地圖是高德地圖或者百度地圖,那麼需要你把背景的經緯度轉換為火星坐标系下的坐标,這方面的内同就不多講,以後另開文章詳說。
重點強調的是,背景請求過來的經緯度坐标,需要轉換成目前的螢幕坐标,才能正确的把你動态建立的Div移動到經緯度對應位置。
第一步,要做require中引入"esri/geometry/Point"類,然後在代碼中根據你的經緯度建立一個點mapPoint:
var mapPoint=new Point(
{
latitude: myLattitude,//背景的經緯度
longitude: myLongitude,//背景的經緯度
spatialReference: mapView.spatialReference
});
第二步,把mapPoint轉換為螢幕像素坐标點screenPoint:
var screenPoint=mapView.toScreen(mapPoint);
第三步,根據screenPoint的x、y坐标移動Div:
var dx=screenPoint.x-10;
var dy=screenPoint.y-10;
$("#id"+popupInfo.id).css('transform', 'translate3d(' + dx + 'px, ' + dy+ 'px, 0)');
因為Div預設的坐标點是左上角,是以上面代碼中的dx、dy是對Div的移動像素進行校正,使得Div的中心正好在經緯度點的中心上。$("#id"+popupInfo.id)是jQuery語句,用來通過ID查詢HTMl元素,因為我的popupInfo.id是一個數字,是以我在前面加了字母修飾,防止出錯。第三條語句就是把動态建立的Div進行移動的主要代碼。通過上述步驟,動态建立的Div就可以移動到經緯度坐标點的位置了。
(3)對移動好位置的Div附着Tooltip氣泡視窗
html5tooltips支援高度自定義,可以顯示簡略内容和詳細内容以及動畫效果,那麼我們先建立單個的氣泡視窗:
var singleTooltip= {
animateFunction: "fadein",//動畫效果
color: "rouge", //視窗顔色
contentText: popupInfo.id,//氣泡視窗的簡略内容(我這裡簡略内容隻顯示裝置ID)
contentMore: popupInfo.content,//氣泡視窗的詳細内容,裝置狀态等等
stickTo: "right",//氣泡視窗的現實位置,這裡是Div的右側
targetSelector: "#id"+popupInfo.id//附着對象的HTML元素ID
};
上面代碼是建立了一個Tooltip氣泡視窗,并把它附着到對應ID的Div上,因為我這裡的裝置很多,是以我是通過循環建立多個Tooltip氣泡視窗,然後把它們放到一個數組中。
var tooltipCollection=[];
tooltipCollection.push(singleTooltip);
注意,var tooltipCollection=[];這句代碼要寫在循環外面,不然你的數組永遠隻有一個值。
最後,所有的Tooltip氣泡視窗添加到數組之後,通過html5tooltips來初始化這些Tooltip氣泡視窗并且顯示出來。很簡單就一句代碼:
html5tooltips(tooltipCollection);
注意,上述一行代碼,也是要寫到循環外面的。
(4)地圖視圖變化時更新Div位置
前面的内容是地圖初始化的時候,建立動态Div并附着Tooltip氣泡視窗,當視圖放大或者縮小的時候,Div位置相對螢幕是不動的。是以我們要及時更新Div的位置,也就是需要添加mapView.watch("extent", function (evt) {})事件。
之前我們把從背景取到的裝置資訊包括經緯度點,都是存放到tooltipCollection這個數組中,是以在視圖變化的時候,我們周遊這個數組中的裝置點,重新把經緯度坐标轉換為螢幕坐标點即可。代碼如下:
mapView.watch("extent", function (evt) {
for (var i = 0; i < app.popupsInfo.length; i++) {
var popupInfo = app.popupsInfo[i];
//坐标轉換
//坐标轉換
var mapPoint=new Point(
{
latitude: popupInfo.lat,
longitude: popupInfo.lon,
spatialReference: app.mapView.spatialReference
});
var screenPoint=app.mapView.toScreen(mapPoint);
if(screenPoint==null)return;
var dx=screenPoint.x-10;
var dy=screenPoint.y-10;
$("#id"+popupInfo.id).css('transform', 'translate3d(' + dx + 'px, ' + dy+ 'px, 0)');
var tooltip =html5tooltips.getTooltipByTarget(document.getElementById("id"+popupInfo.id));
tooltip.moveTooltip();
}
});
上述代碼中,首先根據數組裡面的經緯度點,重新轉換為視圖變化後的螢幕坐标,然後移動Div。然後再通過html5tooltips.getTooltipByTarget()這個自帶的方法來查找對應的氣泡視窗tooltip,最後使用tooltip.moveTooltip()來更新氣泡視窗的位置。
4、修改html5tooltips代碼
首先在檔案“html5tooltips.js”中找到,function show()這個函數。
function show()
{
isHiding = false;
if (ttElement.style.visibility !== 'visible') {
ttElement.style.visibility = 'visible';
setTimeout(function(){
moveTooltip();
applyAnimationClass(elBox, ttModel.animateFunction + "-from",
ttModel.animateFunction + "-to");
});
}
return this;
}
在這個函數下面添加一個自定義的函數,function showShort(),這個函數的功能就是顯示簡略内容。
function showShort() {
isHiding = false;
if (ttElement.style.visibility == 'visible') {
ttElement.style.visibility = 'visible';
if (ttModel.contentMore) {
elMore.style.display = 'none';
elMore.style.visibility = 'hidden';
}
applyAnimationClass(elBox, ttModel.animateFunction + "-from", ttModel.animateFunction + "-to");
moveTooltip();
}
return this;
}
建立好showShort()函數後,接着修改function createTooltip(target,options,tooltips)這個html5tooltips自帶的函數,我這裡就直接把修改好的貼進來了:
function createTooltip(target,options,tooltips)
{
var tooltip;
for(var i=tooltips.length;i--;)
if(tooltips[i].model.target===target){
tooltip=tooltips[i];
break;
}
if(!tooltip){
tooltip=new HTML5TooltipUIComponent;
tooltips.push(tooltip);
}
tooltip.set(options);
tooltip.set('target',target);
var hovered,focused;
/*
function tryUnmountTooltip()
{
if (target===hovered || tooltip.element===hovered)
return;
if (tooltip.model.persistent)
tooltip.hide(function(){
tooltip.unmount();
});
else
tooltip.unmount();
}
function disposeTooltip()
{
hovered = null;
if (target===focused)
return;
if (tooltip.model.persistent)
setTimeout(tryUnmountTooltip, tooltip.model.hideDelay);
else
tryUnmountTooltip();
}
*/
tooltip.mount();
tooltip.show();
target.addEventListener('mouseenter',function(){
tooltip.mount();
tooltip.showMore();
});
/*
target.addEventListener("mouseenter",function(){
if (this===hovered || this===focused)
return;
hovered = this;
setTimeout(function() {
if (this===hovered){
tooltip.mount();
tooltip.showMore();
}
}.bind(this), tooltip.model.delay);
});*/
//target.addEventListener("mouseleave",disposeTooltip);
target.addEventListener('mouseleave',function(){
tooltip.mount();
tooltip.showShort();
});
target.addEventListener("focus",function(){
if (["INPUT", "TEXTAREA"].indexOf(this.tagName) === -1 &&
this.getAttribute("contenteditable") === null)
return;
focused = this;
tooltip.mount();
tooltip.showMore();
});
/*
target.addEventListener("blur",function(){
focused = null;
if(hovered !== tooltip.element){
tooltip.unmount();
}
});
tooltip.element.addEventListener("mouseenter",function(){
hovered = this;
});*/
//tooltip.element.addEventListener("mouseleave",disposeTooltip);
/*
target.element.addEventListener('mouseleave',function(){
tooltip.mount();
tooltip.showShort();
});*/
}
修改好的createTooltip(target,options,tooltips)這個html5tooltips自帶的函數,大家可以參考源碼比較一下做了哪些改動,我這裡就不一一細講了,其中還注釋掉了原來的一些監聽函數。
最後,因為你添加了一個新的函數showShort(),你需要把這個函數放到接口位置,找到下面代碼塊的位置,并加入建立函數:
// PUBLIC INTERFACE
this.hide=hide;
this.show=show;
this.showShort=showShort;
this.showMore=showMore;
this.moveTooltip=moveTooltip;
所有關于html5tooltips修改的内容都在于此,文章頂部位置,提供我修改好的html5tooltips.js檔案以供大家使用。