leiting-bim 2.2.15 → 2.2.16
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-DCy5yaJf.js → index-BBQ0Ss7h.js} +4786 -4729
- package/{index-JNQ_QjcY.js → index-Cr65jF2U.js} +1 -1
- package/leiting-bim.es.js +1 -1
- package/leiting-bim.umd.js +11 -11
- package/leitingbim.css +1 -1
- package/package.json +1 -1
- package/skybox/021/nx.jpg +0 -0
- package/skybox/021/ny.jpg +0 -0
- package/skybox/021/nz.jpg +0 -0
- package/skybox/021/px.jpg +0 -0
- package/skybox/021/py.jpg +0 -0
- package/skybox/021/pz.jpg +0 -0
- package/skybox/022/nx.jpg +0 -0
- package/skybox/022/ny.jpg +0 -0
- package/skybox/022/nz.jpg +0 -0
- package/skybox/022/px.jpg +0 -0
- package/skybox/022/py.jpg +0 -0
- package/skybox/022/pz.jpg +0 -0
- package/skybox/023/nx.jpg +0 -0
- package/skybox/023/ny.jpg +0 -0
- package/skybox/023/nz.jpg +0 -0
- package/skybox/023/px.jpg +0 -0
- package/skybox/023/py.jpg +0 -0
- package/skybox/023/pz.jpg +0 -0
- package/skybox/024/nx.jpg +0 -0
- package/skybox/024/ny.jpg +0 -0
- package/skybox/024/nz.jpg +0 -0
- package/skybox/024/px.jpg +0 -0
- package/skybox/024/py.jpg +0 -0
- package/skybox/024/pz.jpg +0 -0
- package/skybox/025/nx.jpg +0 -0
- package/skybox/025/ny.jpg +0 -0
- package/skybox/025/nz.jpg +0 -0
- package/skybox/025/px.jpg +0 -0
- package/skybox/025/py.jpg +0 -0
- package/skybox/025/pz.jpg +0 -0
- package/skybox/long_text_4E0781D6-58CB-4AD9-9079-ABA999E80B79.txt +637 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="gis-panel">
|
|
3
|
+
<!-- 顶部标题栏 -->
|
|
4
|
+
<div class="panel-header">
|
|
5
|
+
<div class="header-left">
|
|
6
|
+
<span class="header-icon">🌌</span>
|
|
7
|
+
<span class="header-title-text">自定义天空盒</span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="header-center"></div>
|
|
10
|
+
<div class="header-right">
|
|
11
|
+
<div class="header-status">
|
|
12
|
+
<span class="status-dot" :class="{ ready: isReady }"></span>
|
|
13
|
+
{{ isReady ? '就绪' : '初始化...' }}
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="panel-body">
|
|
19
|
+
<!-- 近景天空盒选择 -->
|
|
20
|
+
<div class="control-section">
|
|
21
|
+
<div class="section-label">近景天空盒</div>
|
|
22
|
+
<div class="btn-grid near-grid">
|
|
23
|
+
<button
|
|
24
|
+
v-for="(item, index) in nearSkyBoxes"
|
|
25
|
+
:key="index"
|
|
26
|
+
class="custom-btn"
|
|
27
|
+
:class="{ active: currentNearIndex === index }"
|
|
28
|
+
@click="changeNearSkyBox(index)"
|
|
29
|
+
>
|
|
30
|
+
{{ item.name }}
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- 视角控制 -->
|
|
36
|
+
<div class="control-section">
|
|
37
|
+
<div class="section-label">视角控制</div>
|
|
38
|
+
<div class="btn-grid">
|
|
39
|
+
<button class="custom-btn primary" @click="flyToHome">🚀 远景视角</button>
|
|
40
|
+
<button class="custom-btn primary" @click="saveCurrentView">💾 保存视角</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<!-- 当前状态信息 -->
|
|
45
|
+
<div class="control-section">
|
|
46
|
+
<div class="section-label">当前状态</div>
|
|
47
|
+
<div class="info-text">
|
|
48
|
+
当前显示: {{ currentSkyBoxName }}
|
|
49
|
+
</div>
|
|
50
|
+
<div class="info-text">
|
|
51
|
+
高度: {{ currentHeight.toFixed(0) }} 米
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<script setup>
|
|
59
|
+
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
|
60
|
+
|
|
61
|
+
const isReady = ref(false);
|
|
62
|
+
const currentNearIndex = ref(0);
|
|
63
|
+
const currentHeight = ref(0);
|
|
64
|
+
const activeSkyBoxType = ref('far');
|
|
65
|
+
|
|
66
|
+
// 默认近景天空盒配置(路径对应 lowCodeTheme/img/skybox/021~025,确认存在)
|
|
67
|
+
const defaultConfig = {
|
|
68
|
+
nearList: [
|
|
69
|
+
{ name: '近景1', path: './lowCodeTheme/img/skybox/021', ext: 'jpg' },
|
|
70
|
+
{ name: '近景2', path: './lowCodeTheme/img/skybox/022', ext: 'jpg' },
|
|
71
|
+
{ name: '近景3', path: './lowCodeTheme/img/skybox/023', ext: 'jpg' },
|
|
72
|
+
{ name: '近景4', path: './lowCodeTheme/img/skybox/024', ext: 'jpg' },
|
|
73
|
+
{ name: '近景5', path: './lowCodeTheme/img/skybox/025', ext: 'jpg' },
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// 直接使用注入的 props,不再 defineProps
|
|
78
|
+
const nearSkyBoxes = computed(() => props.nearList || defaultConfig.nearList);
|
|
79
|
+
|
|
80
|
+
const currentSkyBoxName = computed(() => {
|
|
81
|
+
if (activeSkyBoxType.value === 'far') return '远景天空盒';
|
|
82
|
+
const item = nearSkyBoxes.value[currentNearIndex.value];
|
|
83
|
+
return item ? item.name : '未知';
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
let viewer = null;
|
|
87
|
+
let originalSkyBox = null;
|
|
88
|
+
let originalSkyAtmosphereShow = true;
|
|
89
|
+
let originalCaptured = false;
|
|
90
|
+
let nearSkyBoxInstances = [];
|
|
91
|
+
let renderHandler = null;
|
|
92
|
+
|
|
93
|
+
// 根据基础路径和扩展名生成六面贴图路径
|
|
94
|
+
const getSources = (basePath, ext) => {
|
|
95
|
+
return {
|
|
96
|
+
positiveX: `${basePath}/px.${ext}`,
|
|
97
|
+
negativeX: `${basePath}/nx.${ext}`,
|
|
98
|
+
positiveY: `${basePath}/py.${ext}`,
|
|
99
|
+
negativeY: `${basePath}/ny.${ext}`,
|
|
100
|
+
positiveZ: `${basePath}/pz.${ext}`,
|
|
101
|
+
negativeZ: `${basePath}/nz.${ext}`
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// 初始化 GroundSkyBox 类
|
|
106
|
+
// 所有已从 Cesium 命名空间移除的工具函数用原生 JS 替代,避免版本兼容问题
|
|
107
|
+
const initGroundSkyBoxClass = (Cesium) => {
|
|
108
|
+
if (!Cesium || Cesium.GroundSkyBox) return;
|
|
109
|
+
|
|
110
|
+
const BoxGeometry = Cesium.BoxGeometry;
|
|
111
|
+
const Cartesian3 = Cesium.Cartesian3;
|
|
112
|
+
const Matrix3 = Cesium.Matrix3;
|
|
113
|
+
const Matrix4 = Cesium.Matrix4;
|
|
114
|
+
const Transforms = Cesium.Transforms;
|
|
115
|
+
const VertexFormat = Cesium.VertexFormat;
|
|
116
|
+
const CubeMap = Cesium.CubeMap;
|
|
117
|
+
const DrawCommand = Cesium.DrawCommand;
|
|
118
|
+
const loadCubeMap = Cesium.loadCubeMap;
|
|
119
|
+
const RenderState = Cesium.RenderState;
|
|
120
|
+
const VertexArray = Cesium.VertexArray;
|
|
121
|
+
const BlendingState = Cesium.BlendingState;
|
|
122
|
+
const SceneMode = Cesium.SceneMode;
|
|
123
|
+
const ShaderProgram = Cesium.ShaderProgram;
|
|
124
|
+
const ShaderSource = Cesium.ShaderSource;
|
|
125
|
+
const GeometryPipeline = Cesium.GeometryPipeline;
|
|
126
|
+
const BufferUsage = Cesium.BufferUsage;
|
|
127
|
+
|
|
128
|
+
// 原生 JS 替代已从 Cesium 移除的工具函数
|
|
129
|
+
const defaultValue = (a, b) => (a !== undefined && a !== null) ? a : b;
|
|
130
|
+
const defined = (x) => x !== undefined && x !== null;
|
|
131
|
+
const destroyObject = (obj) => { obj.isDestroyed = () => true; return obj; };
|
|
132
|
+
|
|
133
|
+
const SkyBoxFS =
|
|
134
|
+
'#version 300 es\n' +
|
|
135
|
+
'uniform samplerCube u_cubeMap;\n' +
|
|
136
|
+
'in vec3 v_texCoord;\n' +
|
|
137
|
+
'out vec4 fragColor;\n' +
|
|
138
|
+
'void main()\n{\n' +
|
|
139
|
+
' vec4 color = texture(u_cubeMap, normalize(v_texCoord));\n' +
|
|
140
|
+
' fragColor = vec4(czm_gammaCorrect(color).rgb, czm_morphTime);\n' +
|
|
141
|
+
'}\n';
|
|
142
|
+
|
|
143
|
+
const SkyBoxVS =
|
|
144
|
+
'#version 300 es\n' +
|
|
145
|
+
'in vec3 position;\n' +
|
|
146
|
+
'out vec3 v_texCoord;\n' +
|
|
147
|
+
'uniform mat3 u_rotateMatrix;\n' +
|
|
148
|
+
'void main()\n{\n' +
|
|
149
|
+
' vec3 p = czm_viewRotation * u_rotateMatrix * (czm_temeToPseudoFixed * (czm_entireFrustum.y * position));\n' +
|
|
150
|
+
' gl_Position = czm_projection * vec4(p, 1.0);\n' +
|
|
151
|
+
' v_texCoord = position.xyz;\n' +
|
|
152
|
+
'}\n';
|
|
153
|
+
|
|
154
|
+
// 兼容 Matrix4.getRotation 在新版 Cesium 中被移除的情况
|
|
155
|
+
if (typeof Matrix4.getRotation !== 'function') {
|
|
156
|
+
Matrix4.getRotation = Matrix4.getMatrix3;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function SkyBoxOnGround(options) {
|
|
160
|
+
this.sources = options.sources;
|
|
161
|
+
this._sources = undefined;
|
|
162
|
+
this.show = defaultValue(options.show, true);
|
|
163
|
+
this._command = new DrawCommand({
|
|
164
|
+
modelMatrix: Matrix4.clone(Matrix4.IDENTITY),
|
|
165
|
+
owner: this,
|
|
166
|
+
});
|
|
167
|
+
this._cubeMap = undefined;
|
|
168
|
+
this._attributeLocations = undefined;
|
|
169
|
+
this._useHdr = undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const skyboxMatrix3 = new Matrix3();
|
|
173
|
+
SkyBoxOnGround.prototype.update = function (frameState, useHdr) {
|
|
174
|
+
const that = this;
|
|
175
|
+
if (!this.show) return undefined;
|
|
176
|
+
if (frameState.mode !== SceneMode.SCENE3D && frameState.mode !== SceneMode.MORPHING) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
if (!frameState.passes.render) return undefined;
|
|
180
|
+
|
|
181
|
+
const context = frameState.context;
|
|
182
|
+
if (this._sources !== this.sources) {
|
|
183
|
+
this._sources = this.sources;
|
|
184
|
+
const sources = this.sources;
|
|
185
|
+
if (
|
|
186
|
+
!defined(sources.positiveX) ||
|
|
187
|
+
!defined(sources.negativeX) ||
|
|
188
|
+
!defined(sources.positiveY) ||
|
|
189
|
+
!defined(sources.negativeY) ||
|
|
190
|
+
!defined(sources.positiveZ) ||
|
|
191
|
+
!defined(sources.negativeZ)
|
|
192
|
+
) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
'this.sources is required and must have positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ properties.',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (typeof sources.positiveX === 'string') {
|
|
199
|
+
loadCubeMap(context, this._sources).then(function (cubeMap) {
|
|
200
|
+
that._cubeMap = that._cubeMap && that._cubeMap.destroy();
|
|
201
|
+
that._cubeMap = cubeMap;
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
this._cubeMap = this._cubeMap && this._cubeMap.destroy();
|
|
205
|
+
this._cubeMap = new CubeMap({
|
|
206
|
+
context: context,
|
|
207
|
+
source: sources,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const command = this._command;
|
|
213
|
+
// 关键:每帧更新 modelMatrix 为相机位置的 ENU 变换,使天空盒跟随地表方向不歪斜
|
|
214
|
+
command.modelMatrix = Transforms.eastNorthUpToFixedFrame(frameState.camera._positionWC);
|
|
215
|
+
if (!defined(command.vertexArray)) {
|
|
216
|
+
command.uniformMap = {
|
|
217
|
+
u_cubeMap: function () {
|
|
218
|
+
return that._cubeMap;
|
|
219
|
+
},
|
|
220
|
+
u_rotateMatrix: function () {
|
|
221
|
+
return Matrix4.getRotation(command.modelMatrix, skyboxMatrix3);
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const geometry = BoxGeometry.createGeometry(
|
|
226
|
+
BoxGeometry.fromDimensions({
|
|
227
|
+
dimensions: new Cartesian3(2.0, 2.0, 2.0),
|
|
228
|
+
vertexFormat: VertexFormat.POSITION_ONLY,
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
const attributeLocations = (this._attributeLocations =
|
|
232
|
+
GeometryPipeline.createAttributeLocations(geometry));
|
|
233
|
+
|
|
234
|
+
command.vertexArray = VertexArray.fromGeometry({
|
|
235
|
+
context: context,
|
|
236
|
+
geometry: geometry,
|
|
237
|
+
attributeLocations: attributeLocations,
|
|
238
|
+
bufferUsage: BufferUsage._DRAW,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
command.renderState = RenderState.fromCache({
|
|
242
|
+
blending: BlendingState.ALPHA_BLEND,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!defined(command.shaderProgram) || this._useHdr !== useHdr) {
|
|
247
|
+
const fs = new ShaderSource({
|
|
248
|
+
defines: [useHdr ? 'HDR' : ''],
|
|
249
|
+
sources: [SkyBoxFS],
|
|
250
|
+
});
|
|
251
|
+
command.shaderProgram = ShaderProgram.fromCache({
|
|
252
|
+
context: context,
|
|
253
|
+
vertexShaderSource: SkyBoxVS,
|
|
254
|
+
fragmentShaderSource: fs,
|
|
255
|
+
attributeLocations: this._attributeLocations,
|
|
256
|
+
});
|
|
257
|
+
this._useHdr = useHdr;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!defined(this._cubeMap)) return undefined;
|
|
261
|
+
return command;
|
|
262
|
+
};
|
|
263
|
+
SkyBoxOnGround.prototype.isDestroyed = function () {
|
|
264
|
+
return false;
|
|
265
|
+
};
|
|
266
|
+
SkyBoxOnGround.prototype.destroy = function () {
|
|
267
|
+
const command = this._command;
|
|
268
|
+
command.vertexArray = command.vertexArray && command.vertexArray.destroy();
|
|
269
|
+
command.shaderProgram = command.shaderProgram && command.shaderProgram.destroy();
|
|
270
|
+
this._cubeMap = this._cubeMap && this._cubeMap.destroy();
|
|
271
|
+
return destroyObject(this);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
Cesium.GroundSkyBox = SkyBoxOnGround;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// 初始化天空盒
|
|
278
|
+
const initSkyBox = () => {
|
|
279
|
+
const Cesium = window.Cesium;
|
|
280
|
+
viewer = window.viewer || (window.map ? window.map.viewer : null);
|
|
281
|
+
if (!viewer || !Cesium) return;
|
|
282
|
+
|
|
283
|
+
// 初始化 GroundSkyBox 类
|
|
284
|
+
initGroundSkyBoxClass(Cesium);
|
|
285
|
+
if (!Cesium.GroundSkyBox) return;
|
|
286
|
+
|
|
287
|
+
// 保存原始天空盒状态
|
|
288
|
+
if (!originalCaptured) {
|
|
289
|
+
originalSkyBox = viewer.scene.skyBox;
|
|
290
|
+
originalSkyAtmosphereShow = viewer.scene.skyAtmosphere
|
|
291
|
+
? viewer.scene.skyAtmosphere.show
|
|
292
|
+
: true;
|
|
293
|
+
originalCaptured = true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 创建所有近景天空盒实例(使用 GroundSkyBox)
|
|
297
|
+
nearSkyBoxInstances = nearSkyBoxes.value.map(item => {
|
|
298
|
+
return new Cesium.GroundSkyBox({
|
|
299
|
+
sources: getSources(item.path, item.ext)
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// 监听渲染帧,根据相机高度自动切换远近天空盒
|
|
304
|
+
renderHandler = viewer.scene.postRender.addEventListener(() => {
|
|
305
|
+
const p = viewer.camera.position;
|
|
306
|
+
const height = Cesium.Cartographic.fromCartesian(p).height;
|
|
307
|
+
currentHeight.value = height;
|
|
308
|
+
|
|
309
|
+
if (height < 2500) {
|
|
310
|
+
if (activeSkyBoxType.value !== 'near') {
|
|
311
|
+
activeSkyBoxType.value = 'near';
|
|
312
|
+
updateSkyBox();
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
if (activeSkyBoxType.value !== 'far') {
|
|
316
|
+
activeSkyBoxType.value = 'far';
|
|
317
|
+
updateSkyBox();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
isReady.value = true;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// 切换当前显示的天空盒
|
|
326
|
+
const updateSkyBox = () => {
|
|
327
|
+
if (!viewer) return;
|
|
328
|
+
if (activeSkyBoxType.value === 'near') {
|
|
329
|
+
const skybox = nearSkyBoxInstances[currentNearIndex.value];
|
|
330
|
+
if (skybox) {
|
|
331
|
+
viewer.scene.skyBox = skybox;
|
|
332
|
+
if (viewer.scene.skyAtmosphere) viewer.scene.skyAtmosphere.show = false;
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
// 恢复远景天空盒
|
|
336
|
+
if (originalSkyBox) {
|
|
337
|
+
viewer.scene.skyBox = originalSkyBox;
|
|
338
|
+
}
|
|
339
|
+
if (viewer.scene.skyAtmosphere) viewer.scene.skyAtmosphere.show = originalSkyAtmosphereShow;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// 切换近景天空盒
|
|
344
|
+
const changeNearSkyBox = (index) => {
|
|
345
|
+
currentNearIndex.value = index;
|
|
346
|
+
if (activeSkyBoxType.value === 'near') {
|
|
347
|
+
updateSkyBox();
|
|
348
|
+
} else {
|
|
349
|
+
// 尝试飞向保存的视角,如果没有则飞向默认地面
|
|
350
|
+
if (!flyToSavedView()) {
|
|
351
|
+
flyToGround();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// 飞向地面(触发近景天空盒)
|
|
357
|
+
const flyToGround = () => {
|
|
358
|
+
const Cesium = window.Cesium;
|
|
359
|
+
if (!Cesium) return;
|
|
360
|
+
|
|
361
|
+
const rawDest = new Cesium.Cartesian3(
|
|
362
|
+
-1573556.399109955,
|
|
363
|
+
5327839.961767385,
|
|
364
|
+
3123210.094581469
|
|
365
|
+
);
|
|
366
|
+
const carto = Cesium.Cartographic.fromCartesian(rawDest);
|
|
367
|
+
carto.height = 1200;
|
|
368
|
+
|
|
369
|
+
const destination = Cesium.Cartesian3.fromRadians(carto.longitude, carto.latitude, carto.height);
|
|
370
|
+
|
|
371
|
+
const flyToOpts = {
|
|
372
|
+
destination: destination,
|
|
373
|
+
orientation: {
|
|
374
|
+
heading: 2.8892116353659993,
|
|
375
|
+
pitch: -0.20,
|
|
376
|
+
roll: 0.000723327810702834
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
if (viewer) {
|
|
380
|
+
viewer.scene.camera.flyTo(flyToOpts);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// 飞向远景视角
|
|
385
|
+
const flyToHome = () => {
|
|
386
|
+
const flyToOpts = {
|
|
387
|
+
destination: {
|
|
388
|
+
x: -45802988.9566341,
|
|
389
|
+
y: -41512897.1313383,
|
|
390
|
+
z: 42860387.48041018
|
|
391
|
+
},
|
|
392
|
+
orientation: {
|
|
393
|
+
heading: 1.2536256744910776,
|
|
394
|
+
pitch: -1.49857771791951,
|
|
395
|
+
roll: 0.03727351623405806
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
if (viewer) {
|
|
399
|
+
viewer.scene.camera.flyTo(flyToOpts);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// 保存当前视角
|
|
404
|
+
const saveCurrentView = () => {
|
|
405
|
+
if (!viewer) return;
|
|
406
|
+
const camera = viewer.camera;
|
|
407
|
+
const position = camera.position;
|
|
408
|
+
const heading = camera.heading;
|
|
409
|
+
const pitch = camera.pitch;
|
|
410
|
+
const roll = camera.roll;
|
|
411
|
+
|
|
412
|
+
const savedView = {
|
|
413
|
+
destination: {
|
|
414
|
+
x: position.x,
|
|
415
|
+
y: position.y,
|
|
416
|
+
z: position.z
|
|
417
|
+
},
|
|
418
|
+
orientation: {
|
|
419
|
+
heading,
|
|
420
|
+
pitch,
|
|
421
|
+
roll
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
if (typeof updateProps === 'function') {
|
|
426
|
+
updateProps({ savedView });
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// 飞向已保存的视角
|
|
431
|
+
const flyToSavedView = () => {
|
|
432
|
+
if (!props || !props.savedView) return false;
|
|
433
|
+
const view = props.savedView;
|
|
434
|
+
const Cesium = window.Cesium;
|
|
435
|
+
|
|
436
|
+
let destination = view.destination;
|
|
437
|
+
if (destination && typeof destination.x === 'number') {
|
|
438
|
+
if (Cesium) {
|
|
439
|
+
destination = new Cesium.Cartesian3(destination.x, destination.y, destination.z);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (!destination) return false;
|
|
444
|
+
|
|
445
|
+
if (viewer) {
|
|
446
|
+
viewer.camera.flyTo({
|
|
447
|
+
destination: destination,
|
|
448
|
+
orientation: view.orientation,
|
|
449
|
+
duration: 1.5
|
|
450
|
+
});
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
return false;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
onMounted(() => {
|
|
457
|
+
const timer = setInterval(() => {
|
|
458
|
+
if (window.viewer || (window.map && window.map.viewer)) {
|
|
459
|
+
clearInterval(timer);
|
|
460
|
+
initSkyBox();
|
|
461
|
+
flyToSavedView();
|
|
462
|
+
}
|
|
463
|
+
}, 100);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
onUnmounted(() => {
|
|
467
|
+
// 移除渲染监听
|
|
468
|
+
if (renderHandler) {
|
|
469
|
+
renderHandler();
|
|
470
|
+
renderHandler = null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 恢复远景天空盒
|
|
474
|
+
if (viewer && originalCaptured) {
|
|
475
|
+
if (originalSkyBox) {
|
|
476
|
+
viewer.scene.skyBox = originalSkyBox;
|
|
477
|
+
}
|
|
478
|
+
if (viewer.scene.skyAtmosphere) {
|
|
479
|
+
viewer.scene.skyAtmosphere.show = originalSkyAtmosphereShow;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// 销毁近景天空盒实例
|
|
484
|
+
if (nearSkyBoxInstances && nearSkyBoxInstances.length > 0) {
|
|
485
|
+
nearSkyBoxInstances.forEach(instance => {
|
|
486
|
+
if (instance && !instance.isDestroyed()) {
|
|
487
|
+
instance.destroy();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
nearSkyBoxInstances = [];
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
</script>
|
|
494
|
+
|
|
495
|
+
<style scoped>
|
|
496
|
+
.gis-panel {
|
|
497
|
+
width: 100%;
|
|
498
|
+
height: 100%;
|
|
499
|
+
background: rgba(4, 28, 44, 0.9);
|
|
500
|
+
backdrop-filter: blur(8px);
|
|
501
|
+
color: #fff;
|
|
502
|
+
border-radius: 4px;
|
|
503
|
+
display: flex;
|
|
504
|
+
flex-direction: column;
|
|
505
|
+
overflow: hidden;
|
|
506
|
+
font-family: "Microsoft YaHei", sans-serif;
|
|
507
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
|
508
|
+
border: 1px solid rgba(128, 128, 128, 0.2);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.panel-header {
|
|
512
|
+
height: 40px;
|
|
513
|
+
display: flex;
|
|
514
|
+
align-items: center;
|
|
515
|
+
padding: 0 12px;
|
|
516
|
+
background: linear-gradient(90deg, rgba(20, 50, 80, 0.8), rgba(4, 28, 44, 0.2));
|
|
517
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
518
|
+
justify-content: space-between;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.header-left {
|
|
522
|
+
display: flex;
|
|
523
|
+
align-items: center;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.header-icon {
|
|
527
|
+
margin-right: 8px;
|
|
528
|
+
font-size: 16px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.header-title-text {
|
|
532
|
+
font-size: 16px;
|
|
533
|
+
font-weight: bold;
|
|
534
|
+
text-shadow: 0 0 10px rgba(0, 255, 255, 0.3);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.header-center {
|
|
538
|
+
flex: 1;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.header-right {
|
|
542
|
+
display: flex;
|
|
543
|
+
align-items: center;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.header-status {
|
|
547
|
+
font-size: 12px;
|
|
548
|
+
color: #aaa;
|
|
549
|
+
display: flex;
|
|
550
|
+
align-items: center;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.status-dot {
|
|
554
|
+
width: 8px;
|
|
555
|
+
height: 8px;
|
|
556
|
+
background: #f00;
|
|
557
|
+
border-radius: 50%;
|
|
558
|
+
margin-right: 6px;
|
|
559
|
+
transition: all 0.3s;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.status-dot.ready {
|
|
563
|
+
background: #0f0;
|
|
564
|
+
box-shadow: 0 0 8px #0f0;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.panel-body {
|
|
568
|
+
flex: 1;
|
|
569
|
+
padding: 12px;
|
|
570
|
+
overflow-y: auto;
|
|
571
|
+
display: flex;
|
|
572
|
+
flex-direction: column;
|
|
573
|
+
gap: 12px;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.control-section {
|
|
577
|
+
background: rgba(255, 255, 255, 0.05);
|
|
578
|
+
border-radius: 4px;
|
|
579
|
+
padding: 10px;
|
|
580
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.section-label {
|
|
584
|
+
font-size: 12px;
|
|
585
|
+
color: #8db6cd;
|
|
586
|
+
margin-bottom: 8px;
|
|
587
|
+
font-weight: bold;
|
|
588
|
+
border-left: 2px solid #00e5ff;
|
|
589
|
+
padding-left: 6px;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.btn-grid {
|
|
593
|
+
display: grid;
|
|
594
|
+
gap: 8px;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.btn-grid.near-grid {
|
|
598
|
+
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
|
599
|
+
max-height: 140px;
|
|
600
|
+
overflow-y: auto;
|
|
601
|
+
align-content: start;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.custom-btn {
|
|
605
|
+
background: rgba(255, 255, 255, 0.1);
|
|
606
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
607
|
+
color: #fff;
|
|
608
|
+
padding: 6px 0;
|
|
609
|
+
border-radius: 4px;
|
|
610
|
+
cursor: pointer;
|
|
611
|
+
transition: all 0.2s;
|
|
612
|
+
font-size: 12px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.custom-btn:hover {
|
|
616
|
+
background: rgba(255, 255, 255, 0.2);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.custom-btn.active {
|
|
620
|
+
background: rgba(0, 229, 255, 0.2);
|
|
621
|
+
border-color: rgba(0, 229, 255, 0.6);
|
|
622
|
+
color: #00e5ff;
|
|
623
|
+
box-shadow: 0 0 8px rgba(0, 229, 255, 0.2) inset;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.custom-btn.primary {
|
|
627
|
+
grid-column: span 3;
|
|
628
|
+
background: rgba(0, 119, 255, 0.4);
|
|
629
|
+
border-color: rgba(0, 119, 255, 0.6);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.info-text {
|
|
633
|
+
font-size: 12px;
|
|
634
|
+
color: #ccc;
|
|
635
|
+
margin-top: 4px;
|
|
636
|
+
}
|
|
637
|
+
</style>
|