之前介绍了widget的一些特性和功能,现在就针对实际需求看一下widget本身的代码是如何编写的。我们将一起来完成一个widget的编写,其中会介绍到我们在开发项目时会用到的一些技巧以及遵循的规范。由于gis项目本身的特殊性,如果不想后续当业务变的复杂之后造成一些难以排查的bug或者性能问题,最好也和我们一样遵循这些简单的规范。
功能:我们接下来要实现的是底图切换的面板,具体功能如下图
下面我们来一步步的实现
- 创建文件
我们在widgets目录下新建一个目录 src/widgets/basic/manage-basemap
这里我们要求widget都统一存放在widgets下。然后创建 index.vue
文件作为入口。由于将要实现的功能,包括一些地图相关的操作,所以我们还需要创建 map.ts
文件用于编写这些代码。这两个文件基础的代码已经在前面的两篇中有详细的说明,这里不做赘述。
- 获取底图
获取地图这个操作本身很简单 map.getBasemap(true)
,但是需要考虑的是,我们需要通过map进行调用,所以这个操作需要放在 map.ts 文件中。这也是我们项目中的的另一个规范:只要是与mars3d相关的代码,我们都会放在 map.ts 中。看起来这个规范过于严苛,但是确实能够很好的帮我们将ui部分与地图部分进行接耦。我们初步的代码就变成了这样。
// map.ts
export function onMounted(mapInstance: mars3d.Map): void {
map = mapInstance // 记录map
const baseMaps = map.getBasemaps(true) // 获取底图
const hasTerrain = map.hasTerrain // 是否显示地形
}
- 接下来又面临一个问题,此时我们将地图与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 }) // 触发了自定义事件 }
```typescript
// index.vue
import { onUnmounted, ref, markRaw } from "vue"
import useLifecycle from "@mars/common/uses/use-lifecycle"
import * as mapWork from "./map"
const baseMaps = ref<any[]>([]) // 底图列表
const active = ref("") // 当前高亮的底图
const chkHasTerrain = ref(false) // 是否显示地形
mapWork.eventTarget.on("mapLoaded", initData)
onUnmounted(() => {
mapWork.eventTarget.off("mapLoaded", initData)
})
function initData(e: any) {
baseMaps.value = e.baseMaps.map((m: any) => {
if (m.isAdded && m.show) {
active.value = m.uuid
}
return {
name: m.name,
uuid: m.uuid,
options: markRaw(m.options)
}
})
chkHasTerrain.value = e.hasTerrain || false
}
上面的代码很好理解,不过有些细节还是要注意一下,请关注一下26行,可以看到这里是用了一个特殊的函数 markRaw。这是vue提供的一个函数,用于表示此对象不需要被vue进行响应式处理。这里的目的是mars3d本身会产生一些复杂的对象,比如map对象,矢量数据、图层等,这些复杂对象如果再次被vue进行一次响应式处理,将会带来极大的性能开销。
- 现在我们来实现,点击之后切换底图和切换地形,直接放代码。 ```typescript // map.ts export function changeBaseMaps(id: string) { map.basemap = id }
export function changeTerrain(value: boolean) { map.hasTerrain = value }
```typescript
// index.vue
function changeBaseMaps(item: any) {
mapWork.changeBaseMaps((active.value = item.uuid))
}
function changeTerrain() {
mapWork.changeTerrain(chkHasTerrain.value)
}
- 目前就剩下最后一步,支持外部更新高亮底图,用于实现通过其他方式切换底图,自动更新高亮的图块的功能。这里就要使用到widget的一个api
widget.onUpdate
来实现。其实就像相当于一个事件的订阅者。代码如下。 ```typescript // index.vue const { getWidget } = useWidget()
const widget = getWidget(“manage-map” / widget的name /)
widget.onUpdate((e) => { changeBaseMaps(e) })
```typescript
// 外部改变底图时
const { updateWidget } = useWidget()
const changeBaseMap = (item) => {
updateWidget("manage-map", item)
}
over~~~~