mapbox-gl教程(5):常见事件与组件

老实说,这一章写起来有些鸡肋,因为mapbox官方api文档已经写得非常详细了,而且是中文文档且配有示例,读起来非常舒服。
但为了章节内容的完整性,本章仍演示了常见的事件与组件,并记录下笔者使用时遇到的坑以供参考。

事件

mapbox的事件, 通过map.on函数来注册监听,
通过map.off函数来移除监听。
这两个函数通过传入监听事件类型和回调函数来实现监听/取消监听,例如

1
2
3
map.on('click', (e) => {
console.log(e)
})

表示注册一个点击(click)事件,当点击地图时将点击对象打印出来。

也可以指定图层id,使得事件仅在点击到这个图层时触发:

1
2
3
map.on('click', 'poi', (e) => {
console.log(e)
})

官方文档列出了所有的事件名称,大致可分为如下几类:

分类 说明 事件名称
地图对象状态 当地图状态发生变化时触发,例如初始化完成、地图被移除、大小变化等 resize remove load
点击 地图被点击时触发,这类事件可以指定layerId mousedown mouseup mouseover mousemove click dblclick mouseenter mouseleave mouseout contextmenu touchstart touchend
点击(不可指定图层) 地图被点击时触发,这类事件不能指定layerId wheel touchmove touchcancel
显示区域变化 地图的范围、zoom、视角等变化时触发 move movestart moveend drag dragstart dragend zoom zoomstart zoomend rotatestart rotate rotateend pitchstart pitch pitchend boxzoomstart boxzoomend boxzoomcancel
图层展现过程 数据加载/展现渲染开始、完成等时间点上触发 render idle data styledata sourcedata dataloading styledataloading sourcedataloading styleimagemissing
异常 一些意外的异常产生时触发 webglcontextlost webglcontextrestored error

地图状态

这个我们很熟悉了,前面几章的例子中,我们需要在map.on('load')监听map加载完成后,才能进行相关操作。

点击事件(可指定图层的事件、不可指定图层的事件)

通过点击事件,我们可以拿到鼠标点击的经纬度,如果指定了图层id,我们还能拿到图层中被点击的要素:

1
2
3
map1.on('click', 'states-layer', function (e) {
alert(e.features[0].properties.name + ' ' + e.lngLat.lng + ',' + e.lngLat.lat)
});

显示区域变化

通过监听区域的变化,我们可以做很多基于地图范围的事情,例如,我们的某个图层数据量很大,一次性加载到前端会给浏览器很大压力, 此时我们可以考虑在moveend事件触发时再去加载可视范围内的数据,这样就大大减少了数据量,又不会影响视觉效果。

图层展现过程

mapbox中的图层展现出来,需要经历数据加载和图层渲染两个过程,我们可以监听这个过程中的关键节点来做一些事情。 例如,我们可以在矢量瓦片数据加载完成时,把瓦片中的水系数据清空,这样地图上就看不到水系图层了。

1
2
3
4
5
6
7
8
9
10
11
12
map0.on('data', (e) => {
if (!e.tile || !e.tile.buckets) {
return
}
const tile = e.tile;
const buckets = tile.buckets;
for (let key in buckets) {
if (key.indexOf('water') >= 0) {
delete buckets[key]
}
}
})

(当然这个例子并没有实际用途,我们不需要水系图层的话,设置图层的可见性或者直接把图层移除即可,不必如此复杂。 这里只是演示一下data函数的用法,在后续熟悉了矢量瓦片的字节格式后,我们就可以玩出在前端直接改矢量瓦片数据的骚操作了)

组件

官方的叫法是User Interface
主要有如下几种组件:

名称 作用
control 在地图的四个角落放一些小工具按钮(官方提供的或是自己写的)
popup 在地图上放一个弹出框
marker 标记,把一个图标放在地图上

control

这个例子是在地图左上角放了两个control:官方自带的导航控件NavigationControl以及我们自己编写的2.5d视角控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<div id='map2' style='width: 90%; height: 300px;'></div>
<button id="btn3d">3d</button>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiaW1saXV5dSIsImEiOiJjbDM4aHM4eXowMDBpM2RvZGdxdGZjeWMxIn0.mYtay02E_Z4iYOsDx3IdoA';
const map2 = new mapboxgl.Map({
container: 'map2',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-100.04, 38.907],
zoom: 3
});

//官方控件
map2.addControl(new mapboxgl.NavigationControl(), "top-left");

//自定义控件
class PitchToggle {
constructor({ bearing = -20, pitch = 70}) {
this._bearing = bearing;
this._pitch = pitch;
}
onAdd(map) {
this._map = map;
let _this = this;
this._btn = document.getElementById('btn3d');
this._btn.onclick = function() {
if (map.getPitch() === 0) {
let options = { pitch: _this._pitch, bearing: _this._bearing };
options.zoom = _this._minpitchzoom;
map.easeTo(options);
} else {
map.easeTo({ pitch: 0, bearing: 0 });
}
};
this._container = document.createElement("div");
this._container.className = "mapboxgl-ctrl-group mapboxgl-ctrl";
this._container.appendChild(this._btn);
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
}
map2.addControl(new PitchToggle({ minpitchzoom: 11 }), "top-left");
</script>

结合我们点击事件的例子,现在把alert改成一个弹出框气泡:

1
2
3
4
5
6
7
map3.on('click', 'states-layer', function (e) {
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML(e.features[0].properties.name)
.addTo(map3);
});

实际开发中,我们往往会把setHTML换成setDOMContent,从而避免html字符串拼接,更有利于前端设计出美观的弹出框

marker

marker用于在地图上画一个图标,并支持拖拽这个图标,其性能相比构造一个点图层要差很多,几百个marker打到地图上就能感到卡顿。
因此,除非有明确的拖动需求(比如编辑数据),或者临时性的打点,一般建议用点图层来替代marker。

这个例子演示了如何创建一个可拖动的marker。


本文采用 CC BY-SA 4.0 协议 ,转载请注明原始链接: https://blog.wowtools.org/2021/08/23/2021-08-23-mapbox-gl-tutorial-5/

×

请作者喝杯咖啡