騰訊地圖JSAPI教程-在地圖上添加自定義覆蓋物

以下內容轉載自多多洛愛學習的文章《JSAPI-在地圖上添加自定義覆蓋物》

作者:多多洛愛學習

鏈接:https://juejin.im/post/5ee5f80d51882542e2695874

來源:掘金

著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權耐亏,非商業(yè)轉載請注明出處旧烧。

地圖上的覆蓋物

在地圖上添加覆蓋物有兩種方式庐冯,一是在canvas畫布上渲染,比如JSAPI GL繪制MultiMarker/MultiPolygon等矢量圖形覆蓋物就是通過編寫對應圖形的數(shù)據(jù)解析及渲染程序瑰剃,直接繪制在底圖上層爸舒。這樣的渲染方式下視角變換時圖形也可以實現(xiàn)3D形變。另一種方式是通過CSS布局將其他DOM元素疊加到地圖容器之上沉御,這種方式下視角變換時DOM元素需重新計算布局,比如JSAPI v2的Marker/Polygon等覆蓋物昭灵,以及JSAPI GL的InfoWindow信息窗吠裆,這些都屬于DOM覆蓋物。

image

如果你需要疊加一個自定義的復雜元素烂完,第一種方式的話需要實現(xiàn)對應的數(shù)據(jù)解析和著色器程序试疙,需要了解WebGL的渲染原理,成本很高抠蚣,且不易變通祝旷。而DOM是每個前端工程師都非常熟悉的,簡單幾個標簽加CSS就能實現(xiàn)高度定制的DOM元素嘶窄。但是如何將一個DOM元素正確地安置在地圖上缓屠,并且隨著地圖平移、旋轉护侮、縮放實時調整自己的位置呢?

這就要使用到DOMOverlay了储耐。它并不是一個具體的DOM覆蓋物羊初,而是所有DOM覆蓋物的抽象基類,InfoWindow就繼承自它。DOMOverlay抽象出了DOM覆蓋物的生命周期长赞,公共屬性及方法晦攒,實現(xiàn)了地圖事件的監(jiān)聽綁定及解綁,你只需要關注DOM節(jié)點的創(chuàng)建和位置計算方法即可得哆。

DOMOverlay 接口設計

先來看看DOMOverlay的類關系圖脯颜,這里結合了官網(wǎng)示例DOMOverlay中定義的Donut類作為DOMOverlay的實現(xiàn):

image

公共屬性及方法

事件監(jiān)聽及觸發(fā)

從上圖可見,DOMOverlay繼承自Node.js的EventEmitter類贩据,所以它已經實現(xiàn)了事件監(jiān)聽栋操、觸發(fā)等功能的封裝,不太熟悉的同學可以看看Node.js EventEmitter | 菜鳥教程饱亮。

地圖綁定與解綁

DOMOverlay有一個公共屬性map矾芙,其值為該覆蓋物綁定的地圖實例,同時提供了setMap(map: Map)getMap()方法作為map參數(shù)的訪問器近上。
要將自定義覆蓋物顯示在地圖上剔宪,首先得明確具體的地圖實例,有兩種辦法壹无,一是在初始化參數(shù)中定義map屬性葱绒,二是通過setMap進行動態(tài)設置,可以綁定到另一個地圖實例上斗锭,或者解綁地淀。setMap做了什么呢?綁定時一方面主要是將createDOM()返回的DOM元素加入到特定的節(jié)點下拒迅,使其覆蓋在地圖上方且可以進行相對定位骚秦;另一方面是監(jiān)聽地圖變換執(zhí)行updateDOM(),使DOM元素可以跟隨地圖更新定位或內容璧微。解綁時則是將其從父節(jié)點下去除作箍,同時刪除對地圖事件的監(jiān)聽。

DOM元素

DOMOverlay的公共屬性dom指向的是該覆蓋物的具體元素前硫,可以是HTMLElement或者SVGElement胞得,該元素的創(chuàng)建由子類進行實現(xiàn),綁定地圖后會掛載到覆蓋在canvas畫布上層的一個div容器中屹电。

銷毀

當覆蓋物不再被使用時應適時進行銷毀操作阶剑,以防內存泄漏。destroy方法封裝了銷毀時應執(zhí)行的操作危号,一方面將地圖解綁牧愁,另一方面刪除對象上注冊的所有監(jiān)聽器。

抽象方法

DOMOverlay提供了4個抽象方法外莲,在生命周期的不同階段進行調用猪半。

  • onInit在初始化階段調用兔朦,并透傳了構造函數(shù)的參數(shù)options,用于參數(shù)注入
  • createDOM在初始階段調用磨确,用于創(chuàng)建DOM元素并將其返回沽甥,作為dom屬性的值,并加入到特定的父節(jié)點下
  • updateDOM在地圖發(fā)生平移乏奥、縮放摆舟、旋轉時調用,用于更新DOM元素定位
  • onDestroy在銷毀階段調用邓了,可在此函數(shù)中對自定義的對象和事件監(jiān)聽進行刪除

具體的生命周期如下:

image

基于DOMOverlay實現(xiàn)自定義覆蓋物

舉個??:自定義環(huán)形餅圖

[圖片上傳失敗...(image-3e8639-1593402335630)]

以官網(wǎng)示例中的Donut為例恨诱,創(chuàng)建自定義環(huán)形餅圖。官網(wǎng)示例中使用了原生JS語法實現(xiàn)繼承驶悟,這里我們改用ES6語法實現(xiàn)下:

const SVG_NS = 'http://www.w3.org/2000/svg';

// 自定義環(huán)狀餅圖 - 繼承DOMOverlay
class Donut extends TMap.DOMOverlay {
    constructor(options) {
        super(options);
    }

    // 初始化:獲取配置參數(shù)
    onInit({
        position,
        data,
        minRadius = 0,
        maxRadius = 50,
    } = {}) {
        Object.assign(this, {
            position,
            data,
            minRadius,
            maxRadius,
        });
    }

    // 創(chuàng)建DOM元素胡野,返回一個Element,使用this.dom可以獲取到這個元素
    createDOM() {
        let svg = document.createElementNS(SVG_NS, 'svg');
        svg.setAttribute('version', '1.1');
        svg.setAttribute('baseProfile', 'full');

        let r = this.maxRadius;
        svg.setAttribute('viewBox', [-r, -r, r * 2, r * 2].join(' '));
        svg.setAttribute('width', r * 2);
        svg.setAttribute('height', r * 2);
        svg.style.cssText = 'position:absolute;top:0px;left:0px;';

        let donut = createDonut(this.data, this.minRadius, this.maxRadius);
        svg.appendChild(donut);

        return svg;
    }

    // 更新DOM元素痕鳍,在地圖移動/縮放后執(zhí)行
    updateDOM() {
        if (!this.map) {
            return;
        }

        // 經緯度坐標轉容器像素坐標
        let pixel = this.map.projectToContainer(this.position);

        // 使餅圖中心點對齊經緯度坐標點
        let left = pixel.getX() - this.dom.clientWidth / 2 + 'px';
        let top = pixel.getY() - this.dom.clientHeight / 2 + 'px';
        this.dom.style.transform = `translate(${left}, ${top})`;
    }

    // 銷毀時
    onDestroy() {}
}

其中createDonut是根據(jù)數(shù)據(jù)和半徑創(chuàng)建對應的SVG圖形硫豆,這里先不過多關注。

如何進行元素定位笼呆?

這里重點說明下updateDOM的實現(xiàn)熊响,如何進行定位更新。
首先诗赌,我們在初始化階段給position屬性賦值汗茄,position是一個經緯度對象,可以通過map.projectToContainer方法轉為地圖容器內的像素坐標铭若,記為pixel洪碳。地圖容器坐標系是以地圖容器左上角為原點,向右為x正方向叼屠,向下為y正方向的坐標系瞳腌。
另外,我們在createDOM方法中對生成的svg元素設置了CSS樣式position:absolute;top:0px;left:0px;镜雨,所以元素實際定位是與地圖容器左上角對齊嫂侍。
我們需要讓環(huán)形餅圖的中心與pixel位置對齊,首先可以通過clientWidth/clientHeight獲取元素寬高荚坞,然后計算得到元素左上角的像素坐標為(left挑宠、top),最后通過transform: translate(${left}, ${top})設置平移偏移量颓影,將元素移動到對應位置各淀。

為什么不使用top: ${top}; left: ${left}進行定位呢?
因為transformtop/left性能好很多诡挂。top/left是在CPU上進行計算揪阿,會引起周圍區(qū)域的重繪疗我;而transform是利用GPU計算能力,且是在獨立的圖層中進行變換南捂,不會引起重繪。具體可以參考創(chuàng)建前端平移動畫為何translate()優(yōu)于top/right/bottom/left旧找。

如何實現(xiàn)click監(jiān)聽溺健?

有的同學發(fā)現(xiàn)創(chuàng)建了自定義覆蓋物之后就不能像MultiMarker那樣通過on('click')監(jiān)聽到點擊事件了,這是為什么呢钮蛛?因為你沒有觸發(fā)事件啊:joy: 首先你需要監(jiān)聽DOM元素的點擊事件鞭缭,可以在createDOM中實現(xiàn):

    // 創(chuàng)建DOM元素,返回一個Element魏颓,使用this.dom可以獲取到這個元素
    createDOM() {
        ...

        // click事件回調
        this.onClick = () => {
            // DOMOverlay繼承自EventEmitter岭辣,可以使用emit觸發(fā)事件
            this.emit('click');
        };
        
        // 使用addEventListener實現(xiàn)DOM元素的click監(jiān)聽
        svg.addEventListener('click', this.onClick);
        return svg;
    }

click事件回調中可以直接執(zhí)行你想要的操作,或者調用emit觸發(fā)事件甸饱,就可以觸發(fā)通過on掛載的監(jiān)聽器了沦童,如下:

let donut = new Donut({
    map,
    position: new TMap.LatLng(40.02906301748584, 116.25499991104516),
    data: [18, 41, 50],
    minRadius: 20,
    maxRadius: 28
})
donut.on('click', () => {
    console.log(`環(huán)形圖被點擊,位置為${donut.position}`);
});

需要注意的是叹话,在銷毀時應該將事件監(jiān)聽刪除偷遗,所以onDestroy應相應修改為:

    // 銷毀時需解綁事件監(jiān)聽
    onDestroy() {
        if (this.onClick) {
            this.dom.removeEventListener(this.onClick);
        }
    }

類似的,你可以監(jiān)聽mousedown驼壶、mouseup以及移動端的touchstart氏豌、touchend等事件,因為是自定義元素热凹,所以控制權在你自己手上哦泵喘。

為什么出現(xiàn)偏移?

有的同學在實現(xiàn)自定義覆蓋物之后般妙,發(fā)現(xiàn)創(chuàng)建多個元素會發(fā)生向下偏移纪铺,且逐個的偏移量越來越多,這是為什么股冗?
或許你可以檢查下DOM元素是不是沒有設置position:absolute;top:0px;left:0px;霹陡,如果沒有設置絕對定位以及坐標為(0, 0)的話,則transform是在元素原本的定位上進行偏移止状,且元素沒有脫離文檔流烹棉,后加入的元素會依次下移。

其他應用

DOMOverlay可以應用在各種圖文結合怯疤、不易繪制的元素上浆洗。 比如使用點聚合接口時,如果想要使用自定義樣式集峦,而且需要顯示簇大小伏社,就可以使用自定義DOM元素來表達聚合簇抠刺。

image

再比如編輯器中,繪制和編輯圖形時圖形需要實時變化摘昌,使用矢量圖形圖層需要不斷重構數(shù)據(jù)速妖,有較大開銷,所以也是結合DOM覆蓋物聪黎,通過SVG渲染單個圖形罕容。

image

另外,有的同學還問到稿饰,JSAPI v2中的marker跳動動畫在GL里怎么實現(xiàn)呢锦秒?其實也可以使用自定義覆蓋物來實現(xiàn),官網(wǎng)也提供了marker動畫示例喉镰。

什么情況下不適合使用DOMOverlay旅择?
需要注意的是泡挺,當你需要繪制大量(>1000)的覆蓋物時是不適合使用DOMOverlay的钻注,因為每個DOM元素都是單獨進行定位更新的計算固逗,會帶來非常大的開銷贞岭,在地圖變化時會非吵短桑卡頓踢涌。
海量覆蓋物的渲染還是推薦使用MultiMarker/MultiPolygon等矢量圖形圖層秋冰,或者位置數(shù)據(jù)可視化API犁罩,提供了散點圖偿凭、弧線圖产弹、軌跡圖、區(qū)域圖等可視化類型弯囊。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末痰哨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匾嘱,更是在濱河造成了極大的恐慌斤斧,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霎烙,死亡現(xiàn)場離奇詭異撬讽,居然都是意外死亡,警方通過查閱死者的電腦和手機悬垃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門游昼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尝蠕,你說我怎么就攤上這事烘豌。” “怎么了看彼?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵廊佩,是天一觀的道長囚聚。 經常有香客問我,道長标锄,這世上最難降的妖魔是什么顽铸? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮鸯绿,結果婚禮上跋破,老公的妹妹穿的比我還像新娘。我一直安慰自己瓶蝴,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布租幕。 她就那樣靜靜地躺著舷手,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劲绪。 梳的紋絲不亂的頭發(fā)上男窟,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音贾富,去河邊找鬼歉眷。 笑死,一個胖子當著我的面吹牛颤枪,可吹牛的內容都是我干的汗捡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼畏纲,長吁一口氣:“原來是場噩夢啊……” “哼扇住!你這毒婦竟也來了?” 一聲冷哼從身側響起盗胀,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤艘蹋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后票灰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體女阀,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年屑迂,在試婚紗的時候發(fā)現(xiàn)自己被綠了浸策。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡屈糊,死狀恐怖的榛,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情逻锐,我是刑警寧澤夫晌,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布雕薪,位于F島的核電站,受9級特大地震影響晓淀,放射性物質發(fā)生泄漏所袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一凶掰、第九天 我趴在偏房一處隱蔽的房頂上張望燥爷。 院中可真熱鬧,春花似錦懦窘、人聲如沸前翎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽港华。三九已至,卻和暖如春午衰,著一層夾襖步出監(jiān)牢的瞬間立宜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工臊岸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留橙数,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓帅戒,卻偏偏與公主長得像灯帮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜘澜,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容