尝试用 pinia与hooks更方便地管理map相关方法
首先创建一个pinia store 用来生成map实例与操作map的一些方法
// marsMapStore.ts
import * as mars3d from "mars3d";
import type Mars3d from "mars3d";
// 各个页面map.ts中的"mapMounted"生命周期方法
type MountedFunc = ((mapIns: mars3d.Map) => void) | (() => void);
/**
* 读取默认配置文件
* 默认配置文件路径 /config/config.json
*/
const defaultConfigUrl = `/config/config.json?time=${new Date().getTime()}`;
/**
* 有关地图实例与相关方法与生命周期的store
*/
export const useMarsMapStore = defineStore("MarsMap", () => {
// * state
const map: Ref<Mars3d.Map | null> = ref(null);
/** mapMounted 启动队列
* 各个页面map.ts中的"mapMounted"生命周期方法
* 如果页面加载时map实例还未生成,会将对应的mapMounted方法添加到启动队列,在map实例生成后再调用
*/
const mountedList: Ref<MountedFunc[]> = ref([]);
// * getter
/** marsmap实例 */
const mapIns = computed(() => map.value);
// * action
/**
* 读取配置文件,创建地图
* @param id 地图容器id
* @param options 覆盖json读取的默认配置
* @param options json配置文件地址,默认 public/config/config.json
*/
const setMap = async (ElementId: string, options = {}, configUrl: string = defaultConfigUrl) => {
const { map3d: asyncOption } = await mars3d.Util.fetchJson({ url: configUrl }); // 读取地图配置
const option = mars3d.Util.merge(options, asyncOption); // 合并配置
map.value = new mars3d.Map(ElementId, option); // 创建map实例
/** 生成map实例后执行启动队列 */
mountedList.value.forEach((mapMounted) => map.value && mapMounted(map.value));
};
/**
* 启用map.ts生命周期,且 map实例未创建时,将mapMounted生命周期添加到mountedList
* 列表中的生命周期函数将在map实例生成时被调用
* @param fn map.ts => mapMounted生命周期函数
* 如果有其它想要在生成map实例后执行的方法,但不确定当时map实例是否创建,也可以通过暴露的addMounted方法将其添加到启动队列
* @example
* // xxxx.vue
* const store = useMarsMapStore();
* const xxx = (xx)=>{...}
* store.mapIns.value ? xxx(store.mapIns.value) : store.addMounted(xxx)
*/
const addMounted = (fn: MountedFunc) => mountedList.value.push(fn);
const removeMap = () => {
if (map.value) {
map.value.destroy();
map.value = null;
mountedList.value = [];
}
console.log("map销毁完成", map.value);
};
return { setMap, removeMap, addMounted, mapIns };
});
创建一个map容器
// app.vue
<script setup lang="ts">
// 只有action可以直接解构,state和getter解构会丢失响应性 需要用storeToRefs包裹
const { setMap } = useMarsMapStore();
// 传入容器id,创建map
onMounted(() => { setMap("mars3dContainer") });
</script>
<template>
<div id="app">
<div id="mars3dContainer" class="mars3dContainer"></div>
<div class="viewContainer">
<RouterView />
</div>
</div>
</template>
<style lang="less" scoped>
.mars3dContainer {
height: 100vh;
width: 100vw;
}
</style>
启用map.ts生命周期的方法
// useLifeCycle.ts
export const useLifeCycle: Hooks.LifeCycle.LifeCycle = ({ mapMounted, mapUnMounted }) => {
const mars = useMarsMapStore();
onBeforeMount(() => {
mapMounted && (mars.mapIns ? mapMounted(mars.mapIns) : mars.addMounted(mapMounted));
});
onBeforeUnmount(() => {
mapUnMounted && mapUnMounted();
});
};
对应dts文件
namespace Hooks {
namespace LifeCycle {
interface LifeCycleParams {
mapMounted?: MountedFunc;
mapUnMounted?: Func;
}
type LifeCycle = (params: LifeCycleParams) => void;
}
}
一个标准的map.ts
import * as mars3d from "mars3d";
let map: mars3d.Map;
let tiles3dLayer: mars3d.layer.TilesetLayer;
// ! 生命周期方法
const mapMounted = (mapIns: mars3d.Map) => {
map = mapIns;
setTilesetLayer();
};
const mapUnMounted = () => {
tiles3dLayer.remove();
tiles3dLayer = null;
};
// ! 生命周期方法 end
// 加载合肥市建筑物
function setTilesetLayer() {
// 模型
tiles3dLayer = new mars3d.layer.TilesetLayer({
name: "合肥市建筑物",
url: "//data.mars3d.cn/3dtiles/jzw-hefei/tileset.json",
maximumScreenSpaceError: 1,
maximumMemoryUsage: 1024,
position: { alt: -50 },
popup: [
{ field: "objectid", name: "编号" },
{ field: "name", name: "名称" },
{ field: "height", name: "楼高", unit: "米" }
]
});
map.addLayer(tiles3dLayer);
tiles3dLayer.on(mars3d.EventType.click, (e) => {
console.log("被点击的楼栋数据:", e);
});
}
// 使用对象的方式导出,以保留ts类型检查
export const lifeCycle = { mapMounted, mapUnMounted };
在对应的组件中 只需要
<script setup lang="ts">
import * as mapWork from "./map";
useLifeCycle(mapWork.lifeCycle)
</script>
整体逻辑:
- map.ts中书写方法,并在mapMounted中调用
- xxx.vue导入map.ts的生命周期(mapWork.lifeCycle),传递给"useLifeCycle"
- 组合式函数
useLifeCycle会在vue生命周期中调用map.ts生命周期- 当useLifeCycle运行时,如果map实例未创建,就将生命周期函数加入启动队列,map实例创建时
- 果map实例已创建 在vue生命周期中直接调用
回到开始的app.vue,即使给生成map实例加上延迟,也不会阻止其它组件加载
onMounted(() => {
setTimeout(() => {
setMap("mars3dContainer");
}, 5000);
});