huweili-cesium 1.2.7 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.vue CHANGED
@@ -6,14 +6,6 @@
6
6
  </template>
7
7
 
8
8
  <script setup>
9
- /**
10
- * CesiumMap 组件 — 编排层
11
- *
12
- * 职责边界:
13
- * - 本文件:Vue 生命周期、Pinia 注册、渲染循环、工具栏、onload 事件
14
- * - cesiumMapBootstrap.js:Viewer / Scene / Imagery / Input 分层配置
15
- * - basis.js + cameraInteraction.js:2D 正交 / 3D 透视相机与交互
16
- */
17
9
  import * as Cesium from 'cesium'
18
10
  import { ref, watch, onMounted, onUnmounted, onActivated, onDeactivated, nextTick, toRaw } from 'vue'
19
11
  import { MapConfig, DroneConfig } from './js/config/index.js'
@@ -22,21 +14,9 @@ import { objectUtils } from './js/utils/cesium/object.js'
22
14
  import { basicConfig, teardownOrthographicWheelZoom } from './js/basis.js'
23
15
  import { useMapStore, MapLoadStatus } from './js/stores/mapStore.js'
24
16
  import { useEventBus } from './js/utils/useEventBus.js'
17
+ import { processMapConfigColors } from './js/tileProviders.js'
18
+ import { applyImageryLayers, resolveOnlineBasemap } from './js/utils/mapImagery.js'
25
19
  import { createCustomToolbarButtons } from './js/customToolbarButtons.js'
26
- import {
27
- resolveMapOptions,
28
- buildViewerOptions,
29
- buildContextOptions,
30
- buildSceneRenderOptions,
31
- applySceneRenderOptions,
32
- applyConfiguredImagery,
33
- applyViewerInputPolicy,
34
- createMapViewer,
35
- } from './js/utils/cesiumMapBootstrap.js'
36
-
37
- // ---------------------------------------------------------------------------
38
- // 0. 依赖注入(包内模块能力)
39
- // ---------------------------------------------------------------------------
40
20
  const { merge } = objectUtils()
41
21
  const { mouseController, setInitialCameraView, syncCameraInteractionMode } = basicConfig()
42
22
  const {
@@ -45,38 +25,46 @@ const {
45
25
  removeToolbarButtons,
46
26
  createDefaultToolbarButtons,
47
27
  } = createCustomToolbarButtons()
28
+
48
29
  const mapStore = useMapStore()
49
30
  const { on: onEvent, off: offEvent } = useEventBus()
50
-
51
- // ---------------------------------------------------------------------------
52
- // 1. 组件接口
53
- // ---------------------------------------------------------------------------
54
31
  const emit = defineEmits(['onload'])
55
32
  const props = defineProps({
56
- options: { type: Object, default: undefined },
57
- mapId: { type: String, default: 'default' },
58
- showDefaultToolbar: { type: Boolean, default: true },
59
- toolbarButtons: { type: Array, default: undefined },
60
- extraToolbarButtons: { type: Array, default: () => [] },
33
+ options: {
34
+ type: Object,
35
+ default: undefined,
36
+ },
37
+ mapId: {
38
+ type: String,
39
+ default: 'default',
40
+ },
41
+ showDefaultToolbar: {
42
+ type: Boolean,
43
+ default: true,
44
+ },
45
+ toolbarButtons: {
46
+ type: Array,
47
+ default: undefined,
48
+ },
49
+ extraToolbarButtons: {
50
+ type: Array,
51
+ default: () => [],
52
+ },
61
53
  })
62
54
 
63
- // ---------------------------------------------------------------------------
64
- // 2. 运行时状态
65
- // ---------------------------------------------------------------------------
66
- const cesiumContainer = ref(null)
67
- /** @type {import('cesium').Viewer|null} */
68
- let map = null
69
- let customButtons = []
70
- let isMapActive = true
71
- let animationId = null
72
- let lastActiveTime = Date.now()
73
- const IDLE_TIMEOUT = 30000
74
-
75
- // ---------------------------------------------------------------------------
76
- // 3. 工具栏
77
- // ---------------------------------------------------------------------------
55
+ /**
56
+ * 解析工具栏按钮配置
57
+ *
58
+ * 优先级规则:
59
+ * 1. 如果传入了完整的 toolbarButtons 数组,直接使用
60
+ * 2. 否则组合默认按钮和额外按钮
61
+ *
62
+ * @returns {Array} 工具栏按钮配置数组
63
+ */
78
64
  function resolveToolbarButtonConfigs() {
79
- if (props.toolbarButtons?.length) return props.toolbarButtons
65
+ if (props.toolbarButtons?.length) {
66
+ return props.toolbarButtons
67
+ }
80
68
  const configs = []
81
69
  if (props.showDefaultToolbar) {
82
70
  configs.push(...createDefaultToolbarButtons({ mapId: props.mapId }))
@@ -87,132 +75,245 @@ function resolveToolbarButtonConfigs() {
87
75
  return configs
88
76
  }
89
77
 
78
+ /**
79
+ * 处理场景模式变化(2D/3D 切换)
80
+ *
81
+ * 当 mapStore 中的场景模式发生变化时触发,同步更新相机交互模式
82
+ *
83
+ * @param {Object} param - 场景模式变化事件参数
84
+ * @param {string} param.mapId - 地图实例ID
85
+ * @param {string} param.sceneMode - 场景模式('2D' 或 '3D')
86
+ */
87
+ const handleSceneModeChange = ({ mapId, sceneMode }) => {
88
+ if (mapId !== props.mapId || !map || map.isDestroyed?.()) return
89
+ syncCameraInteractionMode(map, mapId, sceneMode)
90
+ }
91
+
92
+ const cesiumContainer = ref(null)
93
+ let map = null
94
+ let customButtons = []
95
+ let isMapActive = true
96
+
97
+ /**
98
+ * 挂载工具栏按钮到地图
99
+ *
100
+ * 先移除已存在的按钮,再根据配置添加新按钮
101
+ *
102
+ * @param {Cesium.Viewer} viewer - Cesium 地图实例
103
+ */
90
104
  function mountToolbarButtons(viewer) {
91
105
  if (!viewer || viewer.isDestroyed?.()) return
92
- removeToolbarButtons(customButtons)
106
+ removeToolbarButtons(customButtons) // 移除已存在的自定义工具栏按钮
93
107
  customButtons = []
94
- const configs = resolveToolbarButtonConfigs()
95
- if (configs.length) {
96
- customButtons = addToolbarButtons(viewer, configs)
108
+ const toolbarConfigs = resolveToolbarButtonConfigs() // 解析工具栏按钮配置
109
+ if (toolbarConfigs.length) {
110
+ customButtons = addToolbarButtons(viewer, toolbarConfigs) // 添加自定义工具栏按钮
97
111
  }
98
112
  }
99
113
 
100
- // ---------------------------------------------------------------------------
101
- // 4. 场景模式事件(工具栏 2D/3D 切换 → 同步相机交互)
102
- // ---------------------------------------------------------------------------
103
- function handleSceneModeChange({ mapId, sceneMode }) {
104
- if (mapId !== props.mapId || !map || map.isDestroyed?.()) return
105
- syncCameraInteractionMode(map, mapId, sceneMode)
106
- }
114
+ /**
115
+ * 初始化 Cesium 地图
116
+ *
117
+ * 完整的地图初始化流程:
118
+ * 1. 设置加载状态
119
+ * 2. 合并配置(MapConfig + DroneConfig + props.options)
120
+ * 3. 创建 Cesium Viewer 实例
121
+ * 4. 初始化地图工具和事件
122
+ * 5. 触发 onload 事件
123
+ */
124
+ const initCesium = async () => {
125
+ if (!cesiumContainer.value) return
126
+ mapStore.setMapLoadSta(MapLoadStatus.LOADING, props.mapId)
107
127
 
108
- function registerMapContext(viewer, mapOptions) {
109
- const center = mapOptions.scene.center || {}
110
- const sceneMode = mapOptions.scene.sceneMode || '3D'
111
-
112
- mapStore.setMapInfo(
113
- 'center',
114
- {
115
- lng: center.lng,
116
- lat: center.lat,
117
- heading: center.heading,
118
- pitch: center.pitch,
119
- roll: center.roll,
120
- duration: center.duration,
121
- alt: center.alt,
122
- },
123
- props.mapId,
124
- )
125
- mapStore.setMap(viewer, props.mapId)
126
- mapStore.setSceneMode(sceneMode, props.mapId)
127
-
128
- return { center, sceneMode }
129
- }
128
+ let mapOptions
129
+ if (MapConfig) {
130
+ mapOptions = MapConfig
131
+ }
130
132
 
131
- function emitMapReady(viewer, center) {
132
- emit('onload', {
133
- map: viewer,
134
- center,
135
- mapId: props.mapId,
136
- toolbar: {
137
- addToolbarButton: (opt) => addToolbarButton(viewer, opt),
138
- addToolbarButtons: (opts) => addToolbarButtons(viewer, opts),
139
- removeToolbarButtons,
140
- },
141
- syncCameraInteractionMode: (mode) => syncCameraInteractionMode(viewer, props.mapId, mode),
142
- })
143
- }
133
+ if (DroneConfig) {
134
+ mapStore.setTrailTime(DroneConfig.trailTime, props.mapId)
135
+ }
144
136
 
145
- function finalizeCameraAndRender(viewer, mapOptions, sceneMode) {
146
- viewer.resize()
147
- setInitialCameraView(viewer, mapOptions.scene.center, sceneMode, props.mapId)
148
- viewer.render()
149
- }
137
+ if (props.options) {
138
+ let exOptions
139
+ if (props.options.then) {
140
+ exOptions = toRaw(await props.options)
141
+ } else {
142
+ exOptions = toRaw(props.options)
143
+ }
150
144
 
151
- // ---------------------------------------------------------------------------
152
- // 5. 地图初始化主流程
153
- // ---------------------------------------------------------------------------
154
- async function initCesium() {
155
- if (!cesiumContainer.value) return
145
+ if (mapOptions) {
146
+ mapOptions = merge(mapOptions, exOptions)
147
+ } else {
148
+ mapOptions = exOptions
149
+ }
150
+ }
156
151
 
157
- mapStore.setMapLoadSta(MapLoadStatus.LOADING, props.mapId)
158
- if (DroneConfig?.trailTime != null) {
159
- mapStore.setTrailTime(DroneConfig.trailTime, props.mapId)
152
+ if (mapOptions) {
153
+ mapOptions = processMapConfigColors(mapOptions)
160
154
  }
161
155
 
162
- let mapOptions
163
156
  try {
164
- mapOptions = await resolveMapOptions({
165
- mapConfig: MapConfig,
166
- propsOptions: props.options,
167
- merge,
168
- toRaw,
169
- })
170
- if (!mapOptions) {
171
- throw new Error('缺少地图配置 MapConfig / options')
157
+ // 1、Viewer 创建参数
158
+ const viewerOptions = {
159
+ sceneMode: Cesium.SceneMode.SCENE3D, // 场景模式:固定为 3D(2D/3D 由相机与正交投影切换,不用 Cesium 内置 2D 模式)
160
+ useDefaultRenderLoop: false, // 禁用默认渲染循环,由应用自行 requestAnimationFrame 渲染
161
+ baseLayer: mapOptions.control.imageryProvider === false ? false : undefined, // false 时禁用默认底图,后续由 applyImageryLayers 加载
162
+ baseLayerPicker: mapOptions.control.baseLayerPicker, // 底图图层选择器
163
+ geocoder: mapOptions.control.geocoder, // 地址搜索框
164
+ homeButton: mapOptions.control.homeButton, // 回到初始视角按钮
165
+ sceneModePicker: mapOptions.control.sceneModePicker, // 2D/哥伦布/3D 场景模式切换器
166
+ navigationHelpButton: mapOptions.control.navigationHelpButton, // 导航操作说明按钮
167
+ animation: mapOptions.control.animation, // 动画控件
168
+ timeline: mapOptions.control.timeline, // 时间轴
169
+ infoBox: mapOptions.control.infoBox, // 实体信息弹窗
170
+ fullscreenButton: mapOptions.control.fullscreenButton, // 全屏按钮
171
+ vrButton: mapOptions.control.vrButton, // VR 模式按钮
172
+ terrainProvider: mapOptions.terrain.show // 地形服务;不开启时为 undefined
173
+ ? await Cesium.CesiumTerrainProvider.fromUrl(mapOptions.terrain.url, {
174
+ requestWaterMask: mapOptions.terrain.coastlineData, // 水体效果所需海岸线数据
175
+ requestVertexNormals: mapOptions.terrain.lightingData, // 地形光照法线数据
176
+ })
177
+ : undefined,
178
+ terrainShadows: Cesium.ShadowMode.RECEIVE_ONLY, // 地形只接收阴影,不投射,节省性能
179
+ creditContainer: document.createElement('div'), // 用空容器替换默认版权水印区域
172
180
  }
173
181
 
174
- // 1. Viewer:控件、地形、渲染循环策略
175
- const viewerOptions = await buildViewerOptions(mapOptions)
176
- const contextOptions = buildContextOptions()
177
- map = createMapViewer(cesiumContainer.value, viewerOptions, contextOptions)
182
+ // 2、WebGL 上下文与 GPU 偏好(传入 Viewer 的 contextOptions)
183
+ const contextOptions = {
184
+ webgl: {
185
+ alpha: false, // 禁用 canvas 透明通道,减少合成开销
186
+ depth: true, // 启用深度缓冲,保证远近正确遮挡
187
+ stencil: false, // 关闭模板缓冲,一般地图场景用不到
188
+ antialias: true, // 开启 MSAA 抗锯齿
189
+ premultipliedAlpha: true, // 预乘 alpha,与 Cesium 默认混合方式一致
190
+ preserveDrawingBuffer: false, // 不保留上一帧缓冲(截图需 true)
191
+ failIfMajorPerformanceCaveat: false, // 低性能 GPU 仍尝试创建上下文,避免直接失败
192
+ },
193
+ requestWebGl2: true, // 优先使用 WebGL 2.0
194
+ powerPreference: 'high-performance', // 提示浏览器选用独显/高性能 GPU
195
+ }
178
196
 
179
- // 2. Scene / Globe:光照、雾、大气
180
- const sceneRenderOptions = buildSceneRenderOptions(mapOptions)
181
- applySceneRenderOptions(map, sceneRenderOptions)
197
+ // 3、场景渲染项(Viewer 创建后再写到 scene / globe,对应 mapOptions.scene)
198
+ const sceneRenderOptions = {
199
+ debugShowFramesPerSecond: mapOptions.scene.debugShowFramesPerSecond, // 左上角 FPS 调试
200
+ enableLighting: mapOptions.scene.globe.enableLighting, // 地球随太阳的光照变化
201
+ depthTestAgainstTerrain: mapOptions.scene.globe.depthTestAgainstTerrain, // 实体与地形做深度测试
202
+ fogEnabled: mapOptions.scene.fog.enabled, // 远景雾效
203
+ skyAtmosphereShow: mapOptions.scene.skyAtmosphere.show, // 大气层外圈光晕
204
+ dynamicAtmosphereLighting: mapOptions.scene.globe.dynamicAtmosphereLighting, // 大气动态光照
205
+ dynamicAtmosphereLightingFromSun: mapOptions.scene.globe.dynamicAtmosphereLightingFromSun, // 光照以太阳为参考
206
+ }
182
207
 
183
- // 3. Imagery:在线底图
184
- applyConfiguredImagery(map, mapOptions)
208
+ map = new Cesium.Viewer(cesiumContainer.value, {
209
+ ...viewerOptions, // 控件、地形、底图等 Viewer 级配置
210
+ contextOptions, // WebGL 创建参数
211
+ })
185
212
 
186
- // 4. Input:双击、选中、鼠标扩展
187
- applyViewerInputPolicy(map, mapOptions)
188
- mouseController(map, props.mapId)
213
+ // 4、将 sceneRenderOptions 应用到已创建的 Scene / Globe
214
+ map.scene.debugShowFramesPerSecond = sceneRenderOptions.debugShowFramesPerSecond
215
+ map.scene.globe.enableLighting = sceneRenderOptions.enableLighting
216
+ map.scene.globe.depthTestAgainstTerrain = sceneRenderOptions.depthTestAgainstTerrain
217
+ map.scene.fog.enabled = sceneRenderOptions.fogEnabled
218
+ map.scene.skyAtmosphere.show = sceneRenderOptions.skyAtmosphereShow
219
+ map.scene.globe.dynamicAtmosphereLighting = sceneRenderOptions.dynamicAtmosphereLighting
220
+ map.scene.globe.dynamicAtmosphereLightingFromSun = sceneRenderOptions.dynamicAtmosphereLightingFromSun
221
+
222
+ const activeBasemap = resolveOnlineBasemap(mapOptions) // 解析在线底图
223
+ if (activeBasemap) {
224
+ applyImageryLayers(map, mapOptions, activeBasemap) // 应用在线底图
225
+ console.log(`加载在线底图:${activeBasemap.name},URL:${activeBasemap.url}`)
226
+ window._currentBasemapId = activeBasemap.id
227
+ } else { // 未找到可用底图,Cesium 将使用默认底图或空底图显示
228
+ console.warn('未找到可用底图,Cesium 将使用默认底图或空底图显示')
229
+ }
189
230
 
190
- // 5. 业务上下文:Pinia + 工具栏 + 加载态
191
- const { center, sceneMode } = registerMapContext(map, mapOptions)
192
- mountToolbarButtons(map)
193
- mapStore.setMapLoadSta(MapLoadStatus.LOADED, props.mapId)
231
+ if (map.screenSpaceEventHandler && mapOptions.control.disableDoubleClick) {
232
+ // 移除双击左键事件,防止地图缩放
233
+ map.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
234
+ }
194
235
 
195
- // 6. 相机与首帧(须在容器尺寸稳定后 resize)
196
- finalizeCameraAndRender(map, mapOptions, sceneMode)
236
+ if (mapOptions.control.disableEntityClick) {
237
+ map.selectedEntityChanged.addEventListener(() => {
238
+ map.selectedEntity = undefined
239
+ })
240
+ }
241
+
242
+ mouseController(map, props.mapId) // 初始化鼠标控制器
243
+
244
+ mapStore.setMapInfo(
245
+ 'center',
246
+ {
247
+ lng: mapOptions.scene.center.lng,
248
+ lat: mapOptions.scene.center.lat,
249
+ heading: mapOptions.scene.center.heading,
250
+ pitch: mapOptions.scene.center.pitch,
251
+ roll: mapOptions.scene.center.roll,
252
+ duration: mapOptions.scene.center.duration,
253
+ alt: mapOptions.scene.center.alt,
254
+ },
255
+ props.mapId,
256
+ )
257
+ mapStore.setMap(map, props.mapId)
258
+ const sceneMode = mapOptions.scene.sceneMode || '3D'
259
+ mapStore.setSceneMode(sceneMode, props.mapId)
260
+
261
+ const center = mapOptions.scene.center || {}
262
+
263
+ mountToolbarButtons(map) // 挂载工具栏按钮到地图
264
+ mapStore.setMapLoadSta(MapLoadStatus.LOADED, props.mapId)
197
265
 
198
- // 7. 通知宿主(相机、工具栏已就绪)
199
- emitMapReady(map, center)
200
266
  console.log(`Cesium 地图加载成功,mapId: ${props.mapId}`)
267
+ emit('onload', {
268
+ map,
269
+ center,
270
+ mapId: props.mapId,
271
+ toolbar: {
272
+ addToolbarButton: (opt) => addToolbarButton(map, opt), // 添加单个工具栏按钮
273
+ addToolbarButtons: (opts) => addToolbarButtons(map, opts), // 添加多个工具栏按钮
274
+ removeToolbarButtons, // 移除工具栏按钮
275
+ },
276
+ syncCameraInteractionMode: (mode) => syncCameraInteractionMode(map, props.mapId, mode), // 同步相机交互模式
277
+ })
278
+
279
+ map.resize() // 调整地图尺寸
280
+ setInitialCameraView(map, mapOptions.scene.center, sceneMode, props.mapId) // 初始化相机视角
281
+ map.render() // 渲染地图
201
282
 
202
- // 8. 自管渲染循环(useDefaultRenderLoop: false)
283
+ // 如果场景模式为 2D,则同步相机交互模式 (因为初始化相机视角时,已经同步了相机交互模式,保险起见再同步一次)
284
+ if (sceneMode === '2D') {
285
+ nextTick(() => {
286
+ if (!map || map.isDestroyed?.()) return
287
+ syncCameraInteractionMode(map, props.mapId, '2D') // 同步相机交互模式
288
+ })
289
+ }
290
+
291
+ // 启动持续渲染循环
203
292
  startContinuousRendering(map)
204
293
  } catch (error) {
205
- mapStore.setMapLoadSta(MapLoadStatus.FAILED, props.mapId)
206
- console.error('Cesium 地图加载失败:', error)
207
- }
294
+ mapStore.setMapLoadSta(MapLoadStatus.FAILED, props.mapId) // 设置地图加载状态为失败
295
+ console.error('Cesium 地图加载失败:', error)
208
296
  }
209
297
 
210
- // ---------------------------------------------------------------------------
211
- // 6. 渲染循环
212
- // ---------------------------------------------------------------------------
298
+ let animationId = null
299
+ let lastActiveTime = Date.now()
300
+ const IDLE_TIMEOUT = 30000
301
+
302
+ /**
303
+ * 启动持续渲染循环
304
+ *
305
+ * 使用 requestAnimationFrame 维持地图持续渲染,确保动画效果流畅
306
+ * 渲染条件:
307
+ * 1. 地图处于活跃状态(isMapActive = true)
308
+ * 2. 页面未隐藏(document.hidden = false)
309
+ * 3. 地图实例存在且未销毁
310
+ * 4. 最近有用户活动(lastActiveTime 小于空闲超时时间)
311
+ *
312
+ * @param {Cesium.Viewer} viewer - Cesium 地图实例
313
+ */
213
314
  function startContinuousRendering(viewer) {
214
315
  if (animationId) return
215
- const tick = () => {
316
+ function animate() {
216
317
  const now = Date.now()
217
318
  if (
218
319
  isMapActive &&
@@ -223,11 +324,17 @@ function startContinuousRendering(viewer) {
223
324
  ) {
224
325
  viewer.render()
225
326
  }
226
- animationId = requestAnimationFrame(tick)
327
+ animationId = requestAnimationFrame(animate)
227
328
  }
228
- tick()
329
+ animate()
229
330
  }
230
331
 
332
+ /**
333
+ * 停止持续渲染循环
334
+ *
335
+ * 取消 requestAnimationFrame,释放资源
336
+ * 用于 keep-alive 缓存、组件卸载或手动暂停渲染场景
337
+ */
231
338
  function stopRenderLoop() {
232
339
  if (animationId) {
233
340
  cancelAnimationFrame(animationId)
@@ -235,23 +342,49 @@ function stopRenderLoop() {
235
342
  }
236
343
  }
237
344
 
345
+ /**
346
+ * 设置用户活动监听器
347
+ *
348
+ * 监听用户的各种交互行为,更新最后活动时间戳
349
+ * 用于配合持续渲染循环判断是否需要继续渲染地图
350
+ *
351
+ * 监听的事件类型:
352
+ * - mousemove:鼠标移动
353
+ * - keydown:键盘按键
354
+ * - touchmove:触摸移动
355
+ * - wheel:滚轮滚动
356
+ * - click:点击
357
+ */
238
358
  function setupActivityListeners() {
239
- ;['mousemove', 'keydown', 'touchmove', 'wheel', 'click'].forEach((event) => {
359
+ const activities = ['mousemove', 'keydown', 'touchmove', 'wheel', 'click']
360
+ activities.forEach((event) => {
240
361
  document.addEventListener(event, () => {
241
362
  lastActiveTime = Date.now()
242
363
  })
243
364
  })
244
365
  }
245
366
 
246
- // ---------------------------------------------------------------------------
247
- // 7. keep-alive 恢复
248
- // ---------------------------------------------------------------------------
367
+ /**
368
+ * keep-alive 缓存中恢复地图渲染
369
+ *
370
+ * 当使用 keep-alive 包裹地图组件时,组件被导航离开后会被缓存
371
+ * 当再次导航回来时,需要恢复地图的活跃状态和渲染循环
372
+ *
373
+ * 恢复步骤:
374
+ * 1. 设置当前地图ID到store
375
+ * 2. 标记地图为活跃状态
376
+ * 3. 更新最后活动时间戳
377
+ * 4. 在 nextTick 中执行:
378
+ * - 调整地图尺寸(确保容器大小正确)
379
+ * - 同步相机交互模式(2D/3D)
380
+ * - 执行一次渲染
381
+ * - 启动持续渲染循环(如果尚未启动)
382
+ */
249
383
  function resumeMapAfterCache() {
250
384
  if (!map || map.isDestroyed?.()) return
251
385
  mapStore.setCurrentMapId(props.mapId)
252
386
  isMapActive = true
253
387
  lastActiveTime = Date.now()
254
-
255
388
  nextTick(() => {
256
389
  if (!map || map.isDestroyed?.()) return
257
390
  map.resize()
@@ -262,51 +395,57 @@ function resumeMapAfterCache() {
262
395
  })
263
396
  }
264
397
 
265
- // ---------------------------------------------------------------------------
266
- // 8. 生命周期
267
- // ---------------------------------------------------------------------------
398
+ // 宿主传入的 toolbarButtons 变化时,重新挂载工具栏(先移除旧按钮再按新配置添加)
268
399
  watch(
269
400
  () => props.toolbarButtons,
270
401
  () => {
271
- if (map && !map.isDestroyed?.()) mountToolbarButtons(map)
402
+ if (map && !map.isDestroyed?.()) { // 如果地图实例存在且未销毁,则重新挂载工具栏
403
+ mountToolbarButtons(map) // 重新挂载工具栏
404
+ }
272
405
  },
273
- { deep: true },
406
+ { deep: true }, // 深度监听:数组项或按钮对象字段变更也会触发
274
407
  )
275
408
 
276
409
  onMounted(() => {
277
- mapStore.setCurrentMapId(props.mapId)
278
- setupActivityListeners()
279
- onEvent('map-scene-mode-changed', handleSceneModeChange)
280
- initCesium()
410
+ mapStore.setCurrentMapId(props.mapId) // 初始化当前地图ID
411
+ setupActivityListeners() // 初始化用户活动监听器,用于判断是否需要继续渲染地图,避免空闲超时导致的渲染暂停
412
+ onEvent('map-scene-mode-changed', handleSceneModeChange) // 监听场景模式变化事件,用于同步相机交互模式
413
+ initCesium() // 初始化 Cesium 地图实例
281
414
  })
282
415
 
416
+ // 组件激活时,恢复地图渲染
283
417
  onActivated(() => {
284
- mapStore.setCurrentMapId(props.mapId)
285
- if (!map || map.isDestroyed?.()) {
286
- isMapActive = true
287
- initCesium()
418
+ mapStore.setCurrentMapId(props.mapId) // 设置当前地图ID
419
+ if (!map || map.isDestroyed?.()) { // 如果地图实例不存在或已销毁,则重新初始化地图
420
+ isMapActive = true // 标记地图为活跃状态
421
+ initCesium() // 重新初始化地图
288
422
  return
289
423
  }
290
- resumeMapAfterCache()
424
+ resumeMapAfterCache() // 从 keep-alive 缓存中恢复地图渲染 (恢复地图的活跃状态和渲染循环)
291
425
  })
292
426
 
427
+ // 组件失活时,停止地图渲染
293
428
  onDeactivated(() => {
294
- isMapActive = false
295
- stopRenderLoop()
429
+ isMapActive = false // 标记地图为非活跃状态
430
+ stopRenderLoop() // 停止持续渲染循环,释放资源
296
431
  })
297
432
 
433
+ // 组件卸载时,销毁地图实例
298
434
  onUnmounted(() => {
299
435
  isMapActive = false
300
- stopRenderLoop()
301
- offEvent('map-scene-mode-changed', handleSceneModeChange)
302
- if (map) teardownOrthographicWheelZoom(map)
303
- removeToolbarButtons(customButtons)
436
+ stopRenderLoop() // 停止持续渲染循环,释放资源
437
+ offEvent('map-scene-mode-changed', handleSceneModeChange) // 移除场景模式变化事件监听器
438
+ if (map) {
439
+ teardownOrthographicWheelZoom(map) // 移除正交投影滚轮缩放事件监听器
440
+ }
441
+ removeToolbarButtons(customButtons) // 移除自定义工具栏按钮
304
442
  customButtons = []
305
443
  if (map) {
306
444
  map.destroy()
307
445
  map = null
308
446
  }
309
- mapStore.removeMapInstance(props.mapId)
447
+ mapStore.removeMapInstance(props.mapId) // 从 store 中移除地图实例
448
+ mapStore.setCurrentMapId('') // 重置当前地图ID
310
449
  })
311
450
  </script>
312
451
 
package/js/index.js CHANGED
@@ -36,16 +36,6 @@ export {
36
36
  teardownOrthographicWheelZoom,
37
37
  syncCameraInteractionMode,
38
38
  } from './utils/cameraInteraction.js'
39
- export {
40
- resolveMapOptions,
41
- buildViewerOptions,
42
- buildContextOptions,
43
- buildSceneRenderOptions,
44
- applySceneRenderOptions,
45
- applyConfiguredImagery,
46
- applyViewerInputPolicy,
47
- createMapViewer,
48
- } from './utils/cesiumMapBootstrap.js'
49
39
 
50
40
  export {
51
41
  BaseConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huweili-cesium",
3
- "version": "1.2.7",
3
+ "version": "1.2.10",
4
4
  "description": "基于 Cesium 的地图工具库(无人机态势、轨迹、围栏、工具栏等)",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,164 +0,0 @@
1
- /**
2
- * Cesium Viewer 启动配置(按 Cesium 架构分层)
3
- *
4
- * 1. Viewer — 控件、地形、底图策略、渲染循环开关
5
- * 2. Context — WebGL 上下文
6
- * 3. Scene — 光照、雾、大气、调试项
7
- * 4. Imagery — 在线底图(业务配置驱动)
8
- * 5. Input — 双击、实体选中、鼠标扩展
9
- */
10
- import * as Cesium from 'cesium'
11
- import { processMapConfigColors } from '../tileProviders.js'
12
- import { applyImageryLayers, resolveOnlineBasemap } from './mapImagery.js'
13
-
14
- // ---------------------------------------------------------------------------
15
- // 阶段 0:合并业务配置
16
- // ---------------------------------------------------------------------------
17
-
18
- /**
19
- * @param {object} params
20
- * @param {object} [params.mapConfig] MapConfig
21
- * @param {object} [params.propsOptions] 组件 props.options(支持 Promise)
22
- * @param {Function} params.merge objectUtils().merge
23
- * @param {Function} params.toRaw vue toRaw
24
- */
25
- export async function resolveMapOptions({ mapConfig, propsOptions, merge, toRaw }) {
26
- let mapOptions = mapConfig || undefined
27
-
28
- if (propsOptions) {
29
- const exOptions = propsOptions.then
30
- ? toRaw(await propsOptions)
31
- : toRaw(propsOptions)
32
- mapOptions = mapOptions ? merge(mapOptions, exOptions) : exOptions
33
- }
34
-
35
- if (mapOptions) {
36
- mapOptions = processMapConfigColors(mapOptions)
37
- }
38
-
39
- return mapOptions
40
- }
41
-
42
- // ---------------------------------------------------------------------------
43
- // 阶段 1:Viewer 构造参数
44
- // ---------------------------------------------------------------------------
45
-
46
- export async function buildViewerOptions(mapOptions) {
47
- return {
48
- sceneMode: Cesium.SceneMode.SCENE3D,
49
- useDefaultRenderLoop: false,
50
- baseLayer: mapOptions.control.imageryProvider === false ? false : undefined,
51
- baseLayerPicker: mapOptions.control.baseLayerPicker,
52
- geocoder: mapOptions.control.geocoder,
53
- homeButton: mapOptions.control.homeButton,
54
- sceneModePicker: mapOptions.control.sceneModePicker,
55
- navigationHelpButton: mapOptions.control.navigationHelpButton,
56
- animation: mapOptions.control.animation,
57
- timeline: mapOptions.control.timeline,
58
- infoBox: mapOptions.control.infoBox,
59
- fullscreenButton: mapOptions.control.fullscreenButton,
60
- vrButton: mapOptions.control.vrButton,
61
- terrainProvider: mapOptions.terrain.show
62
- ? await Cesium.CesiumTerrainProvider.fromUrl(mapOptions.terrain.url, {
63
- requestWaterMask: mapOptions.terrain.coastlineData,
64
- requestVertexNormals: mapOptions.terrain.lightingData,
65
- })
66
- : undefined,
67
- terrainShadows: Cesium.ShadowMode.RECEIVE_ONLY,
68
- creditContainer: document.createElement('div'),
69
- }
70
- }
71
-
72
- // ---------------------------------------------------------------------------
73
- // 阶段 2:WebGL Context
74
- // ---------------------------------------------------------------------------
75
-
76
- export function buildContextOptions() {
77
- return {
78
- webgl: {
79
- alpha: false,
80
- depth: true,
81
- stencil: false,
82
- antialias: true,
83
- premultipliedAlpha: true,
84
- preserveDrawingBuffer: false,
85
- failIfMajorPerformanceCaveat: false,
86
- },
87
- requestWebGl2: true,
88
- powerPreference: 'high-performance',
89
- }
90
- }
91
-
92
- // ---------------------------------------------------------------------------
93
- // 阶段 3:Scene / Globe 渲染环境
94
- // ---------------------------------------------------------------------------
95
-
96
- export function buildSceneRenderOptions(mapOptions) {
97
- return {
98
- debugShowFramesPerSecond: mapOptions.scene.debugShowFramesPerSecond,
99
- enableLighting: mapOptions.scene.globe.enableLighting,
100
- depthTestAgainstTerrain: mapOptions.scene.globe.depthTestAgainstTerrain,
101
- fogEnabled: mapOptions.scene.fog.enabled,
102
- skyAtmosphereShow: mapOptions.scene.skyAtmosphere.show,
103
- dynamicAtmosphereLighting: mapOptions.scene.globe.dynamicAtmosphereLighting,
104
- dynamicAtmosphereLightingFromSun: mapOptions.scene.globe.dynamicAtmosphereLightingFromSun,
105
- }
106
- }
107
-
108
- export function applySceneRenderOptions(viewer, options) {
109
- viewer.scene.debugShowFramesPerSecond = options.debugShowFramesPerSecond
110
- viewer.scene.globe.enableLighting = options.enableLighting
111
- viewer.scene.globe.depthTestAgainstTerrain = options.depthTestAgainstTerrain
112
- viewer.scene.fog.enabled = options.fogEnabled
113
- viewer.scene.skyAtmosphere.show = options.skyAtmosphereShow
114
- viewer.scene.globe.dynamicAtmosphereLighting = options.dynamicAtmosphereLighting
115
- viewer.scene.globe.dynamicAtmosphereLightingFromSun = options.dynamicAtmosphereLightingFromSun
116
- }
117
-
118
- // ---------------------------------------------------------------------------
119
- // 阶段 4:Imagery 底图
120
- // ---------------------------------------------------------------------------
121
-
122
- /**
123
- * @returns {string|undefined} 当前底图 id(写入 window._currentBasemapId)
124
- */
125
- export function applyConfiguredImagery(viewer, mapOptions) {
126
- const activeBasemap = resolveOnlineBasemap(mapOptions)
127
- if (activeBasemap) {
128
- applyImageryLayers(viewer, mapOptions, activeBasemap)
129
- console.log(`加载在线底图:${activeBasemap.name},URL:${activeBasemap.url}`)
130
- if (typeof window !== 'undefined') {
131
- window._currentBasemapId = activeBasemap.id
132
- }
133
- return activeBasemap.id
134
- }
135
- console.warn('未找到可用底图,Cesium 将使用默认底图或空底图显示')
136
- return undefined
137
- }
138
-
139
- // ---------------------------------------------------------------------------
140
- // 阶段 5:输入与交互策略
141
- // ---------------------------------------------------------------------------
142
-
143
- export function applyViewerInputPolicy(viewer, mapOptions) {
144
- if (viewer.screenSpaceEventHandler && mapOptions.control.disableDoubleClick) {
145
- viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
146
- }
147
-
148
- if (mapOptions.control.disableEntityClick) {
149
- viewer.selectedEntityChanged.addEventListener(() => {
150
- viewer.selectedEntity = undefined
151
- })
152
- }
153
- }
154
-
155
- // ---------------------------------------------------------------------------
156
- // 阶段 6:创建 Viewer
157
- // ---------------------------------------------------------------------------
158
-
159
- export function createMapViewer(container, viewerOptions, contextOptions) {
160
- return new Cesium.Viewer(container, {
161
- ...viewerOptions,
162
- contextOptions,
163
- })
164
- }