地图(Map)
地图组件 交互方式:左键拖动,右键切换视角
TIP
需要在地图中显示字体时,一定要设置 glyphs
属性
TIP
如果要自定义坐标系,crs参数格式为字符串或者数组:[name, wkt, extent];
- name:坐标系名称,必填 -或者Proj4表述,必填。
- extent: 当前坐标系范围,[左,下,右,上]
具体实现可参见:常见问题
基础用法
center:[ 116.3466, 39.8704 ] zoom:8
bearing:0 pitch:0
mouse position:
示例源码
vue
<template>
<div style="height: 500px" class="vw-full vh-full">
<div style="height: 400px">
<mb-map
ref="mapInst"
v-model:zoom="mapZoom"
v-model:center="mapCenter"
v-model:bearing="bearing"
v-model:pitch="pitch"
@mousemove="mouseMoveHandler"
>
<div style="position: absolute; top: 10px; left: 10px">
<button class="primary mr10" @click="easeToRandomPos">
ease to some position
</button>
<button class="primary" @click="flyToRandomPos">
fly to some position
</button>
</div>
<mb-tianditu-layer />
</mb-map>
<div style="height: 60px; margin: 10px 0px">
<p class="text-muted">center:{{ mapCenter }} zoom:{{ mapZoom }}</p>
<p class="text-muted">bearing:{{ bearing }} pitch:{{ pitch }}</p>
<p class="text-muted">mouse position:{{ mousePosition }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { MapInstance } from '@mapbox-vue3/core'
const mapInst = ref<MapInstance>()
const mapCenter = ref([116.3466, 39.8704])
const mapZoom = ref(8)
const bearing = ref(0)
const pitch = ref(0)
const mousePosition = ref('')
const mouseMoveHandler = (movement) => {
mousePosition.value = movement.coordinates
}
const easeToRandomPos = () => {
mapInst.value
?.easeTo({
center: [
mapCenter.value[0] + getRandomArbitrary(-1, 1),
mapCenter.value[1] + getRandomArbitrary(-1, 1),
],
duration: 2000,
})
.then(() => {
// callback after over
})
}
const flyToRandomPos = () => {
mapInst.value
?.flyTo({
center: [getRandomArbitrary(-180, 180), getRandomArbitrary(-90, 90)],
zoom: getRandomArbitrary(0, 18),
})
.then(() => {
// callback after over
})
}
function getRandomArbitrary(min: number, max: number) {
return Math.random() * (max - min) + min
}
</script>
图层重排
示例源码
vue
<template>
<div style="height: 500px" class="vw-full vh-full">
<div style="height: 400px">
<mb-map ref="mapInst" :zoom="mapZoom" :center="mapCenter">
<mb-image-loader :images="images" />
<mb-image-frame-loader :images="images2" />
<div style="position: absolute; top: 10px; left: 10px">
<button
class="primary mr10"
@click="reorder(['buffer2', 's3', 'buffer1', 's2', 's1'])"
>
Order:buffer2, s3, buffer1, s2, s1(top)
</button>
<button
class="primary"
@click="reorder(['s2', 's3', 's1', 'buffer2', 'buffer1'])"
>
Order:s2, s3, s1, buffer2, buffer1(top)
</button>
</div>
<mb-tianditu-layer />
<mb-symbol-layer
id="s1"
:data="symbolDataSource"
text-field="label"
icon-image-field="icon"
:icon-size="0.5"
:text-offset="[0, -1]"
icon-allow-overlap
text-allow-overlap
/>
<mb-buffer-ellipse-layer
id="buffer1"
:center="mapCenter"
:x-semi-axis="10"
:y-semi-axis="5"
:angle="0"
border-color="red"
:border-opacity="0.5"
background-color="red"
center-color="red"
center-radius="6"
resizer-color="green"
resizer-radius="6"
rotater-color="black"
:background-opacity="0.1"
:closable="true"
:countable="true"
:draggable="true"
:resizable="true"
:rotatable="true"
/>
<mb-symbol-layer
id="s2"
:data="symbolDataSource2"
text-field="label"
icon-image-field="icon"
:icon-size="0.5"
:text-offset="[0, -1]"
:cluster="true"
:spiderify="true"
:cluster-mamb-zoom="18"
icon-allow-overlap
text-allow-overlap
/>
<mb-symbol-layer
id="s3"
:data="symbolDataSource3"
text-field="label"
icon-image-field="icon"
:icon-size="0.5"
:text-offset="[0, -1]"
icon-allow-overlap
text-allow-overlap
/>
<mb-buffer-sector-layer
id="buffer2"
:polygon="polygon"
:radius="7"
:center="mapCenter"
border-color="red"
:border-opacity="0.5"
background-color="red"
center-color="red"
resizer-color="blue"
:background-opacity="0.1"
:closable="true"
:countable="true"
:draggable="true"
:resizable="true"
:rotatable="true"
/>
</mb-map>
</div>
</div>
</template>
<script setup lang="ts">
import { onBeforeMount, ref } from 'vue'
import type { MapInstance } from '@mapbox-vue3/core/es'
type SymbolLayerData = Array<{
coordinates: number[]
properties?: Record<string, any>
}>
const mapInst = ref<MapInstance>()
const mapCenter = ref([116.3466, 39.8704])
const mapZoom = ref(10)
const symbolDataSource = ref<SymbolLayerData>([])
const symbolDataSource2 = ref<SymbolLayerData>([])
const symbolDataSource3 = ref<SymbolLayerData>([])
const polygon = [
[116.17381, 39.92155464],
[116.25105956968257, 40.02398993628292],
[116.31591012802686, 39.96769599504311],
]
const images = [
{
name: 'event',
type: 'link',
url: `${__RESOURCE_URL__}images/common_toget.png`,
},
{
name: 'event2',
type: 'link',
url: `${__RESOURCE_URL__}images/eme_team_soc_toget.png`,
},
]
const images2 = [
{
name: 'event3',
dir: 'v',
steps: 3,
duration: 300,
url: `${__RESOURCE_URL__}images/v-steps.png`,
},
]
const reorder = (layerIds: string[]) => {
mapInst.value?.reorderLayers(layerIds)
}
onBeforeMount(() => {
symbolDataSource.value = [
{
coordinates: [116.45346681596038, 39.90085772830014],
properties: {
icon: 'event',
},
},
{
coordinates: [...mapCenter.value],
properties: {
icon: 'event',
},
},
]
symbolDataSource2.value = [
{
coordinates: [116.46346681596, 39.90085772830014],
properties: {
icon: 'event2',
},
},
{
coordinates: [...mapCenter.value],
properties: {
icon: 'event2',
},
},
]
symbolDataSource3.value = [
{
coordinates: [116.47346681596038, 39.94085772830014],
properties: {
icon: 'event3',
iconSize: 0.1,
},
},
{
coordinates: [...mapCenter.value],
properties: {
icon: 'event3',
iconSize: 0.1,
},
},
]
})
</script>
获取地图实例方式
通常来讲,获取 MbMap
实例可以通过 ref
方式实现,获取封装的 mapbox
实例可以通过监听 MbMap
的 created
事件来获取。
- 如果您的组件可以放到
MbMap
组件里面,那么可以使用组件库提供的useMapCreated
hook来获取地图实例
demo3.vue
vue
<template>
<div style="height: 400px" class="vw-full vh-full">
<mb-map>
<instance1 />
</mb-map>
</div>
</template>
<script setup lang="ts">
import Instance1 from './instance1.vue'
</script>
instance1.vue
vue
<script lang="ts" setup>
import { useMapCreated } from '@mapbox-vue3/core'
const init = () => {
const map = getMapInstance()
const mapbox = getMapboxInstance()
console.log(map, mapbox)
}
const { getMapInstance, getMapboxInstance } = useMapCreated(init)
</script>
- 如果想在其他组件中和地图进行交互,例如操作ThreeJS等,您可以声明全局变量,然后监听
created
事件,并存储Mapbox地图实例,在需要的时候使用此实例操作地图。下面给出部分代码示例:
ts
// use-map.ts
import type { MapboxInstance } from '@mapbox-vue3/core'
let mapbox: MapboxInstance
function useMap() {
const onCreated = (mb: MapboxInstance) => {
console.log('map created', mb)
mapbox = mb
}
// 不需要的时候记住销毁
const destroy = () => {
mapbox = null
}
return {
onCreated,
destroy,
// 这里一定要写成方法,直接返回mapbox得到的结果将一直是undefined
getMapbox: () => mapbox
}
}
export { useMap }
vue
<template>
<MbMap
...
@created="onCreated"
/>
</template>
<script setup lang="ts">
import { useMap } from './use-map'
const { onCreated } = useMap()
</script>
在需要使用的地方:
vue
<script lang="ts">
import { useMap } from './use-map'
const { getMapbox } = useMap()
</script>
上述方式有个问题,只能在地图实例创建以后才能使用getMapbox
方法获取实例,否则得到的实例为undefined
- 如果您的代码逻辑在操作地图时不能确定地图是否已经完成创建,那么需要使用稍微复杂的方式进行地图操作,下面给出代码示例:
您需要安装 ts-deferred
工具库
ts
// map-deferred.ts
import { Deferred } from 'ts-deferred';
const defferedMap = new Deferred<void>();
const waitingForMap = async() => {
return await defferedMap.promise;
};
const resolveMap = () => {
defferedMap.resolve();
};
export { waitingForMap, resolveMap };
ts
// use-map.ts
import { resolveMap } from './map-deferred'
import type { MapboxInstance } from '@mapbox-vue3/core'
let mapbox: MapboxInstance
function useMap() {
const onCreated = (mb: MapboxInstance) => {
console.log('map created', mb)
mapbox = mb
resolveMap()
}
// 不需要的时候记住销毁
const destroy = () => {
mapbox = null
}
return {
onCreated,
destroy,
getMapbox: () => mapbox
}
}
export { useMap }
vue
// instance.vue
<script lang="ts" setup>
import { onMounted } from 'vue'
import { useMap } from './use-map'
import { waitingForMap } from './map-deferred'
const { getMapbox } = useMap()
onMounted(async() => {
await waitingForMap()
// 下面可以使用getMapbox()获取地图实例进行操作了
console.log('operate', getMapbox())
})
</script>
vue
<template>
<MbMap
...
@created="onCreated"
/>
<instance />
</template>
<script setup lang="ts">
import { useMap } from './use-map'
import Instance from './instance.vue'
const { onCreated } = useMap()
</script>
API
PROPS
名称 | 描述 | 类型 | 默认值 |
---|---|---|---|
antialias | 是否开启抗锯齿模式; 关闭后性能将有所提升 | boolean | true |
bearing | v-model 地图初始化时的方位角(旋转角度) | number | 0 |
bounds | 地图初始化时的四至范围。如果设置了 bounds 将会覆盖掉 center 和 zoom 的设置 其值为rectangle的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]] | IndexAny / AnyArr | undefined |
center | v-model 地图地理中心点坐标,采用经度、纬度的顺序 | number[] | [116.38745, 39.91266] |
map-style | 地图样式信息 包括字体文件路径以及精灵图资源路径 | IndexAny / string | { version: 8, sources: {}, layers: [], glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', sprite: "mapbox://sprites/mapbox/bright-v8" } |
glyphs | 字体文件路径,可单独设置,会覆盖mapStyle中值 | string | - |
sprite | 雪碧图路径,可单独设置,会覆盖mapStyle中值 | string | - |
max-bounds | 设置之后,地图将限制在给定的最大范围内 | IndexAny / AnyArr | undefined |
maxzoom | 地图最大缩放级别 | number | 22 |
minzoom | 地图最小缩放级别 | number | 0 |
pitch | v-model 地图初始化时的倾角 | number | 0 |
zoom | v-model 地图缩放级别 | number | 2 |
hash | 地图的位置信息是否同步到浏览器URL的hash上 | boolean / string | false |
interactive | 地图是否可交互 | boolean | true |
crs | 坐标系,优先级低于extendProps | string / AnyArr | - |
extend-props | 创建地图扩展属性,可参见Mapbox文档 | IndexAny | {} |
extendProps说明
Mapbox官方文档中创建地图接口的所有参数都可以通过此属性传入,优先级高于Map组件中其他所有属性
TIP
除了可用于 v-model 的四个属性,其他所有属性都是非响应式。 如需修改,可通过created事件参数获取map对象,然后调用Mapbox原生API实现。
EVENTS
名称 | 描述 | 参数 |
---|---|---|
created | 地图初始化完成事件 | map: MapboxInstance |
update:zoom | 地图缩放级别变化事件 | number |
update:center | 地图中心点变化事件 | number[] |
update:bearing | 地图方位角变化事件 | number |
update:pitch | 地图倾角变化事件 | number |
click | 鼠标点击事件 每次点击都会触发 | object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent |
dblclick | 鼠标双击事件 | object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent |
singleclick | 鼠标单击事件 | object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent |
mousemove | 鼠标移动事件 | object — 包含屏幕坐标pixel与经纬度coordinate,originalEvent |
mouseout | 鼠标离开地图事件 | MapMouseEvent & EventData |
SLOTS
名称 | 描述 |
---|---|
default | 默认插槽 |
METHODS
名称 | 描述 | 定义 |
---|---|---|
moveLayer | 将图层移动到beforeId所代表的图层前,若beforeId为空,则将图层移动至数组列表末尾(地图最顶层) | id: string, beforeId: string | undefined |
resize | 根据其 container 元素的尺寸调整地图的大小 | - |
setCenter | 设置地图中心点 | centerValue: number[] — 经纬度数组 |
jumpTo | 不用动态转换的情况下改变中心点、 缩放级别、方位角和倾斜度。地图将保留 options 没有指定的当前值。 | options: object{center, zoom, bearing, pitch} |
easeTo | 使用动态转换,将中心点、缩放级别、方位角和倾斜度组合的原有数值改为新数值。地图将保留 options 没有指定的当前值。 | options: object{center, zoom, bearing, pitch, duration = 1000, easing, offset} => promise |
flyTo | 按照飞行曲线到移动到目标位置。 | options: object{center, zoom, bearing, pitch, duration, easing, offset, animate, curve=1.42, speed=1.2, maxDuration} => promise |
stop | 停止所有地图平移/飞行。 | - |
cameraForBounds | 根据目标四至范围计算返回对应的镜头参数。 | bounds: number[] — 四至范围,其值的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]], options: object{padding,offset=[0,0]} |
fitBounds | 在指定的地理边界内平移和缩放地图,以包含其可见区域。 | bounds: number[] — 四至范围,其值的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]], options: object{padding,linear=false,offset=[0,0]} |
project | 返回一个表示像素坐标的Point,相对于地图的container,与指定的地理位置相对应。 | lnglatLike |
reorderLayers | 重排图层,参数为图层Id数组 | string[] |