之前介绍了widget的一些特性和功能,现在就针对实际需求看一下widget本身的代码是如何编写的。我们将一起来完成一个widget的编写,其中会介绍到我们在开发项目时会用到的一些技巧以及遵循的规范。由于gis项目本身的特殊性,如果不想后续当业务变的复杂之后造成一些难以排查的bug或者性能问题,最好也和我们一样遵循这些简单的规范。
    功能:我们接下来要实现的是底图切换的面板,具体功能如下图
    mars3d基于vue的widget使用(3) - 图1
    下面我们来一步步的实现

    1. 创建文件

    我们在widgets目录下新建一个目录 src/widgets/basic/manage-basemap 这里我们要求widget都统一存放在widgets下。然后创建 index.vue文件作为入口。由于将要实现的功能,包括一些地图相关的操作,所以我们还需要创建 map.ts 文件用于编写这些代码。这两个文件基础的代码已经在前面的两篇中有详细的说明,这里不做赘述。

    1. 获取底图

    获取地图这个操作本身很简单 map.getBasemap(true),但是需要考虑的是,我们需要通过map进行调用,所以这个操作需要放在 map.ts 文件中。这也是我们项目中的的另一个规范:只要是与mars3d相关的代码,我们都会放在 map.ts 中。看起来这个规范过于严苛,但是确实能够很好的帮我们将ui部分与地图部分进行接耦。我们初步的代码就变成了这样。

    1. // map.ts
    2. export function onMounted(mapInstance: mars3d.Map): void {
    3. map = mapInstance // 记录map
    4. const baseMaps = map.getBasemaps(true) // 获取底图
    5. const hasTerrain = map.hasTerrain // 是否显示地形
    6. }
    1. 接下来又面临一个问题,此时我们将地图与ui强制分离之后,ui部分该如何获取到 baseMaps 呢?此时就用到了我们之前说过的 eventTarget 了。代码很简单就直接放在下面了。 ```typescript // map.ts export const eventTarget = new mars3d.BaseClass()

    export function onMounted(mapInstance: mars3d.Map): void { map = mapInstance // 记录map

    const baseMaps = map.getBasemaps(true) // 获取底图 const hasTerrain = map.hasTerrain // 是否显示地形

    eventTarget.fire(“mapLoaded”, { baseMaps, hasTerrain }) // 触发了自定义事件 }

    1. ```typescript
    2. // index.vue
    3. import { onUnmounted, ref, markRaw } from "vue"
    4. import useLifecycle from "@mars/common/uses/use-lifecycle"
    5. import * as mapWork from "./map"
    6. const baseMaps = ref<any[]>([]) // 底图列表
    7. const active = ref("") // 当前高亮的底图
    8. const chkHasTerrain = ref(false) // 是否显示地形
    9. mapWork.eventTarget.on("mapLoaded", initData)
    10. onUnmounted(() => {
    11. mapWork.eventTarget.off("mapLoaded", initData)
    12. })
    13. function initData(e: any) {
    14. baseMaps.value = e.baseMaps.map((m: any) => {
    15. if (m.isAdded && m.show) {
    16. active.value = m.uuid
    17. }
    18. return {
    19. name: m.name,
    20. uuid: m.uuid,
    21. options: markRaw(m.options)
    22. }
    23. })
    24. chkHasTerrain.value = e.hasTerrain || false
    25. }

    上面的代码很好理解,不过有些细节还是要注意一下,请关注一下26行,可以看到这里是用了一个特殊的函数 markRaw。这是vue提供的一个函数,用于表示此对象不需要被vue进行响应式处理。这里的目的是mars3d本身会产生一些复杂的对象,比如map对象,矢量数据、图层等,这些复杂对象如果再次被vue进行一次响应式处理,将会带来极大的性能开销。

    1. 现在我们来实现,点击之后切换底图和切换地形,直接放代码。 ```typescript // map.ts export function changeBaseMaps(id: string) { map.basemap = id }

    export function changeTerrain(value: boolean) { map.hasTerrain = value }

    1. ```typescript
    2. // index.vue
    3. function changeBaseMaps(item: any) {
    4. mapWork.changeBaseMaps((active.value = item.uuid))
    5. }
    6. function changeTerrain() {
    7. mapWork.changeTerrain(chkHasTerrain.value)
    8. }
    1. 目前就剩下最后一步,支持外部更新高亮底图,用于实现通过其他方式切换底图,自动更新高亮的图块的功能。这里就要使用到widget的一个api widget.onUpdate来实现。其实就像相当于一个事件的订阅者。代码如下。 ```typescript // index.vue const { getWidget } = useWidget()

    const widget = getWidget(“manage-map” / widget的name /)

    widget.onUpdate((e) => { changeBaseMaps(e) })

    1. ```typescript
    2. // 外部改变底图时
    3. const { updateWidget } = useWidget()
    4. const changeBaseMap = (item) => {
    5. updateWidget("manage-map", item)
    6. }

    over~~~~