Skip to content

地图(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 实例可以通过监听 MbMapcreated 事件来获取。

  • 如果您的组件可以放到 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是否开启抗锯齿模式; 关闭后性能将有所提升booleantrue
bearingv-model 地图初始化时的方位角(旋转角度)number0
bounds地图初始化时的四至范围。如果设置了 bounds 将会覆盖掉 center 和 zoom 的设置 其值为rectangle的西南点与东北点,例如:[[-73.9876, 40.7661], [-73.9397, 40.8002]]IndexAny / AnyArrundefined
centerv-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 / AnyArrundefined
maxzoom地图最大缩放级别number22
minzoom地图最小缩放级别number0
pitchv-model 地图初始化时的倾角number0
zoomv-model 地图缩放级别number2
hash地图的位置信息是否同步到浏览器URL的hash上boolean / stringfalse
interactive地图是否可交互booleantrue
crs坐标系,优先级低于extendPropsstring / 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[]